diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml
index d7ac0a500..9d253ee5f 100644
--- a/docs/reference/glib/glib-docs.xml
+++ b/docs/reference/glib/glib-docs.xml
@@ -121,6 +121,7 @@
+
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index fa83dd832..2a4d1f281 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -3478,3 +3478,15 @@ g_rc_box_acquire
g_rc_box_release
g_rc_box_release_full
+
+
+arcbox
+g_arc_box_alloc
+g_arc_box_alloc0
+g_arc_box_new
+g_arc_box_new0
+g_arc_box_dup
+g_arc_box_acquire
+g_arc_box_release
+g_arc_box_release_full
+
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 8ec5f4454..352ecb0ec 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -96,6 +96,7 @@ deprecated_sources = \
libglib_2_0_la_SOURCES = \
$(deprecated_sources) \
glib_probes.d \
+ garcbox.c \
garray.c \
gasyncqueue.c \
gasyncqueueprivate.h \
@@ -150,6 +151,7 @@ libglib_2_0_la_SOURCES = \
gqueue.c \
grand.c \
grcbox.c \
+ grcboxprivate.h \
grefcount.c \
gregex.c \
gscanner.c \
diff --git a/glib/garcbox.c b/glib/garcbox.c
new file mode 100644
index 000000000..ea6e742d8
--- /dev/null
+++ b/glib/garcbox.c
@@ -0,0 +1,333 @@
+/* garcbox.c: Atomically reference counted data
+ *
+ * Copyright 2018 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.1 of the License, 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 .
+ */
+
+#include "config.h"
+
+#include "grcbox.h"
+
+#include "gmessages.h"
+#include "grcboxprivate.h"
+#include "grefcount.h"
+
+#ifdef ENABLE_VALGRIND
+#include "valgrind.h"
+#endif
+
+#include
+
+#define G_ARC_BOX(p) (GArcBox *) (((char *) (p)) - G_ARC_BOX_SIZE)
+
+/**
+ * SECTION:arcbox
+ * @Title: Atomically reference counted data
+ * @Short_description: Allocated memory with atomic reference counting semantics
+ *
+ * An "atomically reference counted box", or "ArcBox", is an opaque wrapper
+ * data type that is guaranteed to be as big as the size of a given data type,
+ * and which augments the given data type with thread safe reference counting
+ * semantics for its memory management.
+ *
+ * ArcBox is useful if you have a plain old data type, like a structure
+ * typically placed on the stack, and you wish to provide additional API
+ * to use it on the heap, without necessarily implementing copy/free
+ * semantics, or your own reference counting.
+ *
+ * The typical use is:
+ *
+ * |[
+ * typedef struct {
+ * float x, y;
+ * } Point;
+ *
+ * Point *
+ * point_new (float x, float y)
+ * {
+ * Point *res = g_arc_box_new (Point);
+ *
+ * res->x = x;
+ * res->y = y;
+ *
+ * return res;
+ * }
+ * ]|
+ *
+ * Every time you wish to acquire a reference on the memory, you should
+ * call g_arc_box_acquire(); similarly, when you wish to release a reference
+ * you should call g_arc_box_release():
+ *
+ * |[
+ * Point *
+ * point_ref (Point *p)
+ * {
+ * return g_arc_box_acquire (p);
+ * }
+ *
+ * void
+ * point_unref (Point *p)
+ * {
+ * g_arc_box_release (p);
+ * }
+ * ]|
+ *
+ * If you have additional memory allocated inside the structure, you can
+ * use g_arc_box_release_full(), which takes a function pointer, which
+ * will be called if the reference released was the last:
+ *
+ * |[
+ * typedef struct {
+ * char *name;
+ * char *address;
+ * char *city;
+ * char *state;
+ * int age;
+ * } Person;
+ *
+ * void
+ * person_clear (Person *p)
+ * {
+ * g_free (p->name);
+ * g_free (p->address);
+ * g_free (p->city);
+ * g_free (p->state);
+ * }
+ *
+ * void
+ * person_unref (Person *p)
+ * {
+ * g_arc_box_release_full (p, (GDestroyNotify) person_clear);
+ * }
+ * ]|
+ *
+ * If you wish to transfer the ownership of a reference counted data
+ * type without increasing the reference count, you can use g_steal_pointer():
+ *
+ * |[
+ * Person *p = g_arc_box_new (Person);
+ *
+ * fill_person_details (p);
+ *
+ * add_person_to_database (db, g_steal_pointer (&p));
+ * ]|
+ *
+ * The reference counting operations on data allocated using g_arc_box_alloc(),
+ * g_arc_box_new(), and g_arc_box_dup() are guaranteed to be atomic, and thus
+ * can be safely be performed by different threads. It is important to note that
+ * only the reference acquisition and release are atomic; changes to the content
+ * of the data are your responsibility.
+ *
+ * Since: 2.58.
+ */
+
+/**
+ * g_arc_box_alloc:
+ * @block_size: the size of the allocation
+ *
+ * Allocates @block_size bytes of memory, and adds atomic
+ * reference counting semantics to it.
+ *
+ * The data will be freed when its reference count drops to
+ * zero.
+ *
+ * Returns: a pointer to the allocated memory
+ *
+ * Since: 2.58
+ */
+gpointer
+g_arc_box_alloc (gsize block_size)
+{
+ g_return_val_if_fail (block_size > 0, NULL);
+
+ return g_rc_box_alloc_full (block_size, TRUE, FALSE);
+}
+
+/**
+ * g_arc_box_alloc0:
+ * @block_size: the size of the allocation
+ *
+ * Allocates @block_size bytes of memory, and adds atomic
+ * referenc counting semantics to it.
+ *
+ * The contents of the returned data is set to 0's.
+ *
+ * The data will be freed when its reference count drops to
+ * zero.
+ *
+ * Returns: a pointer to the allocated memory
+ *
+ * Since: 2.58
+ */
+gpointer
+g_arc_box_alloc0 (gsize block_size)
+{
+ g_return_val_if_fail (block_size > 0, NULL);
+
+ return g_rc_box_alloc_full (block_size, TRUE, TRUE);
+}
+
+/**
+ * g_arc_box_new:
+ * @type: the type to allocate, typically a structure name
+ *
+ * A convenience macro to allocate atomically reference counted
+ * data with the size of the given @type.
+ *
+ * This macro calls g_arc_box_alloc() with `sizeof (@type)` and
+ * casts the returned pointer to a pointer of the given @type,
+ * avoiding a type cast in the source code.
+ *
+ * This macro cannot return %NULL, as the minimum allocation
+ * size from `sizeof (@type)` is 1 byte.
+ *
+ * Returns: (not nullable): a pointer to the allocated memory,
+ * cast to a pointer for the given @type
+ *
+ * Since: 2.58
+ */
+
+/**
+ * g_arc_box_new0:
+ * @type: the type to allocate, typically a structure name
+ *
+ * A convenience macro to allocate atomically reference counted
+ * data with the size of the given @type, and set its contents
+ * to 0.
+ *
+ * This macro calls g_arc_box_alloc0() with `sizeof (@type)` and
+ * casts the returned pointer to a pointer of the given @type,
+ * avoiding a type cast in the source code.
+ *
+ * This macro cannot return %NULL, as the minimum allocation
+ * size from `sizeof (@type)` is 1 byte.
+ *
+ * Returns: (not nullable): a pointer to the allocated memory,
+ * cast to a pointer for the given @type
+ *
+ * Since: 2.58
+ */
+
+/**
+ * g_arc_box_dup:
+ * @mem_block: (not nullable): a pointer to reference counted data
+ *
+ * Allocates a new block of data with atomic reference counting
+ * semantics, and copies the contents of @mem_block into
+ * it.
+ *
+ * Returns: (not nullable): a pointer to the allocated memory
+ *
+ * Since: 2.58
+ */
+gpointer
+(g_arc_box_dup) (gpointer mem_block)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+ gpointer res;
+
+ g_return_val_if_fail (mem_block != NULL, NULL);
+#ifndef G_DISABLE_ASSERT
+ g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL);
+#endif
+
+ res = g_rc_box_alloc_full (real_box->mem_size, TRUE, FALSE);
+ memcpy (res, mem_block, real_box->mem_size);
+
+ return res;
+}
+
+/**
+ * g_arc_box_acquire:
+ * @mem_block: (not nullable): a pointer to reference counted data
+ *
+ * Atomically acquires a reference on the data pointed by @mem_block.
+ *
+ * Returns: (not nullabl): a pointer to the data, with its reference
+ * count increased
+ *
+ * Since: 2.58
+ */
+gpointer
+(g_arc_box_acquire) (gpointer mem_block)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+
+ g_return_val_if_fail (mem_block != NULL, NULL);
+#ifndef G_DISABLE_ASSERT
+ g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL);
+#endif
+
+ g_atomic_ref_count_inc (&real_box->ref_count);
+
+ return mem_block;
+}
+
+/**
+ * g_arc_box_release:
+ * @mem_block: (not nullable): a pointer to reference counted data
+ *
+ * Atomically releases a reference on the data pointed by @mem_block.
+ *
+ * If the reference was the last one, it will free the
+ * resources allocated for @mem_block.
+ *
+ * Since: 2.58
+ */
+void
+g_arc_box_release (gpointer mem_block)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+
+ g_return_if_fail (mem_block != NULL);
+#ifndef G_DISABLE_ASSERT
+ g_return_if_fail (real_box->magic == G_BOX_MAGIC);
+#endif
+
+ if (g_atomic_ref_count_dec (&real_box->ref_count))
+ g_free (real_box);
+}
+
+/**
+ * g_arc_box_release_full:
+ * @mem_block: (not nullable): a pointer to reference counted data
+ * @clear_func: (not nullable): a function to call when clearing the data
+ *
+ * Atomically releases a reference on the data pointed by @mem_block.
+ *
+ * If the reference was the last one, it will call @clear_func
+ * to clear the contents of @mem_block, and then will free the
+ * resources allocated for @mem_block.
+ *
+ * Since: 2.58
+ */
+void
+g_arc_box_release_full (gpointer mem_block,
+ GDestroyNotify clear_func)
+{
+ GArcBox *real_box = G_ARC_BOX (mem_block);
+
+ g_return_if_fail (mem_block != NULL);
+ g_return_if_fail (clear_func != NULL);
+#ifndef G_DISABLE_ASSERT
+ g_return_if_fail (real_box->magic == G_BOX_MAGIC);
+#endif
+
+ if (g_atomic_ref_count_dec (&real_box->ref_count))
+ {
+ clear_func (mem_block);
+ g_free (real_box);
+ }
+}
diff --git a/glib/grcbox.c b/glib/grcbox.c
index 0860634be..d41a6def4 100644
--- a/glib/grcbox.c
+++ b/glib/grcbox.c
@@ -1,4 +1,4 @@
-/* grcbox.h: Reference counted data
+/* grcbox.c: Reference counted data
*
* Copyright 2018 Emmanuele Bassi
*
@@ -21,6 +21,7 @@
#include "grcbox.h"
#include "gmessages.h"
+#include "grcboxprivate.h"
#include "grefcount.h"
#ifdef ENABLE_VALGRIND
@@ -129,35 +130,21 @@
* Since: 2.58.
*/
-typedef struct {
- grefcount ref_count;
-
- gsize mem_size;
-
-#ifndef G_DISABLE_ASSERT
- /* A "magic" number, used to perform additional integrity
- * checks on the allocated data
- */
- guint32 magic;
-#endif
-} GRcBox;
-
-#define G_RC_BOX_MAGIC 0x44ae2bf0
-#define G_RC_BOX_SIZE sizeof (GRcBox)
#define G_RC_BOX(p) (GRcBox *) (((char *) (p)) - G_RC_BOX_SIZE)
/* We use the same alignment as GTypeInstance and GNU libc's malloc */
#define STRUCT_ALIGNMENT (2 * sizeof (gsize))
#define ALIGN_STRUCT(offset) ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT)
-static gpointer
+gpointer
g_rc_box_alloc_full (gsize block_size,
+ gboolean atomic,
gboolean clear)
{
- gsize private_size = G_RC_BOX_SIZE;
+ /* sizeof GArcBox == sizeof GRcBox */
+ gsize private_size = G_ARC_BOX_SIZE;
gsize real_size = private_size + block_size;
char *allocated;
- GRcBox *real_box;
#ifdef ENABLE_VALGRIND
if (RUNNING_ON_VALGRIND)
@@ -189,12 +176,24 @@ g_rc_box_alloc_full (gsize block_size,
allocated = g_malloc (real_size);
}
- real_box = (GRcBox *) allocated;
- real_box->mem_size = block_size;
+ if (atomic)
+ {
+ GArcBox *real_box = (GArcBox *) allocated;
+ real_box->mem_size = block_size;
#ifndef G_DISABLE_ASSERT
- real_box->magic = G_RC_BOX_MAGIC;
+ real_box->magic = G_BOX_MAGIC;
#endif
- g_ref_count_init (&real_box->ref_count);
+ g_atomic_ref_count_init (&real_box->ref_count);
+ }
+ else
+ {
+ GRcBox *real_box = (GRcBox *) allocated;
+ real_box->mem_size = block_size;
+#ifndef G_DISABLE_ASSERT
+ real_box->magic = G_BOX_MAGIC;
+#endif
+ g_ref_count_init (&real_box->ref_count);
+ }
return allocated + private_size;
}
@@ -218,7 +217,7 @@ g_rc_box_alloc (gsize block_size)
{
g_return_val_if_fail (block_size > 0, NULL);
- return g_rc_box_alloc_full (block_size, FALSE);
+ return g_rc_box_alloc_full (block_size, FALSE, FALSE);
}
/**
@@ -242,7 +241,7 @@ g_rc_box_alloc0 (gsize block_size)
{
g_return_val_if_fail (block_size > 0, NULL);
- return g_rc_box_alloc_full (block_size, TRUE);
+ return g_rc_box_alloc_full (block_size, FALSE, TRUE);
}
/**
@@ -305,10 +304,10 @@ gpointer
g_return_val_if_fail (mem_block != NULL, NULL);
#ifndef G_DISABLE_ASSERT
- g_return_val_if_fail (real_box->magic == G_RC_BOX_MAGIC, NULL);
+ g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL);
#endif
- res = g_rc_box_alloc_full (real_box->mem_size, FALSE);
+ res = g_rc_box_alloc_full (real_box->mem_size, FALSE, FALSE);
memcpy (res, mem_block, real_box->mem_size);
return res;
@@ -332,7 +331,7 @@ gpointer
g_return_val_if_fail (mem_block != NULL, NULL);
#ifndef G_DISABLE_ASSERT
- g_return_val_if_fail (real_box->magic == G_RC_BOX_MAGIC, NULL);
+ g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL);
#endif
g_ref_count_inc (&real_box->ref_count);
@@ -358,7 +357,7 @@ g_rc_box_release (gpointer mem_block)
g_return_if_fail (mem_block != NULL);
#ifndef G_DISABLE_ASSERT
- g_return_if_fail (real_box->magic == G_RC_BOX_MAGIC);
+ g_return_if_fail (real_box->magic == G_BOX_MAGIC);
#endif
if (g_ref_count_dec (&real_box->ref_count))
@@ -387,7 +386,7 @@ g_rc_box_release_full (gpointer mem_block,
g_return_if_fail (mem_block != NULL);
g_return_if_fail (clear_func != NULL);
#ifndef G_DISABLE_ASSERT
- g_return_if_fail (real_box->magic == G_RC_BOX_MAGIC);
+ g_return_if_fail (real_box->magic == G_BOX_MAGIC);
#endif
if (g_ref_count_dec (&real_box->ref_count))
diff --git a/glib/grcbox.h b/glib/grcbox.h
index 8cd44037a..2b38815d8 100644
--- a/glib/grcbox.h
+++ b/glib/grcbox.h
@@ -40,16 +40,41 @@ GLIB_AVAILABLE_IN_2_58
void g_rc_box_release_full (gpointer mem_block,
GDestroyNotify clear_func);
+GLIB_AVAILABLE_IN_2_58
+gpointer g_arc_box_alloc (gsize block_size) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1);
+GLIB_AVAILABLE_IN_2_58
+gpointer g_arc_box_alloc0 (gsize block_size) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1);
+GLIB_AVAILABLE_IN_2_58
+gpointer g_arc_box_dup (gpointer mem_block) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1);
+GLIB_AVAILABLE_IN_2_58
+gpointer g_arc_box_acquire (gpointer mem_block);
+GLIB_AVAILABLE_IN_2_58
+void g_arc_box_release (gpointer mem_block);
+GLIB_AVAILABLE_IN_2_58
+void g_arc_box_release_full (gpointer mem_block,
+ GDestroyNotify clear_func);
+
#define g_rc_box_new(type) \
((type *) g_rc_box_alloc (sizeof (type)))
#define g_rc_box_new0(type) \
((type *) g_rc_box_alloc0 (sizeof (type)))
+#define g_arc_box_new(type) \
+ ((type *) g_arc_box_alloc (sizeof (type)))
+#define g_arc_box_new0(type) \
+ ((type *) g_arc_box_alloc0 (sizeof (type)))
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && !defined(_cplusplus)
/* Type check to avoid assigning references to different types */
-# define g_rc_box_acquire(mem_block) ((__typeof__(mem_block)) (g_rc_box_acquire) (mem_block))
+# define g_rc_box_acquire(mem_block) \
+ ((__typeof__(mem_block)) (g_rc_box_acquire) (mem_block))
+# define g_arc_box_acquire(mem_block) \
+ ((__typeof__(mem_block)) (g_arc_box_acquire) (mem_block))
+
/* Type check to avoid duplicating data to different types */
-# define g_rc_box_dup(mem_block) ((__typeof__(mem_block)) (g_rc_box_dup) (mem_block))
+# define g_rc_box_dup(mem_block) \
+ ((__typeof__(mem_block)) (g_rc_box_dup) (mem_block))
+# define g_arc_box_dup(mem_block) \
+ ((__typeof__(mem_block)) (g_arc_box_dup) (mem_block))
#endif
G_END_DECLS
diff --git a/glib/grcboxprivate.h b/glib/grcboxprivate.h
new file mode 100644
index 000000000..6599e4d4a
--- /dev/null
+++ b/glib/grcboxprivate.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "gtypes.h"
+
+G_BEGIN_DECLS
+
+typedef struct {
+ grefcount ref_count;
+
+ gsize mem_size;
+
+#ifndef G_DISABLE_ASSERT
+ /* A "magic" number, used to perform additional integrity
+ * checks on the allocated data
+ */
+ guint32 magic;
+#endif
+} GRcBox;
+
+typedef struct {
+ gatomicrefcount ref_count;
+
+ gsize mem_size;
+
+#ifndef G_DISABLE_ASSERT
+ guint32 magic;
+#endif
+} GArcBox;
+
+#define G_BOX_MAGIC 0x44ae2bf0
+
+/* Keep the two refcounted boxes identical in size */
+G_STATIC_ASSERT (sizeof (GRcBox) == sizeof (GArcBox));
+
+#define G_RC_BOX_SIZE sizeof (GRcBox)
+#define G_ARC_BOX_SIZE sizeof (GArcBox)
+
+gpointer g_rc_box_alloc_full (gsize block_size,
+ gboolean atomic,
+ gboolean clear);
+
+G_END_DECLS
diff --git a/glib/meson.build b/glib/meson.build
index 73ab4928f..1a3f44797 100644
--- a/glib/meson.build
+++ b/glib/meson.build
@@ -119,6 +119,7 @@ deprecated_sources = files(
)
glib_sources = files(
+ 'garcbox.c',
'garray.c',
'gasyncqueue.c',
'gatomic.c',