LCOV - code coverage report
Current view: top level - gio/tests - contexts.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 99.0 % 195 193
Test Date: 2024-11-26 05:23:01 Functions: 95.7 % 23 22
Branches: - 0 0

             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                 :        3238 : 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                 :           4 : start_func (void)
     208                 :             : {
     209                 :           4 :   g_assert (!is_running);
     210                 :           4 :   g_atomic_int_set (&is_running, TRUE);
     211                 :           4 : }
     212                 :             : 
     213                 :             : static void
     214                 :           4 : stop_func (void)
     215                 :             : {
     216                 :           4 :   g_assert (is_running);
     217                 :           4 :   g_atomic_int_set (&is_running, FALSE);
     218                 :           4 : }
     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                 :        1021 : per_thread_thing_get (void)
     246                 :             : {
     247                 :        1021 :   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                 :        1010 :   for (i = 0; i < 100; i++)
     267                 :             :     {
     268                 :        1000 :       gpointer instance = per_thread_thing_get ();
     269                 :             : 
     270                 :       11000 :       for (j = 0; j < N_THREADS; j++)
     271                 :       10000 :         g_assert ((instance == instances[j]) == (thread_nr == j));
     272                 :             : 
     273                 :        1000 :       g_assert (g_atomic_int_get (&is_running));
     274                 :             : 
     275                 :        1000 :       g_thread_yield ();
     276                 :             : 
     277                 :        1000 :       g_assert (g_atomic_int_get (&is_running));
     278                 :             :     }
     279                 :             : 
     280                 :        1010 :   for (i = 0; i < 100; i++)
     281                 :             :     {
     282                 :        1000 :       g_object_unref (instances[thread_nr]);
     283                 :             : 
     284                 :       11000 :       for (j = 0; j < N_THREADS; j++)
     285                 :       10000 :         g_assert ((instances[thread_nr] == instances[j]) == (thread_nr == j));
     286                 :             : 
     287                 :        1000 :       g_assert (g_atomic_int_get (&is_running));
     288                 :             : 
     289                 :        1000 :       g_thread_yield ();
     290                 :             :     }
     291                 :             : 
     292                 :             :   /* drop the last ref */
     293                 :          10 :   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                 :       14408 : changed_emitted (PerThreadThing *thing,
     321                 :             :                  gpointer        user_data)
     322                 :             : {
     323                 :       14408 :   gint *observed_value = user_data;
     324                 :             : 
     325                 :       14408 :   g_atomic_int_set (observed_value, g_atomic_int_get (&current_value));
     326                 :       14408 : }
     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                 :       14427 :   while (g_atomic_int_get (&current_value) != -1)
     347                 :       14417 :     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                 :        3213 :       for (k = 0; k < g_test_rand_int_range (1, 5); k++)
     381                 :        2213 :         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                 :      428584 :         while (g_atomic_int_get (&observed_values[i]) != n)
     385                 :             :           {
     386                 :      418584 :             g_thread_yield ();
     387                 :             : 
     388                 :      418584 :             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 2.0-1