[glib/wip/ebassi/rc] Add reference counted memory
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/ebassi/rc] Add reference counted memory
- Date: Tue, 15 Nov 2016 13:36:19 +0000 (UTC)
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]