(root)/
glib-2.79.0/
glib/
tests/
mutex.c
       1  /* Unit tests for GMutex
       2   * Copyright (C) 2011 Red Hat, Inc
       3   * Author: Matthias Clasen
       4   *
       5   * SPDX-License-Identifier: LicenseRef-old-glib-tests
       6   *
       7   * This work is provided "as is"; redistribution and modification
       8   * in whole or in part, in any medium, physical or electronic is
       9   * permitted without restriction.
      10   *
      11   * This work is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      14   *
      15   * In no event shall the authors or contributors be liable for any
      16   * direct, indirect, incidental, special, exemplary, or consequential
      17   * damages (including, but not limited to, procurement of substitute
      18   * goods or services; loss of use, data, or profits; or business
      19   * interruption) however caused and on any theory of liability, whether
      20   * in contract, strict liability, or tort (including negligence or
      21   * otherwise) arising in any way out of the use of this software, even
      22   * if advised of the possibility of such damage.
      23   */
      24  
      25  /* We are testing some deprecated APIs here */
      26  #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
      27  #define GLIB_DISABLE_DEPRECATION_WARNINGS
      28  #endif
      29  
      30  #include <glib.h>
      31  
      32  #include <stdio.h>
      33  
      34  static void
      35  test_mutex1 (void)
      36  {
      37    GMutex mutex;
      38  
      39    g_mutex_init (&mutex);
      40    g_mutex_lock (&mutex);
      41    g_mutex_unlock (&mutex);
      42    g_mutex_lock (&mutex);
      43    g_mutex_unlock (&mutex);
      44    g_mutex_clear (&mutex);
      45  }
      46  
      47  static void
      48  test_mutex2 (void)
      49  {
      50    static GMutex mutex;
      51  
      52    g_mutex_lock (&mutex);
      53    g_mutex_unlock (&mutex);
      54    g_mutex_lock (&mutex);
      55    g_mutex_unlock (&mutex);
      56  }
      57  
      58  static void
      59  test_mutex3 (void)
      60  {
      61    GMutex *mutex;
      62  
      63    mutex = g_mutex_new ();
      64    g_mutex_lock (mutex);
      65    g_mutex_unlock (mutex);
      66    g_mutex_lock (mutex);
      67    g_mutex_unlock (mutex);
      68    g_mutex_free (mutex);
      69  }
      70  
      71  static void
      72  test_mutex4 (void)
      73  {
      74    static GMutex mutex;
      75    gboolean ret;
      76  
      77    ret = g_mutex_trylock (&mutex);
      78    g_assert (ret);
      79  
      80    /* no guarantees that mutex is recursive, so could return 0 or 1 */
      81    if (g_mutex_trylock (&mutex))
      82      g_mutex_unlock (&mutex);
      83  
      84    g_mutex_unlock (&mutex);
      85  }
      86  
      87  #define LOCKS      48
      88  #define ITERATIONS 10000
      89  #define THREADS    100
      90  
      91  
      92  GThread *owners[LOCKS];
      93  GMutex   locks[LOCKS];
      94  
      95  static void
      96  acquire (gint nr)
      97  {
      98    GThread *self;
      99  
     100    self = g_thread_self ();
     101  
     102    if (!g_mutex_trylock (&locks[nr]))
     103      {
     104        if (g_test_verbose ())
     105          g_printerr ("thread %p going to block on lock %d\n", self, nr);
     106  
     107        g_mutex_lock (&locks[nr]);
     108      }
     109  
     110    g_assert (owners[nr] == NULL);   /* hopefully nobody else is here */
     111    owners[nr] = self;
     112  
     113    /* let some other threads try to ruin our day */
     114    g_thread_yield ();
     115    g_thread_yield ();
     116    g_thread_yield ();
     117  
     118    g_assert (owners[nr] == self);   /* hopefully this is still us... */
     119    owners[nr] = NULL;               /* make way for the next guy */
     120  
     121    g_mutex_unlock (&locks[nr]);
     122  }
     123  
     124  static gpointer
     125  thread_func (gpointer data)
     126  {
     127    gint i;
     128    GRand *rand;
     129  
     130    rand = g_rand_new ();
     131  
     132    for (i = 0; i < ITERATIONS; i++)
     133      acquire (g_rand_int_range (rand, 0, LOCKS));
     134  
     135    g_rand_free (rand);
     136  
     137    return NULL;
     138  }
     139  
     140  static void
     141  test_mutex5 (void)
     142  {
     143    gint i;
     144    GThread *threads[THREADS];
     145  
     146    for (i = 0; i < LOCKS; i++)
     147      g_mutex_init (&locks[i]);
     148  
     149    for (i = 0; i < THREADS; i++)
     150      threads[i] = g_thread_create (thread_func, NULL, TRUE, NULL);
     151  
     152    for (i = 0; i < THREADS; i++)
     153      g_thread_join (threads[i]);
     154  
     155    for (i = 0; i < LOCKS; i++)
     156      g_mutex_clear (&locks[i]);
     157  
     158    for (i = 0; i < LOCKS; i++)
     159      g_assert (owners[i] == NULL);
     160  }
     161  
     162  static gpointer
     163  test_mutex_errno_func (gpointer data)
     164  {
     165    GMutex *m = data;
     166  
     167    g_test_summary ("Validates that errno is not touched upon return");
     168    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3034");
     169  
     170    for (unsigned int i = 0; i < 1000; i++)
     171      {
     172        errno = 0;
     173        g_mutex_lock (m);
     174        g_assert_cmpint (errno, ==, 0);
     175  
     176        g_thread_yield ();
     177  
     178        errno = 0;
     179        g_mutex_unlock (m);
     180        g_assert_cmpint (errno, ==, 0);
     181  
     182        errno = 0;
     183        if (g_mutex_trylock (m))
     184          {
     185            g_assert_cmpint (errno, ==, 0);
     186  
     187            g_thread_yield ();
     188  
     189            errno = 0;
     190            g_mutex_unlock (m);
     191            g_assert_cmpint (errno, ==, 0);
     192          }
     193      }
     194  
     195    return NULL;
     196  }
     197  
     198  static void
     199  test_mutex_errno (void)
     200  {
     201    gsize i;
     202    GThread *threads[THREADS];
     203    GMutex m;
     204  
     205    g_mutex_init (&m);
     206  
     207    for (i = 0; i < G_N_ELEMENTS (threads); i++)
     208      {
     209        threads[i] = g_thread_new ("test_mutex_errno",
     210                                   test_mutex_errno_func, &m);
     211      }
     212  
     213    for (i = 0; i < G_N_ELEMENTS (threads); i++)
     214      {
     215        g_thread_join (threads[i]);
     216      }
     217  }
     218  
     219  static gint count_to = 0;
     220  
     221  static gboolean
     222  do_addition (gint *value)
     223  {
     224    static GMutex lock;
     225    gboolean more;
     226  
     227    /* test performance of "good" cases (ie: short critical sections) */
     228    g_mutex_lock (&lock);
     229    if ((more = *value != count_to))
     230      if (*value != -1)
     231        (*value)++;
     232    g_mutex_unlock (&lock);
     233  
     234    return more;
     235  }
     236  
     237  static gpointer
     238  addition_thread (gpointer value)
     239  {
     240    while (do_addition (value));
     241  
     242    return NULL;
     243  }
     244  
     245  static void
     246  test_mutex_perf (gconstpointer data)
     247  {
     248    const guint n_threads = GPOINTER_TO_UINT (data);
     249    GThread *threads[THREADS];
     250    gint64 start_time;
     251    gdouble rate;
     252    gint x = -1;
     253    guint i;
     254  
     255    count_to = g_test_perf () ?  100000000 : n_threads + 1;
     256  
     257    g_assert (n_threads <= G_N_ELEMENTS (threads));
     258  
     259    for (i = 0; n_threads > 0 && i < n_threads - 1; i++)
     260      threads[i] = g_thread_create (addition_thread, &x, TRUE, NULL);
     261  
     262    /* avoid measuring thread setup/teardown time */
     263    start_time = g_get_monotonic_time ();
     264    g_atomic_int_set (&x, 0);
     265    addition_thread (&x);
     266    g_assert_cmpint (g_atomic_int_get (&x), ==, count_to);
     267    rate = g_get_monotonic_time () - start_time;
     268    rate = x / rate;
     269  
     270    for (i = 0; n_threads > 0 && i < n_threads - 1; i++)
     271      g_thread_join (threads[i]);
     272  
     273    g_test_maximized_result (rate, "%f mips", rate);
     274  }
     275  
     276  int
     277  main (int argc, char *argv[])
     278  {
     279    g_test_init (&argc, &argv, NULL);
     280  
     281    g_test_add_func ("/thread/mutex1", test_mutex1);
     282    g_test_add_func ("/thread/mutex2", test_mutex2);
     283    g_test_add_func ("/thread/mutex3", test_mutex3);
     284    g_test_add_func ("/thread/mutex4", test_mutex4);
     285    g_test_add_func ("/thread/mutex5", test_mutex5);
     286    g_test_add_func ("/thread/mutex/errno", test_mutex_errno);
     287  
     288      {
     289        guint i;
     290  
     291        g_test_add_data_func ("/thread/mutex/perf/uncontended", GUINT_TO_POINTER (0), test_mutex_perf);
     292  
     293        for (i = 1; i <= 10; i++)
     294          {
     295            gchar name[80];
     296            sprintf (name, "/thread/mutex/perf/contended/%u", i);
     297            g_test_add_data_func (name, GUINT_TO_POINTER (i), test_mutex_perf);
     298          }
     299      }
     300  
     301    return g_test_run ();
     302  }