LCOV - code coverage report
Current view: top level - gio - gcontextspecificgroup.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 94.6 % 93 88
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 8 8
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright © 2015 Canonical Limited
       3                 :             :  *
       4                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       5                 :             :  *
       6                 :             :  * This library is free software; you can redistribute it and/or
       7                 :             :  * modify it under the terms of the GNU Lesser General Public
       8                 :             :  * License as published by the Free Software Foundation; either
       9                 :             :  * version 2.1 of the License, or (at your option) any later version.
      10                 :             :  *
      11                 :             :  * This library 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.  See the GNU
      14                 :             :  * Lesser General Public License for more details.
      15                 :             :  *
      16                 :             :  * You should have received a copy of the GNU Lesser General Public
      17                 :             :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18                 :             :  *
      19                 :             :  * Author: Ryan Lortie <desrt@desrt.ca>
      20                 :             :  */
      21                 :             : 
      22                 :             : #include "config.h"
      23                 :             : 
      24                 :             : #include "gcontextspecificgroup.h"
      25                 :             : 
      26                 :             : #include <glib-object.h>
      27                 :             : #include "glib-private.h"
      28                 :             : 
      29                 :             : typedef struct
      30                 :             : {
      31                 :             :   GSource   source;
      32                 :             : 
      33                 :             :   GMutex    lock;
      34                 :             :   gpointer  instance;
      35                 :             :   GQueue    pending;
      36                 :             : } GContextSpecificSource;
      37                 :             : 
      38                 :             : static gboolean
      39                 :       14419 : g_context_specific_source_dispatch (GSource     *source,
      40                 :             :                                     GSourceFunc  callback,
      41                 :             :                                     gpointer     user_data)
      42                 :             : {
      43                 :       14419 :   GContextSpecificSource *css = (GContextSpecificSource *) source;
      44                 :             :   guint signal_id;
      45                 :             : 
      46                 :       14419 :   g_mutex_lock (&css->lock);
      47                 :             : 
      48                 :       14419 :   g_assert (!g_queue_is_empty (&css->pending));
      49                 :       14419 :   signal_id = GPOINTER_TO_UINT (g_queue_pop_head (&css->pending));
      50                 :             : 
      51                 :       14419 :   if (g_queue_is_empty (&css->pending))
      52                 :       14419 :     g_source_set_ready_time (source, -1);
      53                 :             : 
      54                 :       14419 :   g_mutex_unlock (&css->lock);
      55                 :             : 
      56                 :       14419 :   g_signal_emit (css->instance, signal_id, 0);
      57                 :             : 
      58                 :       14419 :   return TRUE;
      59                 :             : }
      60                 :             : 
      61                 :             : static void
      62                 :          23 : g_context_specific_source_finalize (GSource *source)
      63                 :             : {
      64                 :          23 :   GContextSpecificSource *css = (GContextSpecificSource *) source;
      65                 :             : 
      66                 :          23 :   g_mutex_clear (&css->lock);
      67                 :          23 :   g_queue_clear (&css->pending);
      68                 :          23 : }
      69                 :             : 
      70                 :             : static GContextSpecificSource *
      71                 :          23 : g_context_specific_source_new (const gchar *name,
      72                 :             :                                gpointer     instance)
      73                 :             : {
      74                 :             :   static GSourceFuncs source_funcs = {
      75                 :             :     NULL,
      76                 :             :     NULL,
      77                 :             :     g_context_specific_source_dispatch,
      78                 :             :     g_context_specific_source_finalize,
      79                 :             :     NULL, NULL
      80                 :             :   };
      81                 :             :   GContextSpecificSource *css;
      82                 :             :   GSource *source;
      83                 :             : 
      84                 :          23 :   source = g_source_new (&source_funcs, sizeof (GContextSpecificSource));
      85                 :          23 :   css = (GContextSpecificSource *) source;
      86                 :             : 
      87                 :          23 :   g_source_set_name (source, name);
      88                 :             : 
      89                 :          23 :   g_mutex_init (&css->lock);
      90                 :          23 :   g_queue_init (&css->pending);
      91                 :          23 :   css->instance = instance;
      92                 :             : 
      93                 :          23 :   return css;
      94                 :             : }
      95                 :             : 
      96                 :             : static gboolean
      97                 :          10 : g_context_specific_group_change_state (gpointer user_data)
      98                 :             : {
      99                 :          10 :   GContextSpecificGroup *group = user_data;
     100                 :             : 
     101                 :          10 :   g_mutex_lock (&group->lock);
     102                 :             : 
     103                 :          10 :   if (group->requested_state != group->effective_state)
     104                 :             :     {
     105                 :          10 :       (* group->requested_func) ();
     106                 :             : 
     107                 :          10 :       group->effective_state = group->requested_state;
     108                 :          10 :       group->requested_func = NULL;
     109                 :             : 
     110                 :          10 :       g_cond_broadcast (&group->cond);
     111                 :             :     }
     112                 :             : 
     113                 :          10 :   g_mutex_unlock (&group->lock);
     114                 :             : 
     115                 :          10 :   return FALSE;
     116                 :             : }
     117                 :             : 
     118                 :             : /* this is not the most elegant way to deal with this, but it's probably
     119                 :             :  * the best.  there are only two other things we could do, really:
     120                 :             :  *
     121                 :             :  *  - run the start function (but not the stop function) from the user's
     122                 :             :  *    thread under some sort of lock.  we don't run the stop function
     123                 :             :  *    from the user's thread to avoid the destroy-while-emitting problem
     124                 :             :  *
     125                 :             :  *  - have some check-and-compare functionality similar to what
     126                 :             :  *    gsettings does where we send an artificial event in case we notice
     127                 :             :  *    a change during the potential race period (using stat, for
     128                 :             :  *    example)
     129                 :             :  */
     130                 :             : static void
     131                 :        1027 : g_context_specific_group_request_state (GContextSpecificGroup *group,
     132                 :             :                                         gboolean               requested_state,
     133                 :             :                                         GCallback              requested_func)
     134                 :             : {
     135                 :        1027 :   if (requested_state != group->requested_state)
     136                 :             :     {
     137                 :          10 :       if (group->effective_state != group->requested_state)
     138                 :             :         {
     139                 :             :           /* abort the currently pending state transition */
     140                 :           0 :           g_assert (group->effective_state == requested_state);
     141                 :             : 
     142                 :           0 :           group->requested_state = requested_state;
     143                 :           0 :           group->requested_func = NULL;
     144                 :             :         }
     145                 :             :       else
     146                 :             :         {
     147                 :             :           /* start a new state transition */
     148                 :          10 :           group->requested_state = requested_state;
     149                 :          10 :           group->requested_func = requested_func;
     150                 :             : 
     151                 :          10 :           g_main_context_invoke (GLIB_PRIVATE_CALL(g_get_worker_context) (),
     152                 :             :                                  g_context_specific_group_change_state, group);
     153                 :             :         }
     154                 :             :     }
     155                 :             : 
     156                 :        1039 :   while (group->requested_state != group->effective_state)
     157                 :          12 :     g_cond_wait (&group->cond, &group->lock);
     158                 :        1027 : }
     159                 :             : 
     160                 :             : gpointer
     161                 :        1023 : g_context_specific_group_get (GContextSpecificGroup *group,
     162                 :             :                               GType                  type,
     163                 :             :                               goffset                context_offset,
     164                 :             :                               GCallback              start_func)
     165                 :             : {
     166                 :             :   GContextSpecificSource *css;
     167                 :             :   GMainContext *context;
     168                 :             : 
     169                 :        1023 :   context = g_main_context_get_thread_default ();
     170                 :        1023 :   if (!context)
     171                 :           3 :     context = g_main_context_default ();
     172                 :             : 
     173                 :        1023 :   g_mutex_lock (&group->lock);
     174                 :             : 
     175                 :        1023 :   if (!group->table)
     176                 :           3 :     group->table = g_hash_table_new (NULL, NULL);
     177                 :             : 
     178                 :        1023 :   css = g_hash_table_lookup (group->table, context);
     179                 :             : 
     180                 :        1023 :   if (!css)
     181                 :             :     {
     182                 :             :       gpointer instance;
     183                 :             : 
     184                 :          23 :       instance = g_object_new (type, NULL);
     185                 :          23 :       css = g_context_specific_source_new (g_type_name (type), instance);
     186                 :          23 :       G_STRUCT_MEMBER (GMainContext *, instance, context_offset) = g_main_context_ref (context);
     187                 :          23 :       g_source_attach ((GSource *) css, context);
     188                 :             : 
     189                 :          23 :       g_hash_table_insert (group->table, context, css);
     190                 :             :     }
     191                 :             :   else
     192                 :        1000 :     g_object_ref (css->instance);
     193                 :             : 
     194                 :        1023 :   if (start_func)
     195                 :        1022 :     g_context_specific_group_request_state (group, TRUE, start_func);
     196                 :             : 
     197                 :        1023 :   g_mutex_unlock (&group->lock);
     198                 :             : 
     199                 :        1023 :   return css->instance;
     200                 :             : }
     201                 :             : 
     202                 :             : void
     203                 :          23 : g_context_specific_group_remove (GContextSpecificGroup *group,
     204                 :             :                                  GMainContext          *context,
     205                 :             :                                  gpointer               instance,
     206                 :             :                                  GCallback              stop_func)
     207                 :             : {
     208                 :             :   GContextSpecificSource *css;
     209                 :             : 
     210                 :          23 :   if (!context)
     211                 :             :     {
     212                 :           0 :       g_critical ("Removing %s with NULL context.  This object was probably directly constructed from a "
     213                 :             :                   "dynamic language.  This is not a valid use of the API.", G_OBJECT_TYPE_NAME (instance));
     214                 :           0 :       return;
     215                 :             :     }
     216                 :             : 
     217                 :          23 :   g_mutex_lock (&group->lock);
     218                 :          23 :   css = g_hash_table_lookup (group->table, context);
     219                 :          23 :   g_hash_table_remove (group->table, context);
     220                 :          23 :   g_assert (css);
     221                 :             : 
     222                 :             :   /* stop only if we were the last one */
     223                 :          23 :   if (stop_func && g_hash_table_size (group->table) == 0)
     224                 :           5 :     g_context_specific_group_request_state (group, FALSE, stop_func);
     225                 :             : 
     226                 :          23 :   g_mutex_unlock (&group->lock);
     227                 :             : 
     228                 :          23 :   g_assert (css->instance == instance);
     229                 :             : 
     230                 :          23 :   g_source_destroy ((GSource *) css);
     231                 :          23 :   g_source_unref ((GSource *) css);
     232                 :          23 :   g_main_context_unref (context);
     233                 :             : }
     234                 :             : 
     235                 :             : void
     236                 :        2572 : g_context_specific_group_emit (GContextSpecificGroup *group,
     237                 :             :                                guint                  signal_id)
     238                 :             : {
     239                 :        2572 :   g_mutex_lock (&group->lock);
     240                 :             : 
     241                 :        2572 :   if (group->table)
     242                 :             :     {
     243                 :             :       GHashTableIter iter;
     244                 :             :       gpointer value;
     245                 :             :       gpointer ptr;
     246                 :             : 
     247                 :        2222 :       ptr = GUINT_TO_POINTER (signal_id);
     248                 :             : 
     249                 :        2222 :       g_hash_table_iter_init (&iter, group->table);
     250                 :       24325 :       while (g_hash_table_iter_next (&iter, NULL, &value))
     251                 :             :         {
     252                 :       22103 :           GContextSpecificSource *css = value;
     253                 :             : 
     254                 :       22103 :           g_mutex_lock (&css->lock);
     255                 :             : 
     256                 :       22103 :           g_queue_remove (&css->pending, ptr);
     257                 :       22103 :           g_queue_push_tail (&css->pending, ptr);
     258                 :             : 
     259                 :       22103 :           g_source_set_ready_time ((GSource *) css, 0);
     260                 :             : 
     261                 :       22103 :           g_mutex_unlock (&css->lock);
     262                 :             :         }
     263                 :             :     }
     264                 :             : 
     265                 :        2572 :   g_mutex_unlock (&group->lock);
     266                 :        2572 : }
        

Generated by: LCOV version 2.0-1