• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* garcbox.c: Atomically reference counted data
2  *
3  * Copyright 2018  Emmanuele Bassi
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include "grcboxprivate.h"
22 
23 #include "gmessages.h"
24 #include "grefcount.h"
25 
26 #ifdef ENABLE_VALGRIND
27 #include "valgrind.h"
28 #endif
29 
30 #include "glib_trace.h"
31 
32 #include <string.h>
33 
34 #define G_ARC_BOX(p)            (GArcBox *) (((char *) (p)) - G_ARC_BOX_SIZE)
35 
36 /**
37  * SECTION:arcbox
38  * @Title: Atomically reference counted data
39  * @Short_description: Allocated memory with atomic reference counting semantics
40  *
41  * An "atomically reference counted box", or "ArcBox", is an opaque wrapper
42  * data type that is guaranteed to be as big as the size of a given data type,
43  * and which augments the given data type with thread safe reference counting
44  * semantics for its memory management.
45  *
46  * ArcBox is useful if you have a plain old data type, like a structure
47  * typically placed on the stack, and you wish to provide additional API
48  * to use it on the heap; or if you want to implement a new type to be
49  * passed around by reference without necessarily implementing copy/free
50  * semantics or your own reference counting.
51  *
52  * The typical use is:
53  *
54  * |[<!-- language="C" -->
55  * typedef struct {
56  *   char *name;
57  *   char *address;
58  *   char *city;
59  *   char *state;
60  *   int age;
61  * } Person;
62  *
63  * Person *
64  * person_new (void)
65  * {
66  *   return g_atomic_rc_box_new0 (Person);
67  * }
68  * ]|
69  *
70  * Every time you wish to acquire a reference on the memory, you should
71  * call g_atomic_rc_box_acquire(); similarly, when you wish to release a reference
72  * you should call g_atomic_rc_box_release():
73  *
74  * |[<!-- language="C" -->
75  * // Add a Person to the Database; the Database acquires ownership
76  * // of the Person instance
77  * void
78  * add_person_to_database (Database *db, Person *p)
79  * {
80  *   db->persons = g_list_prepend (db->persons, g_atomic_rc_box_acquire (p));
81  * }
82  *
83  * // Removes a Person from the Database; the reference acquired by
84  * // add_person_to_database() is released here
85  * void
86  * remove_person_from_database (Database *db, Person *p)
87  * {
88  *   db->persons = g_list_remove (db->persons, p);
89  *   g_atomic_rc_box_release (p);
90  * }
91  * ]|
92  *
93  * If you have additional memory allocated inside the structure, you can
94  * use g_atomic_rc_box_release_full(), which takes a function pointer, which
95  * will be called if the reference released was the last:
96  *
97  * |[<!-- language="C" -->
98  * void
99  * person_clear (Person *p)
100  * {
101  *   g_free (p->name);
102  *   g_free (p->address);
103  *   g_free (p->city);
104  *   g_free (p->state);
105  * }
106  *
107  * void
108  * remove_person_from_database (Database *db, Person *p)
109  * {
110  *   db->persons = g_list_remove (db->persons, p);
111  *   g_atomic_rc_box_release_full (p, (GDestroyNotify) person_clear);
112  * }
113  * ]|
114  *
115  * If you wish to transfer the ownership of a reference counted data
116  * type without increasing the reference count, you can use g_steal_pointer():
117  *
118  * |[<!-- language="C" -->
119  *   Person *p = g_atomic_rc_box_new (Person);
120  *
121  *   fill_person_details (p);
122  *
123  *   add_person_to_database (db, g_steal_pointer (&p));
124  * ]|
125  *
126  * ## Thread safety
127  *
128  * The reference counting operations on data allocated using g_atomic_rc_box_alloc(),
129  * g_atomic_rc_box_new(), and g_atomic_rc_box_dup() are guaranteed to be atomic, and thus
130  * can be safely be performed by different threads. It is important to note that
131  * only the reference acquisition and release are atomic; changes to the content
132  * of the data are your responsibility.
133  *
134  * ## Automatic pointer clean up
135  *
136  * If you want to add g_autoptr() support to your plain old data type through
137  * reference counting, you can use the G_DEFINE_AUTOPTR_CLEANUP_FUNC() and
138  * g_atomic_rc_box_release():
139  *
140  * |[<!-- language="C" -->
141  * G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, g_atomic_rc_box_release)
142  * ]|
143  *
144  * If you need to clear the contents of the data, you will need to use an
145  * ancillary function that calls g_rc_box_release_full():
146  *
147  * |[<!-- language="C" -->
148  * static void
149  * my_data_struct_release (MyDataStruct *data)
150  * {
151  *   // my_data_struct_clear() is defined elsewhere
152  *   g_atomic_rc_box_release_full (data, (GDestroyNotify) my_data_struct_clear);
153  * }
154  *
155  * G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, my_data_struct_release)
156  * ]|
157  *
158  * Since: 2.58
159  */
160 
161 /**
162  * g_atomic_rc_box_alloc:
163  * @block_size: the size of the allocation, must be greater than 0
164  *
165  * Allocates @block_size bytes of memory, and adds atomic
166  * reference counting semantics to it.
167  *
168  * The data will be freed when its reference count drops to
169  * zero.
170  *
171  * The allocated data is guaranteed to be suitably aligned for any
172  * built-in type.
173  *
174  * Returns: (transfer full) (not nullable): a pointer to the allocated memory
175  *
176  * Since: 2.58
177  */
178 gpointer
g_atomic_rc_box_alloc(gsize block_size)179 g_atomic_rc_box_alloc (gsize block_size)
180 {
181   g_return_val_if_fail (block_size > 0, NULL);
182 
183   return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, FALSE);
184 }
185 
186 /**
187  * g_atomic_rc_box_alloc0:
188  * @block_size: the size of the allocation, must be greater than 0
189  *
190  * Allocates @block_size bytes of memory, and adds atomic
191  * reference counting semantics to it.
192  *
193  * The contents of the returned data is set to zero.
194  *
195  * The data will be freed when its reference count drops to
196  * zero.
197  *
198  * The allocated data is guaranteed to be suitably aligned for any
199  * built-in type.
200  *
201  * Returns: (transfer full) (not nullable): a pointer to the allocated memory
202  *
203  * Since: 2.58
204  */
205 gpointer
g_atomic_rc_box_alloc0(gsize block_size)206 g_atomic_rc_box_alloc0 (gsize block_size)
207 {
208   g_return_val_if_fail (block_size > 0, NULL);
209 
210   return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, TRUE);
211 }
212 
213 /**
214  * g_atomic_rc_box_new:
215  * @type: the type to allocate, typically a structure name
216  *
217  * A convenience macro to allocate atomically reference counted
218  * data with the size of the given @type.
219  *
220  * This macro calls g_atomic_rc_box_alloc() with `sizeof (@type)` and
221  * casts the returned pointer to a pointer of the given @type,
222  * avoiding a type cast in the source code.
223  *
224  * Returns: (transfer full) (not nullable): a pointer to the allocated
225  *   memory, cast to a pointer for the given @type
226  *
227  * Since: 2.58
228  */
229 
230 /**
231  * g_atomic_rc_box_new0:
232  * @type: the type to allocate, typically a structure name
233  *
234  * A convenience macro to allocate atomically reference counted
235  * data with the size of the given @type, and set its contents
236  * to zero.
237  *
238  * This macro calls g_atomic_rc_box_alloc0() with `sizeof (@type)` and
239  * casts the returned pointer to a pointer of the given @type,
240  * avoiding a type cast in the source code.
241  *
242  * Returns: (transfer full) (not nullable): a pointer to the allocated
243  *   memory, cast to a pointer for the given @type
244  *
245  * Since: 2.58
246  */
247 
248 /**
249  * g_atomic_rc_box_dup:
250  * @block_size: the number of bytes to copy, must be greater than 0
251  * @mem_block: (not nullable): the memory to copy
252  *
253  * Allocates a new block of data with atomic reference counting
254  * semantics, and copies @block_size bytes of @mem_block
255  * into it.
256  *
257  * Returns: (transfer full) (not nullable): a pointer to the allocated
258  *   memory
259  *
260  * Since: 2.58
261  */
gpointer(g_atomic_rc_box_dup)262 gpointer
263 (g_atomic_rc_box_dup) (gsize         block_size,
264                        gconstpointer mem_block)
265 {
266   gpointer res;
267 
268   g_return_val_if_fail (block_size > 0, NULL);
269   g_return_val_if_fail (mem_block != NULL, NULL);
270 
271   res = g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, TRUE, FALSE);
272   memcpy (res, mem_block, block_size);
273 
274   return res;
275 }
276 
277 /**
278  * g_atomic_rc_box_acquire:
279  * @mem_block: (not nullable): a pointer to reference counted data
280  *
281  * Atomically acquires a reference on the data pointed by @mem_block.
282  *
283  * Returns: (transfer full) (not nullable): a pointer to the data,
284  *   with its reference count increased
285  *
286  * Since: 2.58
287  */
gpointer(g_atomic_rc_box_acquire)288 gpointer
289 (g_atomic_rc_box_acquire) (gpointer mem_block)
290 {
291   GArcBox *real_box = G_ARC_BOX (mem_block);
292 
293   g_return_val_if_fail (mem_block != NULL, NULL);
294 #ifndef G_DISABLE_ASSERT
295   g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL);
296 #endif
297 
298   g_atomic_ref_count_inc (&real_box->ref_count);
299 
300   TRACE (GLIB_RCBOX_ACQUIRE (mem_block, 1));
301 
302   return mem_block;
303 }
304 
305 /**
306  * g_atomic_rc_box_release:
307  * @mem_block: (transfer full) (not nullable): a pointer to reference counted data
308  *
309  * Atomically releases a reference on the data pointed by @mem_block.
310  *
311  * If the reference was the last one, it will free the
312  * resources allocated for @mem_block.
313  *
314  * Since: 2.58
315  */
316 void
g_atomic_rc_box_release(gpointer mem_block)317 g_atomic_rc_box_release (gpointer mem_block)
318 {
319   g_atomic_rc_box_release_full (mem_block, NULL);
320 }
321 
322 /**
323  * g_atomic_rc_box_release_full:
324  * @mem_block: (transfer full) (not nullable): a pointer to reference counted data
325  * @clear_func: (not nullable): a function to call when clearing the data
326  *
327  * Atomically releases a reference on the data pointed by @mem_block.
328  *
329  * If the reference was the last one, it will call @clear_func
330  * to clear the contents of @mem_block, and then will free the
331  * resources allocated for @mem_block.
332  *
333  * Since: 2.58
334  */
335 void
g_atomic_rc_box_release_full(gpointer mem_block,GDestroyNotify clear_func)336 g_atomic_rc_box_release_full (gpointer       mem_block,
337                               GDestroyNotify clear_func)
338 {
339   GArcBox *real_box = G_ARC_BOX (mem_block);
340 
341   g_return_if_fail (mem_block != NULL);
342 #ifndef G_DISABLE_ASSERT
343   g_return_if_fail (real_box->magic == G_BOX_MAGIC);
344 #endif
345 
346   if (g_atomic_ref_count_dec (&real_box->ref_count))
347     {
348       char *real_mem = (char *) real_box - real_box->private_offset;
349 
350       TRACE (GLIB_RCBOX_RELEASE (mem_block, 1));
351 
352       if (clear_func != NULL)
353         clear_func (mem_block);
354 
355       TRACE (GLIB_RCBOX_FREE (mem_block));
356       g_free (real_mem);
357     }
358 }
359 
360 /**
361  * g_atomic_rc_box_get_size:
362  * @mem_block: (not nullable): a pointer to reference counted data
363  *
364  * Retrieves the size of the reference counted data pointed by @mem_block.
365  *
366  * Returns: the size of the data, in bytes
367  *
368  * Since: 2.58
369  */
370 gsize
g_atomic_rc_box_get_size(gpointer mem_block)371 g_atomic_rc_box_get_size (gpointer mem_block)
372 {
373   GArcBox *real_box = G_ARC_BOX (mem_block);
374 
375   g_return_val_if_fail (mem_block != NULL, 0);
376 #ifndef G_DISABLE_ASSERT
377   g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, 0);
378 #endif
379 
380   return real_box->mem_size;
381 }
382