LCOV - code coverage report
Current view: top level - glib/gio - gcontextspecificgroup.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 88 93 94.6 %
Date: 2024-03-26 05:16:46 Functions: 8 8 100.0 %
Branches: 25 28 89.3 %

           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                 :      18577 : g_context_specific_source_dispatch (GSource     *source,
      40                 :            :                                     GSourceFunc  callback,
      41                 :            :                                     gpointer     user_data)
      42                 :            : {
      43                 :      18577 :   GContextSpecificSource *css = (GContextSpecificSource *) source;
      44                 :            :   guint signal_id;
      45                 :            : 
      46                 :      18577 :   g_mutex_lock (&css->lock);
      47                 :            : 
      48                 :      18579 :   g_assert (!g_queue_is_empty (&css->pending));
      49                 :      18579 :   signal_id = GPOINTER_TO_UINT (g_queue_pop_head (&css->pending));
      50                 :            : 
      51         [ +  + ]:      18579 :   if (g_queue_is_empty (&css->pending))
      52                 :      18572 :     g_source_set_ready_time (source, -1);
      53                 :            : 
      54                 :      18577 :   g_mutex_unlock (&css->lock);
      55                 :            : 
      56                 :      18579 :   g_signal_emit (css->instance, signal_id, 0);
      57                 :            : 
      58                 :      18577 :   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                 :          8 : g_context_specific_group_change_state (gpointer user_data)
      98                 :            : {
      99                 :          8 :   GContextSpecificGroup *group = user_data;
     100                 :            : 
     101                 :          8 :   g_mutex_lock (&group->lock);
     102                 :            : 
     103         [ +  - ]:          8 :   if (group->requested_state != group->effective_state)
     104                 :            :     {
     105                 :          8 :       (* group->requested_func) ();
     106                 :            : 
     107                 :          8 :       group->effective_state = group->requested_state;
     108                 :          8 :       group->requested_func = NULL;
     109                 :            : 
     110                 :          8 :       g_cond_broadcast (&group->cond);
     111                 :            :     }
     112                 :            : 
     113                 :          8 :   g_mutex_unlock (&group->lock);
     114                 :            : 
     115                 :          8 :   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                 :       1026 : g_context_specific_group_request_state (GContextSpecificGroup *group,
     132                 :            :                                         gboolean               requested_state,
     133                 :            :                                         GCallback              requested_func)
     134                 :            : {
     135         [ +  + ]:       1026 :   if (requested_state != group->requested_state)
     136                 :            :     {
     137         [ -  + ]:          8 :       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                 :          8 :           group->requested_state = requested_state;
     149                 :          8 :           group->requested_func = requested_func;
     150                 :            : 
     151                 :          8 :           g_main_context_invoke (GLIB_PRIVATE_CALL(g_get_worker_context) (),
     152                 :            :                                  g_context_specific_group_change_state, group);
     153                 :            :         }
     154                 :            :     }
     155                 :            : 
     156         [ +  + ]:       1038 :   while (group->requested_state != group->effective_state)
     157                 :         12 :     g_cond_wait (&group->cond, &group->lock);
     158                 :       1026 : }
     159                 :            : 
     160                 :            : gpointer
     161                 :       1022 : 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                 :       1022 :   context = g_main_context_get_thread_default ();
     170         [ +  + ]:       1022 :   if (!context)
     171                 :          3 :     context = g_main_context_default ();
     172                 :            : 
     173                 :       1022 :   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                 :          4 :     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                 :       2476 : g_context_specific_group_emit (GContextSpecificGroup *group,
     237                 :            :                                guint                  signal_id)
     238                 :            : {
     239                 :       2476 :   g_mutex_lock (&group->lock);
     240                 :            : 
     241         [ +  + ]:       2476 :   if (group->table)
     242                 :            :     {
     243                 :            :       GHashTableIter iter;
     244                 :            :       gpointer value;
     245                 :            :       gpointer ptr;
     246                 :            : 
     247                 :       2172 :       ptr = GUINT_TO_POINTER (signal_id);
     248                 :            : 
     249                 :       2172 :       g_hash_table_iter_init (&iter, group->table);
     250         [ +  + ]:      23797 :       while (g_hash_table_iter_next (&iter, NULL, &value))
     251                 :            :         {
     252                 :      21625 :           GContextSpecificSource *css = value;
     253                 :            : 
     254                 :      21625 :           g_mutex_lock (&css->lock);
     255                 :            : 
     256                 :      21625 :           g_queue_remove (&css->pending, ptr);
     257                 :      21625 :           g_queue_push_tail (&css->pending, ptr);
     258                 :            : 
     259                 :      21625 :           g_source_set_ready_time ((GSource *) css, 0);
     260                 :            : 
     261                 :      21625 :           g_mutex_unlock (&css->lock);
     262                 :            :         }
     263                 :            :     }
     264                 :            : 
     265                 :       2476 :   g_mutex_unlock (&group->lock);
     266                 :       2476 : }

Generated by: LCOV version 1.14