[glib/wip/ebassi/rc] Add reference counted memory



commit b49fd4c7303c45c8d8969098e7f417b36e279f9a
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Tue Nov 15 12:27:16 2016 +0000

    Add reference counted memory
    
    A common pattern inside GLib, and the rest of the G* platform, consists
    of allocating some structure with a reference count. Usually, this
    structure is hidden, but in some cases reference counting can be bolted
    on top of public plain old data structures, or on top of existing C
    types, like a char array.
    
    If the data structure that needs reference counting is public we usually
    "box" it into a reference counted container. Unfortunately, since we're
    limited by the C syntax, this either means constantly de-referencing the
    contents of the box in order to get to the real type; or it means
    duplicating the whole API dealing with that type.
    
    The alternative is to make the "box" transparent, and hide the reference
    counting mechanism behind the pointer to the contents, using the same
    mechanism GObject employs to handle private instance data.
    
    This allows us to allocate some memory for a data structure and use it
    in the same way we normally would with memory allocated via the system
    allocator. Whenever we need to operate on the reference count of the
    data, though, we reach behind the pointer and access the real reference
    counted box.

 glib/grefcount.c |  148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 glib/grefcount.h |   21 ++++++++
 2 files changed, 169 insertions(+), 0 deletions(-)
---
diff --git a/glib/grefcount.c b/glib/grefcount.c
index b1da192..1772c11 100644
--- a/glib/grefcount.c
+++ b/glib/grefcount.c
@@ -23,7 +23,14 @@
 
 #include "grefcount.h"
 
+#include <string.h>
+#include <stdlib.h>
+
 #include "gatomic.h"
+#include "gmem.h"
+#include "gmessages.h"
+#include "gtestutils.h"
+#include "valgrind.h"
 
 /**
  * SECTION:refcount
@@ -178,3 +185,144 @@ g_ref_counter_is_atomic (volatile int *ref_count)
 
   return sign < 0;
 }
+
+typedef struct {
+  int ref_count;
+
+  gsize alloc_size;
+
+  GDestroyNotify notify_func;
+} GRef;
+
+#define STRUCT_ALIGNMENT        (2 * sizeof (gsize))
+#define ALIGN_STRUCT(offset)    ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT)
+
+#define G_REF_SIZE              sizeof (GRef)
+#define G_REF(ptr)              (GRef *) (((char *) (ptr)) - G_REF_SIZE)
+
+static void
+g_ref_free (gpointer ref)
+{
+  GRef *real_ref = G_REF (ref);
+  gsize alloc_size = real_ref->alloc_size;
+  gsize private_size = G_REF_SIZE;
+  char *allocated = ((char *) ref) - private_size;
+
+  if (real_ref->notify_func != NULL)
+    real_ref->notify_func (ref);
+
+  if (RUNNING_ON_VALGRIND)
+    {
+      private_size += ALIGN_STRUCT (1);
+      allocated -= ALIGN_STRUCT (1);
+
+      *(gpointer *) (allocated + private_size + alloc_size) = NULL;
+    }
+
+  g_free (allocated);
+}
+
+static gpointer
+g_ref_alloc_internal (gsize          alloc_size,
+                      gboolean       clear,
+                      gboolean       atomic,
+                      GDestroyNotify notify)
+{
+  gsize private_size = G_REF_SIZE;
+  char *allocated = NULL;
+  GRef *real_ref;
+
+  g_assert (alloc_size != 0);
+
+  /* When running under Valgrind we grow the allocation by one pointer, and we
+   * use the pointer at the end to keep a reference to the beginning of the
+   * public data; this way, we allow Valgrind to do some accounting and spot
+   * eventual leaks
+   */
+  if (RUNNING_ON_VALGRIND)
+    {
+      private_size += ALIGN_STRUCT (1);
+
+      if (clear)
+        allocated = g_malloc0 (private_size + alloc_size + sizeof (gpointer));
+      else
+        allocated = g_malloc (private_size + alloc_size + sizeof (gpointer));
+
+      *(gpointer *) (allocated + private_size + alloc_size) = allocated + ALIGN_STRUCT (1);
+
+      VALGRIND_MALLOCLIKE_BLOCK (allocated + private_size, alloc_size + sizeof (gpointer), 0, TRUE);
+      VALGRIND_MALLOCLIKE_BLOCK (allocated + ALIGN_STRUCT (1), private_size - ALIGN_STRUCT (1), 0, TRUE);
+    }
+  else
+    {
+      if (clear)
+        allocated = g_malloc0 (private_size + alloc_size);
+      else
+        allocated = g_malloc (private_size + alloc_size);
+    }
+
+  real_ref = (GRef *) allocated;
+  real_ref->alloc_size = alloc_size;
+  real_ref->notify_func = notify;
+
+  g_ref_counter_init (&real_ref->ref_count, atomic);
+
+  return allocated + private_size;
+}
+
+gpointer
+g_ref_alloc (gsize          size,
+             GDestroyNotify notify)
+{
+  g_return_val_if_fail (size > 0, NULL);
+
+  return g_ref_alloc_internal (size, FALSE, FALSE, notify);
+}
+
+gpointer
+g_ref_alloc0 (gsize          size,
+              GDestroyNotify notify)
+{
+  g_return_val_if_fail (size > 0, NULL);
+
+  return g_ref_alloc_internal (size, TRUE, FALSE, notify);
+}
+
+gpointer
+g_ref_dup (gconstpointer  data,
+           gsize          size,
+           GDestroyNotify notify)
+{
+  gpointer res;
+
+  g_return_val_if_fail (size > 0, NULL);
+
+  res = g_ref_alloc_internal (size, FALSE, FALSE, notify);
+
+  memcpy (res, data, size);
+
+  return res;
+}
+
+gpointer
+g_ref_acquire (gpointer ref)
+{
+  GRef *real_ref = G_REF (ref);
+
+  g_return_val_if_fail (ref != NULL, NULL);
+
+  g_ref_counter_acquire (&real_ref->ref_count);
+
+  return ref;
+}
+
+void
+g_ref_release (gpointer ref)
+{
+  GRef *real_ref = G_REF (ref);
+
+  g_return_if_fail (ref != NULL);
+
+  if (g_ref_counter_release (&real_ref->ref_count))
+    g_ref_free (ref);
+}
diff --git a/glib/grefcount.h b/glib/grefcount.h
index 5791e73..5e38028 100644
--- a/glib/grefcount.h
+++ b/glib/grefcount.h
@@ -38,6 +38,27 @@ void            g_ref_counter_make_atomic       (volatile int *ref_count);
 GLIB_AVAILABLE_IN_2_52
 gboolean        g_ref_counter_is_atomic         (volatile int *ref_count);
 
+#define g_ref_new(Type,Notify) \
+  (Type *) g_ref_alloc (sizeof (Type), Notify)
+
+#define g_ref_new0(Type,Notify) \
+  (Type *) g_ref_alloc0 (sizeof (Type), Notify)
+
+GLIB_AVAILABLE_IN_2_52
+gpointer        g_ref_alloc                     (gsize          size,
+                                                 GDestroyNotify notify);
+GLIB_AVAILABLE_IN_2_52
+gpointer        g_ref_alloc0                    (gsize          size,
+                                                 GDestroyNotify notify);
+GLIB_AVAILABLE_IN_2_52
+gpointer        g_ref_dup                       (gconstpointer  data,
+                                                 gsize          size,
+                                                 GDestroyNotify notify);
+GLIB_AVAILABLE_IN_2_52
+gpointer        g_ref_acquire                   (gpointer       ref);
+GLIB_AVAILABLE_IN_2_52
+void            g_ref_release                   (gpointer       ref);
+
 G_END_DECLS
 
 #endif /* __G_REF_COUNT_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]