(root)/
glib-2.79.0/
gobject/
gobjectnotifyqueue.c
       1  /* GObject - GLib Type, Object, Parameter and Signal Library
       2   * Copyright (C) 1998-1999, 2000-2001 Tim Janik and Red Hat, Inc.
       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
      17   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   */
      19  
      20  /* WARNING:
      21   *
      22   *    This file is INSTALLED and other projects (outside of glib)
      23   *    #include its contents.
      24   */
      25  
      26  #ifndef __G_OBJECT_NOTIFY_QUEUE_H__
      27  #define __G_OBJECT_NOTIFY_QUEUE_H__
      28  
      29  #include <string.h> /* memset */
      30  
      31  #include <glib-object.h>
      32  
      33  G_BEGIN_DECLS
      34  
      35  
      36  /* --- typedefs --- */
      37  typedef struct _GObjectNotifyContext          GObjectNotifyContext;
      38  typedef struct _GObjectNotifyQueue            GObjectNotifyQueue;
      39  typedef void (*GObjectNotifyQueueDispatcher) (GObject     *object,
      40  					      guint        n_pspecs,
      41  					      GParamSpec **pspecs);
      42  
      43  
      44  /* --- structures --- */
      45  struct _GObjectNotifyContext
      46  {
      47    GQuark                       quark_notify_queue;
      48    GObjectNotifyQueueDispatcher dispatcher;
      49    GTrashStack                 *_nqueue_trash; /* unused */
      50  };
      51  struct _GObjectNotifyQueue
      52  {
      53    GObjectNotifyContext *context;
      54    GSList               *pspecs;
      55    guint16               n_pspecs;
      56    guint16               freeze_count;
      57  };
      58  
      59  G_LOCK_DEFINE_STATIC(notify_lock);
      60  
      61  /* --- functions --- */
      62  static void
      63  g_object_notify_queue_free (gpointer data)
      64  {
      65    GObjectNotifyQueue *nqueue = data;
      66  
      67    g_slist_free (nqueue->pspecs);
      68    g_slice_free (GObjectNotifyQueue, nqueue);
      69  }
      70  
      71  static inline GObjectNotifyQueue*
      72  g_object_notify_queue_freeze (GObject		   *object,
      73  			      GObjectNotifyContext *context)
      74  {
      75    GObjectNotifyQueue *nqueue;
      76  
      77    G_LOCK(notify_lock);
      78    nqueue = g_datalist_id_get_data (&object->qdata, context->quark_notify_queue);
      79    if (!nqueue)
      80      {
      81        nqueue = g_slice_new0 (GObjectNotifyQueue);
      82        nqueue->context = context;
      83        g_datalist_id_set_data_full (&object->qdata, context->quark_notify_queue,
      84  				   nqueue, g_object_notify_queue_free);
      85      }
      86  
      87    if (nqueue->freeze_count >= 65535)
      88      g_critical("Free queue for %s (%p) is larger than 65535,"
      89                 " called g_object_freeze_notify() too often."
      90                 " Forgot to call g_object_thaw_notify() or infinite loop",
      91                 G_OBJECT_TYPE_NAME (object), object);
      92    else
      93      nqueue->freeze_count++;
      94    G_UNLOCK(notify_lock);
      95  
      96    return nqueue;
      97  }
      98  
      99  static inline void
     100  g_object_notify_queue_thaw (GObject            *object,
     101  			    GObjectNotifyQueue *nqueue)
     102  {
     103    GObjectNotifyContext *context = nqueue->context;
     104    GParamSpec *pspecs_mem[16], **pspecs, **free_me = NULL;
     105    GSList *slist;
     106    guint n_pspecs = 0;
     107  
     108    g_return_if_fail (nqueue->freeze_count > 0);
     109    g_return_if_fail (g_atomic_int_get(&object->ref_count) > 0);
     110  
     111    G_LOCK(notify_lock);
     112  
     113    /* Just make sure we never get into some nasty race condition */
     114    if (G_UNLIKELY(nqueue->freeze_count == 0)) {
     115      G_UNLOCK(notify_lock);
     116      g_critical ("%s: property-changed notification for %s(%p) is not frozen",
     117  	        G_STRFUNC, G_OBJECT_TYPE_NAME (object), object);
     118      return;
     119    }
     120  
     121    nqueue->freeze_count--;
     122    if (nqueue->freeze_count) {
     123      G_UNLOCK(notify_lock);
     124      return;
     125    }
     126  
     127    pspecs = nqueue->n_pspecs > 16 ? free_me = g_new (GParamSpec*, nqueue->n_pspecs) : pspecs_mem;
     128  
     129    for (slist = nqueue->pspecs; slist; slist = slist->next)
     130      {
     131        pspecs[n_pspecs++] = slist->data;
     132      }
     133    g_datalist_id_set_data (&object->qdata, context->quark_notify_queue, NULL);
     134  
     135    G_UNLOCK(notify_lock);
     136  
     137    if (n_pspecs)
     138      context->dispatcher (object, n_pspecs, pspecs);
     139    g_free (free_me);
     140  }
     141  
     142  static inline void
     143  g_object_notify_queue_clear (GObject            *object,
     144  			     GObjectNotifyQueue *nqueue)
     145  {
     146    g_return_if_fail (nqueue->freeze_count > 0);
     147  
     148    G_LOCK(notify_lock);
     149  
     150    g_slist_free (nqueue->pspecs);
     151    nqueue->pspecs = NULL;
     152    nqueue->n_pspecs = 0;
     153  
     154    G_UNLOCK(notify_lock);
     155  }
     156  
     157  static inline void
     158  g_object_notify_queue_add (GObject            *object,
     159  			   GObjectNotifyQueue *nqueue,
     160  			   GParamSpec	      *pspec)
     161  {
     162    if (pspec->flags & G_PARAM_READABLE)
     163      {
     164        GParamSpec *redirect;
     165  
     166        G_LOCK(notify_lock);
     167  
     168        g_return_if_fail (nqueue->n_pspecs < 65535);
     169  
     170        redirect = g_param_spec_get_redirect_target (pspec);
     171        if (redirect)
     172  	pspec = redirect;
     173  	    
     174        /* we do the deduping in _thaw */
     175        if (g_slist_find (nqueue->pspecs, pspec) == NULL)
     176          {
     177            nqueue->pspecs = g_slist_prepend (nqueue->pspecs, pspec);
     178            nqueue->n_pspecs++;
     179          }
     180  
     181        G_UNLOCK(notify_lock);
     182      }
     183  }
     184  
     185  /* NB: This function is not threadsafe, do not ever use it if
     186   * you need a threadsafe notify queue.
     187   * Use g_object_notify_queue_freeze() to acquire the queue and
     188   * g_object_notify_queue_thaw() after you are done instead.
     189   */
     190  static inline GObjectNotifyQueue*
     191  g_object_notify_queue_from_object (GObject              *object,
     192                                     GObjectNotifyContext *context)
     193  {
     194    return g_datalist_id_get_data (&object->qdata, context->quark_notify_queue);
     195  }
     196  
     197  G_END_DECLS
     198  
     199  #endif /* __G_OBJECT_NOTIFY_QUEUE_H__ */