LCOV - code coverage report
Current view: top level - glib/gio/tests - contexts.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 193 195 99.0 %
Date: 2024-04-23 05:16:05 Functions: 22 23 95.7 %
Branches: 39 42 92.9 %

           Branch data     Line data    Source code
       1                 :            : #include "gcontextspecificgroup.c"
       2                 :            : #include <gio/gio.h>
       3                 :            : #include <stdlib.h>
       4                 :            : #include <string.h>
       5                 :            : 
       6                 :            : #define N_THREADS 10
       7                 :            : 
       8                 :            : static gchar *test_file;
       9                 :            : 
      10                 :            : char *test_file_buffer;
      11                 :            : gsize test_file_size;
      12                 :            : static char async_read_buffer[8192];
      13                 :            : 
      14                 :            : static void
      15                 :          2 : read_data (GObject *source, GAsyncResult *result, gpointer loop)
      16                 :            : {
      17                 :          2 :   GInputStream *in = G_INPUT_STREAM (source);
      18                 :          2 :   GError *error = NULL;
      19                 :            :   gssize nread;
      20                 :            : 
      21                 :          2 :   nread = g_input_stream_read_finish (in, result, &error);
      22                 :          2 :   g_assert_no_error (error);
      23                 :            : 
      24                 :          2 :   g_assert_cmpint (nread, >, 0);
      25                 :          2 :   g_assert_cmpint (nread, <=, MIN(sizeof (async_read_buffer), test_file_size));
      26                 :          2 :   g_assert (memcmp (async_read_buffer, test_file_buffer, nread) == 0);
      27                 :            : 
      28                 :          2 :   g_main_loop_quit (loop);
      29                 :          2 : }
      30                 :            : 
      31                 :            : static void
      32                 :          2 : opened_for_read (GObject *source, GAsyncResult *result, gpointer loop)
      33                 :            : {
      34                 :          2 :   GFile *file = G_FILE (source);
      35                 :            :   GFileInputStream *in;
      36                 :          2 :   GError *error = NULL;
      37                 :            : 
      38                 :          2 :   in = g_file_read_finish (file, result, &error);
      39                 :          2 :   g_assert_no_error (error);
      40                 :            : 
      41                 :          2 :   memset (async_read_buffer, 0, sizeof (async_read_buffer));
      42                 :          2 :   g_input_stream_read_async (G_INPUT_STREAM (in),
      43                 :            :                              async_read_buffer, sizeof (async_read_buffer),
      44                 :            :                              G_PRIORITY_DEFAULT, NULL,
      45                 :            :                              read_data, loop);
      46                 :            : 
      47                 :          2 :   g_object_unref (in);
      48                 :          2 : }
      49                 :            : 
      50                 :            : /* Test 1: Async I/O started in a thread with a thread-default context
      51                 :            :  * will stick to that thread, and will complete even if the default
      52                 :            :  * main loop is blocked. (NB: the last part would not be true if we
      53                 :            :  * were testing GFileMonitor!)
      54                 :            :  */
      55                 :            : 
      56                 :            : static gboolean idle_start_test1_thread (gpointer loop);
      57                 :            : static gpointer test1_thread (gpointer user_data);
      58                 :            : 
      59                 :            : static gboolean test1_done;
      60                 :            : static GCond test1_cond;
      61                 :            : static GMutex test1_mutex;
      62                 :            : 
      63                 :            : static void
      64                 :          1 : test_thread_independence (void)
      65                 :            : {
      66                 :            :   GMainLoop *loop;
      67                 :            : 
      68                 :          1 :   loop = g_main_loop_new (NULL, FALSE);
      69                 :          1 :   g_idle_add (idle_start_test1_thread, loop);
      70                 :          1 :   g_main_loop_run (loop);
      71                 :          1 :   g_main_loop_unref (loop);
      72                 :          1 : }
      73                 :            : 
      74                 :            : static gboolean
      75                 :          1 : idle_start_test1_thread (gpointer loop)
      76                 :            : {
      77                 :            :   gint64 time;
      78                 :            :   GThread *thread;
      79                 :            :   gboolean io_completed;
      80                 :            : 
      81                 :          1 :   g_mutex_lock (&test1_mutex);
      82                 :          1 :   thread = g_thread_new ("test1", test1_thread, NULL);
      83                 :            : 
      84                 :          1 :   time = g_get_monotonic_time () + 20 * G_TIME_SPAN_SECOND;
      85         [ +  + ]:          2 :   while (!test1_done)
      86                 :            :     {
      87                 :          1 :       io_completed = g_cond_wait_until (&test1_cond, &test1_mutex, time);
      88                 :          1 :       g_assert (io_completed);
      89                 :            :     }
      90                 :          1 :   g_thread_join (thread);
      91                 :            : 
      92                 :          1 :   g_mutex_unlock (&test1_mutex);
      93                 :          1 :   g_main_loop_quit (loop);
      94                 :          1 :   return G_SOURCE_REMOVE;
      95                 :            : }
      96                 :            : 
      97                 :            : static gpointer
      98                 :          1 : test1_thread (gpointer user_data)
      99                 :            : {
     100                 :            :   GMainContext *context;
     101                 :            :   GMainLoop *loop;
     102                 :            :   GFile *file;
     103                 :            : 
     104                 :            :   /* Wait for main thread to be waiting on test1_cond */
     105                 :          1 :   g_mutex_lock (&test1_mutex);
     106                 :            : 
     107                 :          1 :   context = g_main_context_new ();
     108                 :          1 :   g_assert (g_main_context_get_thread_default () == NULL);
     109                 :          1 :   g_main_context_push_thread_default (context);
     110                 :          1 :   g_assert (g_main_context_get_thread_default () == context);
     111                 :            : 
     112                 :          1 :   file = g_file_new_for_path (test_file);
     113                 :          1 :   g_assert (g_file_supports_thread_contexts (file));
     114                 :            : 
     115                 :          1 :   loop = g_main_loop_new (context, FALSE);
     116                 :          1 :   g_file_read_async (file, G_PRIORITY_DEFAULT, NULL,
     117                 :            :                      opened_for_read, loop);
     118                 :          1 :   g_object_unref (file);
     119                 :          1 :   g_main_loop_run (loop);
     120                 :          1 :   g_main_loop_unref (loop);
     121                 :            : 
     122                 :          1 :   test1_done = TRUE;
     123                 :          1 :   g_cond_signal (&test1_cond);
     124                 :          1 :   g_mutex_unlock (&test1_mutex);
     125                 :            : 
     126                 :          1 :   g_main_context_pop_thread_default (context);
     127                 :          1 :   g_main_context_unref (context);
     128                 :            : 
     129                 :          1 :   return NULL;
     130                 :            : }
     131                 :            : 
     132                 :            : /* Test 2: If we push a thread-default context in the main thread, we
     133                 :            :  * can run async ops in that context without running the default
     134                 :            :  * context.
     135                 :            :  */
     136                 :            : 
     137                 :            : static gboolean test2_fail (gpointer user_data);
     138                 :            : 
     139                 :            : static void
     140                 :          1 : test_context_independence (void)
     141                 :            : {
     142                 :            :   GMainContext *context;
     143                 :            :   GMainLoop *loop;
     144                 :            :   GFile *file;
     145                 :            :   guint default_timeout;
     146                 :            :   GSource *thread_default_timeout;
     147                 :            : 
     148                 :          1 :   context = g_main_context_new ();
     149                 :          1 :   g_assert (g_main_context_get_thread_default () == NULL);
     150                 :          1 :   g_main_context_push_thread_default (context);
     151                 :          1 :   g_assert (g_main_context_get_thread_default () == context);
     152                 :            : 
     153                 :          1 :   file = g_file_new_for_path (test_file);
     154                 :          1 :   g_assert (g_file_supports_thread_contexts (file));
     155                 :            : 
     156                 :            :   /* Add a timeout to the main loop, to fail immediately if it gets run */
     157                 :          1 :   default_timeout = g_timeout_add_full (G_PRIORITY_HIGH, 0,
     158                 :            :                                         test2_fail, NULL, NULL);
     159                 :            :   /* Add a timeout to the alternate loop, to fail if the I/O *doesn't* run */
     160                 :          1 :   thread_default_timeout = g_timeout_source_new_seconds (2);
     161                 :          1 :   g_source_set_callback (thread_default_timeout, test2_fail, NULL, NULL);
     162                 :          1 :   g_source_attach (thread_default_timeout, context);
     163                 :            : 
     164                 :          1 :   loop = g_main_loop_new (context, FALSE);
     165                 :          1 :   g_file_read_async (file, G_PRIORITY_DEFAULT, NULL,
     166                 :            :                      opened_for_read, loop);
     167                 :          1 :   g_object_unref (file);
     168                 :          1 :   g_main_loop_run (loop);
     169                 :          1 :   g_main_loop_unref (loop);
     170                 :            : 
     171                 :          1 :   g_source_remove (default_timeout);
     172                 :          1 :   g_source_destroy (thread_default_timeout);
     173                 :          1 :   g_source_unref (thread_default_timeout);
     174                 :            : 
     175                 :          1 :   g_main_context_pop_thread_default (context);
     176                 :          1 :   g_main_context_unref (context);
     177                 :          1 : }
     178                 :            : 
     179                 :            : static gboolean
     180                 :          0 : test2_fail (gpointer user_data)
     181                 :            : {
     182                 :            :   g_assert_not_reached ();
     183                 :            :   return FALSE;
     184                 :            : }
     185                 :            : 
     186                 :            : 
     187                 :            : typedef struct
     188                 :            : {
     189                 :            :   GObject parent_instance;
     190                 :            : 
     191                 :            :   GMainContext *context;
     192                 :            : } PerThreadThing;
     193                 :            : 
     194                 :            : typedef GObjectClass PerThreadThingClass;
     195                 :            : 
     196                 :            : static GType per_thread_thing_get_type (void);
     197                 :            : 
     198   [ +  +  +  -  :       3268 : G_DEFINE_TYPE (PerThreadThing, per_thread_thing, G_TYPE_OBJECT)
                   +  + ]
     199                 :            : 
     200                 :            : static GContextSpecificGroup group;
     201                 :            : static gpointer instances[N_THREADS];
     202                 :            : static gint is_running;
     203                 :            : static gint current_value;
     204                 :            : static gint observed_values[N_THREADS];
     205                 :            : 
     206                 :            : static void
     207                 :          3 : start_func (void)
     208                 :            : {
     209                 :          3 :   g_assert (!is_running);
     210                 :          3 :   g_atomic_int_set (&is_running, TRUE);
     211                 :          3 : }
     212                 :            : 
     213                 :            : static void
     214                 :          3 : stop_func (void)
     215                 :            : {
     216                 :          3 :   g_assert (is_running);
     217                 :          3 :   g_atomic_int_set (&is_running, FALSE);
     218                 :          3 : }
     219                 :            : 
     220                 :            : static void
     221                 :         21 : per_thread_thing_finalize (GObject *object)
     222                 :            : {
     223                 :         21 :   PerThreadThing *thing = (PerThreadThing *) object;
     224                 :            : 
     225                 :         21 :   g_context_specific_group_remove (&group, thing->context, thing, stop_func);
     226                 :            : 
     227                 :         21 :   G_OBJECT_CLASS (per_thread_thing_parent_class)->finalize (object);
     228                 :         21 : }
     229                 :            : 
     230                 :            : static void
     231                 :         21 : per_thread_thing_init (PerThreadThing *thing)
     232                 :            : {
     233                 :         21 : }
     234                 :            : 
     235                 :            : static void
     236                 :          1 : per_thread_thing_class_init (PerThreadThingClass *class)
     237                 :            : {
     238                 :          1 :   class->finalize = per_thread_thing_finalize;
     239                 :            : 
     240                 :          1 :   g_signal_new ("changed", per_thread_thing_get_type (), G_SIGNAL_RUN_FIRST, 0,
     241                 :            :                 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
     242                 :          1 : }
     243                 :            : 
     244                 :            : static gpointer
     245                 :       1013 : per_thread_thing_get (void)
     246                 :            : {
     247                 :       1013 :   return g_context_specific_group_get (&group, per_thread_thing_get_type (),
     248                 :            :                                        G_STRUCT_OFFSET (PerThreadThing, context),
     249                 :            :                                        start_func);
     250                 :            : }
     251                 :            : 
     252                 :            : static gpointer
     253                 :         10 : test_identity_thread (gpointer user_data)
     254                 :            : {
     255                 :         10 :   guint thread_nr = GPOINTER_TO_UINT (user_data);
     256                 :            :   GMainContext *my_context;
     257                 :            :   guint i, j;
     258                 :            : 
     259                 :         10 :   my_context = g_main_context_new ();
     260                 :         10 :   g_main_context_push_thread_default (my_context);
     261                 :            : 
     262                 :         10 :   g_assert (!instances[thread_nr]);
     263                 :         10 :   instances[thread_nr] = per_thread_thing_get ();
     264                 :         10 :   g_assert (g_atomic_int_get (&is_running));
     265                 :            : 
     266         [ +  + ]:       1008 :   for (i = 0; i < 100; i++)
     267                 :            :     {
     268                 :        993 :       gpointer instance = per_thread_thing_get ();
     269                 :            : 
     270         [ +  + ]:      10955 :       for (j = 0; j < N_THREADS; j++)
     271                 :       9958 :         g_assert ((instance == instances[j]) == (thread_nr == j));
     272                 :            : 
     273                 :        997 :       g_assert (g_atomic_int_get (&is_running));
     274                 :            : 
     275                 :        997 :       g_thread_yield ();
     276                 :            : 
     277                 :        983 :       g_assert (g_atomic_int_get (&is_running));
     278                 :            :     }
     279                 :            : 
     280         [ +  + ]:       1012 :   for (i = 0; i < 100; i++)
     281                 :            :     {
     282                 :        996 :       g_object_unref (instances[thread_nr]);
     283                 :            : 
     284         [ +  + ]:      10800 :       for (j = 0; j < N_THREADS; j++)
     285                 :       9817 :         g_assert ((instances[thread_nr] == instances[j]) == (thread_nr == j));
     286                 :            : 
     287                 :        983 :       g_assert (g_atomic_int_get (&is_running));
     288                 :            : 
     289                 :        983 :       g_thread_yield ();
     290                 :            :     }
     291                 :            : 
     292                 :            :   /* drop the last ref */
     293                 :         16 :   g_object_unref (instances[thread_nr]);
     294                 :         10 :   instances[thread_nr] = NULL;
     295                 :            : 
     296                 :         10 :   g_main_context_pop_thread_default (my_context);
     297                 :         10 :   g_main_context_unref (my_context);
     298                 :            : 
     299                 :            :   /* at least one thread should see this cleared on exit */
     300         [ +  + ]:         10 :   return GUINT_TO_POINTER (!group.requested_state);
     301                 :            : }
     302                 :            : 
     303                 :            : static void
     304                 :          1 : test_context_specific_identity (void)
     305                 :            : {
     306                 :            :   GThread *threads[N_THREADS];
     307                 :          1 :   gboolean exited = FALSE;
     308                 :            :   guint i;
     309                 :            : 
     310                 :          1 :   g_assert (!g_atomic_int_get (&is_running));
     311         [ +  + ]:         11 :   for (i = 0; i < N_THREADS; i++)
     312                 :         10 :     threads[i] = g_thread_new ("test", test_identity_thread, GUINT_TO_POINTER (i));
     313         [ +  + ]:         11 :   for (i = 0; i < N_THREADS; i++)
     314                 :         10 :     exited |= GPOINTER_TO_UINT (g_thread_join (threads[i]));
     315                 :          1 :   g_assert (exited);
     316                 :          1 :   g_assert (!group.requested_state);
     317                 :          1 : }
     318                 :            : 
     319                 :            : static void
     320                 :      17019 : changed_emitted (PerThreadThing *thing,
     321                 :            :                  gpointer        user_data)
     322                 :            : {
     323                 :      17019 :   gint *observed_value = user_data;
     324                 :            : 
     325                 :      17019 :   g_atomic_int_set (observed_value, g_atomic_int_get (&current_value));
     326                 :      17019 : }
     327                 :            : 
     328                 :            : static gpointer
     329                 :         10 : test_emit_thread (gpointer user_data)
     330                 :            : {
     331                 :         10 :   gint *observed_value = user_data;
     332                 :            :   GMainContext *my_context;
     333                 :            :   gpointer instance;
     334                 :            : 
     335                 :         10 :   my_context = g_main_context_new ();
     336                 :         10 :   g_main_context_push_thread_default (my_context);
     337                 :            : 
     338                 :         10 :   instance = per_thread_thing_get ();
     339                 :         10 :   g_assert (g_atomic_int_get (&is_running));
     340                 :            : 
     341                 :         10 :   g_signal_connect (instance, "changed", G_CALLBACK (changed_emitted), observed_value);
     342                 :            : 
     343                 :            :   /* observe after connection */
     344                 :         10 :   g_atomic_int_set (observed_value, g_atomic_int_get (&current_value));
     345                 :            : 
     346         [ +  + ]:      17063 :   while (g_atomic_int_get (&current_value) != -1)
     347                 :      17053 :     g_main_context_iteration (my_context, TRUE);
     348                 :            : 
     349                 :         10 :   g_object_unref (instance);
     350                 :            : 
     351                 :         10 :   g_main_context_pop_thread_default (my_context);
     352                 :         10 :   g_main_context_unref (my_context);
     353                 :            : 
     354                 :            :   /* at least one thread should see this cleared on exit */
     355         [ +  + ]:         10 :   return GUINT_TO_POINTER (!group.requested_state);
     356                 :            : }
     357                 :            : 
     358                 :            : static void
     359                 :          1 : test_context_specific_emit (void)
     360                 :            : {
     361                 :            :   GThread *threads[N_THREADS];
     362                 :          1 :   gboolean exited = FALSE;
     363                 :            :   gsize i;
     364                 :            :   gint k, n;
     365                 :            : 
     366         [ +  + ]:         11 :   for (i = 0; i < N_THREADS; i++)
     367                 :         10 :     threads[i] = g_thread_new ("test", test_emit_thread, &observed_values[i]);
     368                 :            : 
     369                 :            :   /* make changes and ensure that they are observed */
     370         [ +  + ]:       1001 :   for (n = 0; n < 1000; n++)
     371                 :            :     {
     372                 :            :       gint64 expiry;
     373                 :            : 
     374                 :            :       /* don't burn CPU forever */
     375                 :       1000 :       expiry = g_get_monotonic_time () + 10 * G_TIME_SPAN_SECOND;
     376                 :            : 
     377                 :       1000 :       g_atomic_int_set (&current_value, n);
     378                 :            : 
     379                 :            :       /* wake them to notice */
     380         [ +  + ]:       3252 :       for (k = 0; k < g_test_rand_int_range (1, 5); k++)
     381                 :       2252 :         g_context_specific_group_emit (&group, g_signal_lookup ("changed", per_thread_thing_get_type ()));
     382                 :            : 
     383         [ +  + ]:      11000 :       for (i = 0; i < N_THREADS; i++)
     384         [ +  + ]:     369061 :         while (g_atomic_int_get (&observed_values[i]) != n)
     385                 :            :           {
     386                 :     359061 :             g_thread_yield ();
     387                 :            : 
     388         [ -  + ]:     359061 :             if (g_get_monotonic_time () > expiry)
     389                 :          0 :               g_error ("timed out");
     390                 :            :           }
     391                 :            :     }
     392                 :            : 
     393                 :            :   /* tell them to quit */
     394                 :          1 :   g_atomic_int_set (&current_value, -1);
     395                 :          1 :   g_context_specific_group_emit (&group, g_signal_lookup ("notify", G_TYPE_OBJECT));
     396                 :            : 
     397         [ +  + ]:         11 :   for (i = 0; i < N_THREADS; i++)
     398                 :         10 :     exited |= GPOINTER_TO_UINT (g_thread_join (threads[i]));
     399                 :          1 :   g_assert (exited);
     400                 :          1 :   g_assert (!group.requested_state);
     401                 :          1 : }
     402                 :            : 
     403                 :            : static void
     404                 :          1 : test_context_specific_emit_and_unref (void)
     405                 :            : {
     406                 :            :   gpointer obj;
     407                 :            : 
     408                 :          1 :   obj = per_thread_thing_get ();
     409                 :          1 :   g_context_specific_group_emit (&group, g_signal_lookup ("changed", per_thread_thing_get_type ()));
     410                 :          1 :   g_object_unref (obj);
     411                 :            : 
     412         [ -  + ]:          1 :   while (g_main_context_iteration (NULL, 0))
     413                 :            :     ;
     414                 :          1 : }
     415                 :            : 
     416                 :            : int
     417                 :          1 : main (int argc, char **argv)
     418                 :            : {
     419                 :          1 :   GError *error = NULL;
     420                 :            :   int ret;
     421                 :            : 
     422                 :          1 :   g_test_init (&argc, &argv, NULL);
     423                 :            : 
     424                 :          1 :   test_file = g_test_build_filename (G_TEST_DIST, "contexts.c", NULL);
     425                 :          1 :   g_file_get_contents (test_file, &test_file_buffer,
     426                 :            :                        &test_file_size, &error);
     427                 :          1 :   g_assert_no_error (error);
     428                 :            : 
     429                 :          1 :   g_test_add_func ("/gio/contexts/thread-independence", test_thread_independence);
     430                 :          1 :   g_test_add_func ("/gio/contexts/context-independence", test_context_independence);
     431                 :          1 :   g_test_add_func ("/gio/contexts/context-specific/identity", test_context_specific_identity);
     432                 :          1 :   g_test_add_func ("/gio/contexts/context-specific/emit", test_context_specific_emit);
     433                 :          1 :   g_test_add_func ("/gio/contexts/context-specific/emit-and-unref", test_context_specific_emit_and_unref);
     434                 :            : 
     435                 :          1 :   ret = g_test_run();
     436                 :            : 
     437                 :          1 :   g_free (test_file_buffer);
     438                 :          1 :   g_free (test_file);
     439                 :            : 
     440                 :          1 :   return ret;
     441                 :            : }

Generated by: LCOV version 1.14