[glib/wip/refptr: 1/5] Add reference counted memory areas
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/refptr: 1/5] Add reference counted memory areas
- Date: Sun, 8 Feb 2015 13:41:05 +0000 (UTC)
commit c7cac3bc408d36f8cdf4fa5dc32c0dd7cf3bf453
Author: Emmanuele Bassi <ebassi gnome org>
Date: Mon Jan 26 12:26:38 2015 +0000
Add reference counted memory areas
A lot of our data structures are reference counted these days. It can be
useful to allow creating reference counted data without necessarily
duplicating all the reference counting machinery — a field in the
struct, reference and unreference functions, a GDestroyNotify-compatible
function for destroying the contents.
We can re-use the same overallocation and pointer offset trick we use
when allocating a GTypeInstance in gobject, and add all the reference
counting meta-information directly on top of the allocation size.
https://bugzilla.gnome.org/show_bug.cgi?id=622721
docs/reference/glib/glib-docs.xml | 1 +
docs/reference/glib/glib-sections.txt | 24 ++
glib/Makefile.am | 2 +
glib/glib.h | 1 +
glib/gref.c | 464 +++++++++++++++++++++++++++++++++
glib/gref.h | 65 +++++
6 files changed, 557 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml
index 12de3f2..cfec541 100644
--- a/docs/reference/glib/glib-docs.xml
+++ b/docs/reference/glib/glib-docs.xml
@@ -55,6 +55,7 @@
<xi:include href="xml/modules.xml" />
<xi:include href="xml/memory.xml" />
<xi:include href="xml/memory_slices.xml" />
+ <xi:include href="xml/refcount.xml" />
<xi:include href="xml/iochannels.xml" />
<xi:include href="xml/error_reporting.xml" />
<xi:include href="xml/warnings.xml" />
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 45827d6..b6b6f35 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -1017,6 +1017,30 @@ g_mem_profile
</SECTION>
<SECTION>
+<FILE>refcount</FILE>
+g_ref_pointer_new
+g_ref_pointer_new0
+
+<SUBSECTION>
+g_ref_pointer_alloc
+g_ref_pointer_alloc0
+g_ref_pointer_take
+
+<SUBSECTION>
+g_ref_pointer_make_atomic
+
+<SUBSECTION>
+g_ref_pointer_acquire
+g_ref_pointer_release
+
+<SUBSECTION>
+g_ref_count_init
+g_ref_count_inc
+g_ref_count_dec
+g_ref_count_make_atomic
+</SECTION>
+
+<SECTION>
<TITLE>Warnings and Assertions</TITLE>
<FILE>warnings</FILE>
g_print
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 9c728f8..59c5d93 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -153,6 +153,7 @@ libglib_2_0_la_SOURCES = \
gquark.c \
gqueue.c \
grand.c \
+ gref.c \
gregex.c \
gscanner.c \
gscripttable.h \
@@ -285,6 +286,7 @@ glibsubinclude_HEADERS = \
gquark.h \
gqueue.h \
grand.h \
+ gref.h \
gregex.h \
gscanner.h \
gsequence.h \
diff --git a/glib/glib.h b/glib/glib.h
index 1212d13..0d60063 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -69,6 +69,7 @@
#include <glib/gquark.h>
#include <glib/gqueue.h>
#include <glib/grand.h>
+#include <glib/gref.h>
#include <glib/gregex.h>
#include <glib/gscanner.h>
#include <glib/gsequence.h>
diff --git a/glib/gref.c b/glib/gref.c
new file mode 100644
index 0000000..93e3da3
--- /dev/null
+++ b/glib/gref.c
@@ -0,0 +1,464 @@
+/* gref.h: Reference counted memory areas
+ *
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:refcount
+ * @short_description: reference counted memory areas
+ * @title: References
+ *
+ * These functions provide support for allocating and freeing reference
+ * counted memory areas.
+ *
+ * Reference counted memory areas are kept alive as long as something holds
+ * a reference on them; as soon as their reference count drops to zero, the
+ * associated memory is freed.
+ */
+
+#include "config.h"
+#include "glibconfig.h"
+
+#include "gref.h"
+
+#include "gatomic.h"
+#include "gconstructor.h"
+#include "gmessages.h"
+#include "gtestutils.h"
+#include "valgrind.h"
+
+#include <string.h>
+
+/**
+ * g_ref_count_init:
+ * @refcount: a pointer to an integer used for reference counting
+ * @atomic: whether reference counting should be atomic
+ *
+ * Initializes a reference counting value.
+ *
+ * Since: 2.44
+ */
+void
+g_ref_count_init (volatile int *refcount,
+ gboolean atomic)
+{
+ *refcount = atomic ? -1 : 1;
+}
+
+/**
+ * g_ref_count_inc:
+ * @refcount: a pointer to an integer used for reference counting
+ *
+ * Increases a reference counter, using atomic operations if needed.
+ *
+ * Since: 2.44
+ */
+void
+g_ref_count_inc (volatile int *refcount)
+{
+ int refs;
+
+test_again:
+ refs = *refcount;
+
+ if (refs > 0)
+ *refcount += 1;
+ else if (G_UNLIKELY (!g_atomic_int_compare_and_exchange (refcount, refs, refs - 1)))
+ goto test_again;
+}
+
+/**
+ * g_ref_count_dec:
+ * @refcount: a pointer to an integer used for reference counting
+ *
+ * Decreases a reference counter, using atomic operations if needed.
+ *
+ * Returns: %TRUE if the reference counter hit zero
+ *
+ * Since: 2.44
+ */
+gboolean
+g_ref_count_dec (volatile int *refcount)
+{
+ int refs;
+
+test_again:
+ refs = *refcount;
+ if (refs == -1 || refs == 1)
+ return TRUE;
+ if (refs > 0)
+ *refcount -= 1;
+ else if (G_UNLIKELY (!g_atomic_int_compare_and_exchange (refcount, refs, refs + 1)))
+ goto test_again;
+
+ return FALSE;
+}
+
+/**
+ * g_ref_count_make_atomic:
+ * @refcount: a pointer to an integer used for reference counting
+ *
+ * Changes the reference counting semantics of a reference counter to
+ * be atomic if they weren't.
+ *
+ * Since: 2.44
+ */
+void
+g_ref_count_make_atomic (volatile int *refcount)
+{
+ int refs = g_atomic_int_get (refcount);
+
+ if (refs > 0)
+ *refcount = -refs;
+}
+
+#define STRUCT_ALIGNMENT (2 * sizeof (gsize))
+#define ALIGN_STRUCT(offset) ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT)
+
+#define G_REF_PTR_SIZE sizeof (GRefPtr)
+#define G_REF_PTR(ptr) (GRefPtr *) (((char *) (ptr)) - G_REF_PTR_SIZE)
+
+typedef struct _GRefPtr GRefPtr;
+
+struct _GRefPtr
+{
+ volatile int ref_count;
+
+ gsize alloc_size;
+
+ GDestroyNotify notify;
+};
+
+#ifdef G_ENABLE_DEBUG
+#include "ghash.h"
+#include "gthread.h"
+
+static GHashTable *referenced_pointers;
+G_LOCK_DEFINE_STATIC (referenced_pointers);
+
+static inline void
+g_ref_pointer_register (gpointer ref)
+{
+ G_LOCK (referenced_pointers);
+ if (G_UNLIKELY (referenced_pointers == NULL))
+ referenced_pointers = g_hash_table_new (NULL, NULL);
+
+ g_hash_table_add (referenced_pointers, ref);
+ G_UNLOCK (referenced_pointers);
+}
+
+static inline void
+g_ref_pointer_unregister (gpointer ref)
+{
+ G_LOCK (referenced_pointers);
+ if (G_LIKELY (referenced_pointers != NULL))
+ g_hash_table_remove (referenced_pointers, ref);
+ G_UNLOCK (referenced_pointers);
+}
+
+static gboolean
+g_is_ref_pointer (gconstpointer ref)
+{
+ gboolean res = FALSE;
+
+ G_LOCK (referenced_pointers);
+ if (G_LIKELY (referenced_pointers != NULL))
+ res = g_hash_table_contains (referenced_pointers, ref);
+ G_UNLOCK (referenced_pointers);
+
+ return res;
+}
+
+#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA
+#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(glib_ref_pointers_clear)
+#endif
+G_DEFINE_DESTRUCTOR(glib_ref_pointers_clear)
+
+static void
+glib_ref_pointers_clear (void)
+{
+ if (referenced_pointers != NULL)
+ g_hash_table_unref (referenced_pointers);
+}
+
+#endif /* G_ENABLE_DEBUG */
+
+/**
+ * g_ref_pointer_new:
+ * @Type: the type to allocate
+ * @free_func: the free function
+ *
+ * Allocates a reference counted memory area corresponding to @Type.
+ *
+ * See also: g_ref_pointer_alloc()
+ *
+ * Returns: the newly allocated memory area
+ *
+ * Since: 2.44
+ */
+
+/**
+ * g_ref_pointer_new0:
+ * @Type: the type to allocate
+ * @free_func: the free function
+ *
+ * Allocates and clears a reference counted memory area corresponding
+ * to @Type.
+ *
+ * See also: g_ref_pointer_alloc0()
+ *
+ * Returns: the newly allocated memory area
+ *
+ * Since: 2.44
+ */
+
+/*< private >
+ * g_ref_pointer_destroy:
+ * @ref: a reference counted memory area
+ *
+ * Forces the destruction of a reference counter memory area.
+ */
+static void
+g_ref_pointer_destroy (gpointer ref)
+{
+ GRefPtr *real = G_REF_PTR (ref);
+ gsize alloc_size = real->alloc_size;
+ gsize private_size = G_REF_PTR_SIZE;
+ char *allocated = ((char *) ref) - private_size;
+
+ if (real->notify != NULL)
+ real->notify (ref);
+
+#ifdef G_ENABLE_DEBUG
+ g_ref_pointer_unregister (ref);
+#endif
+
+ if (RUNNING_ON_VALGRIND)
+ {
+ private_size += ALIGN_STRUCT (1);
+ allocated -= ALIGN_STRUCT (1);
+
+ *(gpointer *) (allocated + private_size + alloc_size) = NULL;
+ g_free (allocated);
+ }
+ else
+ g_free (allocated);
+}
+
+static gpointer
+g_ref_pointer_alloc_internal (gsize alloc_size,
+ gboolean clear,
+ GDestroyNotify notify)
+{
+ gsize private_size = G_REF_PTR_SIZE;
+ gchar *allocated;
+ GRefPtr *real;
+
+ g_return_val_if_fail (alloc_size != 0, NULL);
+
+ 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);
+ }
+
+#ifdef G_ENABLE_DEBUG
+ g_ref_pointer_register (allocated + private_size);
+#endif
+
+ real = (GRefPtr *) allocated;
+ real->notify = notify;
+ real->alloc_size = alloc_size;
+ g_ref_count_init (&real->ref_count, FALSE);
+
+ return allocated + private_size;
+}
+
+/**
+ * g_ref_pointer_alloc:
+ * @alloc_size: the number of bytes to allocate
+ * @notify: (nullable): a function to be called when the reference
+ * count drops to zero
+ *
+ * Allocates a reference counted memory area.
+ *
+ * Reference counted memory areas are automatically freed when their
+ * reference count drops to zero.
+ *
+ * Use g_ref_pointer_acquire() to acquire a reference, and
+ * g_ref_pointer_release() to release it when you're done.
+ *
+ * The contents of the returned memory are undefined.
+ *
+ * Returns: a pointer to the allocated memory
+ *
+ * Since: 2.44
+ */
+gpointer
+g_ref_pointer_alloc (gsize alloc_size,
+ GDestroyNotify notify)
+{
+ return g_ref_pointer_alloc_internal (alloc_size, FALSE, notify);
+}
+
+/**
+ * g_ref_pointer_alloc0:
+ * @alloc_size: the number of bytes to allocate
+ * @notify: (nullable): a function to be called when the reference
+ * count drops to zero
+ *
+ * Allocates a reference counted memory area.
+ *
+ * Reference counted memory areas are automatically freed when their
+ * reference count drops to zero.
+ *
+ * Use g_ref_pointer_acquire() to acquire a reference, and
+ * g_ref_pointer_release() to release it.
+ *
+ * The contents of the returned memory are set to 0.
+ *
+ * Returns: a pointer to the allocated memory
+ *
+ * Since: 2.44
+ */
+gpointer
+g_ref_pointer_alloc0 (gsize alloc_size,
+ GDestroyNotify notify)
+{
+ return g_ref_pointer_alloc_internal (alloc_size, TRUE, notify);
+}
+
+/**
+ * g_ref_pointer_take:
+ * @data: data to duplicate
+ * @alloc_size: the size of @data
+ * @notify: notification function to be called when the reference
+ * count drops to zero
+ *
+ * Duplicates existing data into a reference counted memory area.
+ *
+ * Returns: the newly allocated reference counted area
+ *
+ * Since: 2.44
+ */
+gpointer
+g_ref_pointer_take (gconstpointer data,
+ gsize alloc_size,
+ GDestroyNotify notify)
+{
+ gpointer res = g_ref_pointer_alloc (alloc_size, notify);
+
+ memcpy (res, data, alloc_size);
+
+ return res;
+}
+
+/**
+ * g_ref_pointer_acquire:
+ * @ref: a reference counted memory area
+ *
+ * Acquires a reference on the given memory area.
+ *
+ * Use g_ref_pointer_release() to release the reference when done.
+ *
+ * You should only call this function if you are implementing
+ * a reference counted type.
+ *
+ * Returns: the memory area, with its reference count increased by 1
+ *
+ * Since: 2.44
+ */
+gpointer
+g_ref_pointer_acquire (gpointer ref)
+{
+ GRefPtr *real = G_REF_PTR (ref);
+
+ g_return_val_if_fail (ref != NULL, NULL);
+#ifdef G_ENABLE_DEBUG
+ g_return_val_if_fail (g_is_ref_pointer (ref), ref);
+#endif
+
+ g_ref_count_inc (&real->ref_count);
+
+ return ref;
+}
+
+/**
+ * g_ref_pointer_release:
+ * @ref: a reference counted memory area
+ *
+ * Releases a reference acquired using g_ref_pointer_acquire().
+ *
+ * If the reference count drops to zero, the notification function
+ * used when allocating the memory will be called, and then the memory
+ * area will be freed.
+ *
+ * You should only call this function if you are implementing
+ * a reference counted type.
+ *
+ * Since: 2.44
+ */
+void
+g_ref_pointer_release (gpointer ref)
+{
+ GRefPtr *real = G_REF_PTR (ref);
+
+ g_return_if_fail (ref != NULL);
+#ifdef G_ENABLE_DEBUG
+ g_return_if_fail (g_is_ref_pointer (ref));
+#endif
+
+ if (g_ref_count_dec (&real->ref_count))
+ g_ref_pointer_destroy (ref);
+}
+
+/**
+ * g_ref_pointer_make_atomic:
+ * @ref: a reference counted memory area
+ *
+ * Makes reference count operations on a reference counted memory area
+ * always atomic.
+ *
+ * Since: 2.44
+ */
+void
+g_ref_pointer_make_atomic (gpointer ref)
+{
+ GRefPtr *real = G_REF_PTR (ref);
+
+ g_return_if_fail (ref != NULL);
+#ifdef G_ENABLE_DEBUG
+ g_return_if_fail (g_is_ref_pointer (ref));
+#endif
+
+ g_ref_count_make_atomic (&real->ref_count);
+}
diff --git a/glib/gref.h b/glib/gref.h
new file mode 100644
index 0000000..dbbd2d2
--- /dev/null
+++ b/glib/gref.h
@@ -0,0 +1,65 @@
+/* gref.h: Reference counted memory areas
+ *
+ * Copyright 2015 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __G_REF_H__
+#define __G_REF_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only <glib.h> can be included directly."
+#endif
+
+#include <glib/gtypes.h>
+
+G_BEGIN_DECLS
+
+GLIB_AVAILABLE_IN_2_44
+gpointer g_ref_pointer_alloc (gsize alloc_size,
+ GDestroyNotify notify) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE (1);
+GLIB_AVAILABLE_IN_2_44
+gpointer g_ref_pointer_alloc0 (gsize alloc_size,
+ GDestroyNotify notify) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE (1);
+
+#define g_ref_pointer_new(Type,free_func) ((Type *) g_ref_pointer_alloc (sizeof (Type),
(GDestroyNotify) free_func))
+
+#define g_ref_pointer_new0(Type,free_func) ((Type *) g_ref_pointer_alloc0 (sizeof (Type),
(GDestroyNotify) free_func))
+
+GLIB_AVAILABLE_IN_2_44
+gpointer g_ref_pointer_take (gconstpointer data,
+ gsize alloc_size,
+ GDestroyNotify notify) G_GNUC_WARN_UNUSED_RESULT;
+GLIB_AVAILABLE_IN_2_44
+void g_ref_pointer_make_atomic (gpointer ref);
+
+GLIB_AVAILABLE_IN_2_44
+gpointer g_ref_pointer_acquire (gpointer ref);
+GLIB_AVAILABLE_IN_2_44
+void g_ref_pointer_release (gpointer ref);
+
+GLIB_AVAILABLE_IN_2_44
+void g_ref_count_init (volatile int *refcount,
+ gboolean atomic);
+GLIB_AVAILABLE_IN_2_44
+void g_ref_count_inc (volatile int *refcount);
+GLIB_AVAILABLE_IN_2_44
+gboolean g_ref_count_dec (volatile int *refcount);
+GLIB_AVAILABLE_IN_2_44
+void g_ref_count_make_atomic (volatile int *refcount);
+
+G_END_DECLS
+
+#endif /* __G_REF_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]