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',