/src/irssi/subprojects/glib-2.74.3/glib/grcbox.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* grcbox.c: Reference counted data |
2 | | * |
3 | | * Copyright 2018 Emmanuele Bassi |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "config.h" |
22 | | |
23 | | #include "grcboxprivate.h" |
24 | | |
25 | | #include "gmessages.h" |
26 | | #include "grefcount.h" |
27 | | #include "gtestutils.h" |
28 | | |
29 | | #ifdef ENABLE_VALGRIND |
30 | | #include "valgrind.h" |
31 | | #endif |
32 | | |
33 | | #include "glib_trace.h" |
34 | | |
35 | | #include <string.h> |
36 | | |
37 | | /** |
38 | | * SECTION:rcbox |
39 | | * @Title: Reference counted data |
40 | | * @Short_description: Allocated memory with reference counting semantics |
41 | | * |
42 | | * A "reference counted box", or "RcBox", is an opaque wrapper data type |
43 | | * that is guaranteed to be as big as the size of a given data type, and |
44 | | * which augments the given data type with reference counting semantics |
45 | | * for its memory management. |
46 | | * |
47 | | * RcBox is useful if you have a plain old data type, like a structure |
48 | | * typically placed on the stack, and you wish to provide additional API |
49 | | * to use it on the heap; or if you want to implement a new type to be |
50 | | * passed around by reference without necessarily implementing copy/free |
51 | | * semantics or your own reference counting. |
52 | | * |
53 | | * The typical use is: |
54 | | * |
55 | | * |[<!-- language="C" --> |
56 | | * typedef struct { |
57 | | * char *name; |
58 | | * char *address; |
59 | | * char *city; |
60 | | * char *state; |
61 | | * int age; |
62 | | * } Person; |
63 | | * |
64 | | * Person * |
65 | | * person_new (void) |
66 | | * { |
67 | | * return g_rc_box_new0 (Person); |
68 | | * } |
69 | | * ]| |
70 | | * |
71 | | * Every time you wish to acquire a reference on the memory, you should |
72 | | * call g_rc_box_acquire(); similarly, when you wish to release a reference |
73 | | * you should call g_rc_box_release(): |
74 | | * |
75 | | * |[<!-- language="C" --> |
76 | | * // Add a Person to the Database; the Database acquires ownership |
77 | | * // of the Person instance |
78 | | * void |
79 | | * add_person_to_database (Database *db, Person *p) |
80 | | * { |
81 | | * db->persons = g_list_prepend (db->persons, g_rc_box_acquire (p)); |
82 | | * } |
83 | | * |
84 | | * // Removes a Person from the Database; the reference acquired by |
85 | | * // add_person_to_database() is released here |
86 | | * void |
87 | | * remove_person_from_database (Database *db, Person *p) |
88 | | * { |
89 | | * db->persons = g_list_remove (db->persons, p); |
90 | | * g_rc_box_release (p); |
91 | | * } |
92 | | * ]| |
93 | | * |
94 | | * If you have additional memory allocated inside the structure, you can |
95 | | * use g_rc_box_release_full(), which takes a function pointer, which |
96 | | * will be called if the reference released was the last: |
97 | | * |
98 | | * |[<!-- language="C" --> |
99 | | * void |
100 | | * person_clear (Person *p) |
101 | | * { |
102 | | * g_free (p->name); |
103 | | * g_free (p->address); |
104 | | * g_free (p->city); |
105 | | * g_free (p->state); |
106 | | * } |
107 | | * |
108 | | * void |
109 | | * remove_person_from_database (Database *db, Person *p) |
110 | | * { |
111 | | * db->persons = g_list_remove (db->persons, p); |
112 | | * g_rc_box_release_full (p, (GDestroyNotify) person_clear); |
113 | | * } |
114 | | * ]| |
115 | | * |
116 | | * If you wish to transfer the ownership of a reference counted data |
117 | | * type without increasing the reference count, you can use g_steal_pointer(): |
118 | | * |
119 | | * |[<!-- language="C" --> |
120 | | * Person *p = g_rc_box_new (Person); |
121 | | * |
122 | | * // fill_person_details() is defined elsewhere |
123 | | * fill_person_details (p); |
124 | | * |
125 | | * // add_person_to_database_no_ref() is defined elsewhere; it adds |
126 | | * // a Person to the Database without taking a reference |
127 | | * add_person_to_database_no_ref (db, g_steal_pointer (&p)); |
128 | | * ]| |
129 | | * |
130 | | * ## Thread safety |
131 | | * |
132 | | * The reference counting operations on data allocated using g_rc_box_alloc(), |
133 | | * g_rc_box_new(), and g_rc_box_dup() are not thread safe; it is your code's |
134 | | * responsibility to ensure that references are acquired are released on the |
135 | | * same thread. |
136 | | * |
137 | | * If you need thread safe reference counting, see the [atomic reference counted |
138 | | * data][arcbox] API. |
139 | | * |
140 | | * ## Automatic pointer clean up |
141 | | * |
142 | | * If you want to add g_autoptr() support to your plain old data type through |
143 | | * reference counting, you can use the G_DEFINE_AUTOPTR_CLEANUP_FUNC() and |
144 | | * g_rc_box_release(): |
145 | | * |
146 | | * |[<!-- language="C" --> |
147 | | * G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, g_rc_box_release) |
148 | | * ]| |
149 | | * |
150 | | * If you need to clear the contents of the data, you will need to use an |
151 | | * ancillary function that calls g_rc_box_release_full(): |
152 | | * |
153 | | * |[<!-- language="C" --> |
154 | | * static void |
155 | | * my_data_struct_release (MyDataStruct *data) |
156 | | * { |
157 | | * // my_data_struct_clear() is defined elsewhere |
158 | | * g_rc_box_release_full (data, (GDestroyNotify) my_data_struct_clear); |
159 | | * } |
160 | | * |
161 | | * G_DEFINE_AUTOPTR_CLEANUP_FUNC (MyDataStruct, my_data_struct_release) |
162 | | * ]| |
163 | | * |
164 | | * Since: 2.58 |
165 | | */ |
166 | | |
167 | | /* We use the same alignment as GTypeInstance and GNU libc's malloc */ |
168 | | #define ALIGN_STRUCT(offset) ((offset + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT) |
169 | | |
170 | 0 | #define G_RC_BOX(p) (GRcBox *) (((char *) (p)) - G_RC_BOX_SIZE) |
171 | | |
172 | | gpointer |
173 | | g_rc_box_alloc_full (gsize block_size, |
174 | | gsize alignment, |
175 | | gboolean atomic, |
176 | | gboolean clear) |
177 | 0 | { |
178 | | /* We don't do an (atomic ? G_ARC_BOX_SIZE : G_RC_BOX_SIZE) check, here |
179 | | * because we have a static assertion that sizeof(GArcBox) == sizeof(GRcBox) |
180 | | * inside grcboxprivate.h, and we don't want the compiler to unnecessarily |
181 | | * warn about both branches of the conditional yielding identical results |
182 | | */ |
183 | 0 | gsize private_size = G_ARC_BOX_SIZE; |
184 | 0 | gsize private_offset = 0; |
185 | 0 | gsize real_size; |
186 | 0 | char *allocated; |
187 | |
|
188 | 0 | g_assert (alignment != 0); |
189 | | |
190 | | /* We need to ensure that the private data is aligned */ |
191 | 0 | if (private_size % alignment != 0) |
192 | 0 | { |
193 | 0 | private_offset = private_size % alignment; |
194 | 0 | private_size += (alignment - private_offset); |
195 | 0 | } |
196 | |
|
197 | 0 | g_assert (block_size < (G_MAXSIZE - private_size)); |
198 | 0 | real_size = private_size + block_size; |
199 | | |
200 | | /* The real allocated size must be a multiple of @alignment, to |
201 | | * maintain the alignment of block_size |
202 | | */ |
203 | 0 | if (real_size % alignment != 0) |
204 | 0 | { |
205 | 0 | gsize offset = real_size % alignment; |
206 | 0 | g_assert (real_size < (G_MAXSIZE - (alignment - offset))); |
207 | 0 | real_size += (alignment - offset); |
208 | 0 | } |
209 | | |
210 | | #ifdef ENABLE_VALGRIND |
211 | | if (RUNNING_ON_VALGRIND) |
212 | | { |
213 | | /* When running under Valgrind we massage the memory allocation |
214 | | * to include a pointer at the tail end of the block; the pointer |
215 | | * is then set to the start of the block. This trick allows |
216 | | * Valgrind to keep track of the over-allocation and not be |
217 | | * confused when passing the pointer around |
218 | | */ |
219 | | g_assert (private_size < (G_MAXSIZE - ALIGN_STRUCT (1))); |
220 | | private_size += ALIGN_STRUCT (1); |
221 | | |
222 | | if (clear) |
223 | | allocated = g_malloc0 (real_size + sizeof (gpointer)); |
224 | | else |
225 | | allocated = g_malloc (real_size + sizeof (gpointer)); |
226 | | |
227 | | *(gpointer *) (allocated + private_size + block_size) = allocated + ALIGN_STRUCT (1); |
228 | | |
229 | | VALGRIND_MALLOCLIKE_BLOCK (allocated + private_size, block_size + sizeof (gpointer), 0, TRUE); |
230 | | VALGRIND_MALLOCLIKE_BLOCK (allocated + ALIGN_STRUCT (1), private_size - ALIGN_STRUCT (1), 0, TRUE); |
231 | | } |
232 | | else |
233 | | #endif /* ENABLE_VALGRIND */ |
234 | 0 | { |
235 | 0 | if (clear) |
236 | 0 | allocated = g_malloc0 (real_size); |
237 | 0 | else |
238 | 0 | allocated = g_malloc (real_size); |
239 | 0 | } |
240 | |
|
241 | 0 | if (atomic) |
242 | 0 | { |
243 | | /* We leave the alignment padding at the top of the allocation, |
244 | | * so we have an in memory layout of: |
245 | | * |
246 | | * |[ offset ][ sizeof(GArcBox) ]||[ block_size ]| |
247 | | */ |
248 | 0 | GArcBox *real_box = (GArcBox *) (allocated + private_offset); |
249 | | /* Store the real size */ |
250 | 0 | real_box->mem_size = block_size; |
251 | | /* Store the alignment offset, to be used when freeing the |
252 | | * allocated block |
253 | | */ |
254 | 0 | real_box->private_offset = private_offset; |
255 | 0 | #ifndef G_DISABLE_ASSERT |
256 | 0 | real_box->magic = G_BOX_MAGIC; |
257 | 0 | #endif |
258 | 0 | g_atomic_ref_count_init (&real_box->ref_count); |
259 | 0 | } |
260 | 0 | else |
261 | 0 | { |
262 | | /* We leave the alignment padding at the top of the allocation, |
263 | | * so we have an in memory layout of: |
264 | | * |
265 | | * |[ offset ][ sizeof(GRcBox) ]||[ block_size ]| |
266 | | */ |
267 | 0 | GRcBox *real_box = (GRcBox *) (allocated + private_offset); |
268 | | /* Store the real size */ |
269 | 0 | real_box->mem_size = block_size; |
270 | | /* Store the alignment offset, to be used when freeing the |
271 | | * allocated block |
272 | | */ |
273 | 0 | real_box->private_offset = private_offset; |
274 | 0 | #ifndef G_DISABLE_ASSERT |
275 | 0 | real_box->magic = G_BOX_MAGIC; |
276 | 0 | #endif |
277 | 0 | g_ref_count_init (&real_box->ref_count); |
278 | 0 | } |
279 | |
|
280 | 0 | TRACE (GLIB_RCBOX_ALLOC (allocated, block_size, atomic, clear)); |
281 | |
|
282 | 0 | return allocated + private_size; |
283 | 0 | } |
284 | | |
285 | | /** |
286 | | * g_rc_box_alloc: |
287 | | * @block_size: the size of the allocation, must be greater than 0 |
288 | | * |
289 | | * Allocates @block_size bytes of memory, and adds reference |
290 | | * counting semantics to it. |
291 | | * |
292 | | * The data will be freed when its reference count drops to |
293 | | * zero. |
294 | | * |
295 | | * The allocated data is guaranteed to be suitably aligned for any |
296 | | * built-in type. |
297 | | * |
298 | | * Returns: (transfer full) (not nullable): a pointer to the allocated memory |
299 | | * |
300 | | * Since: 2.58 |
301 | | */ |
302 | | gpointer |
303 | | g_rc_box_alloc (gsize block_size) |
304 | 0 | { |
305 | 0 | g_return_val_if_fail (block_size > 0, NULL); |
306 | | |
307 | 0 | return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, FALSE, FALSE); |
308 | 0 | } |
309 | | |
310 | | /** |
311 | | * g_rc_box_alloc0: |
312 | | * @block_size: the size of the allocation, must be greater than 0 |
313 | | * |
314 | | * Allocates @block_size bytes of memory, and adds reference |
315 | | * counting semantics to it. |
316 | | * |
317 | | * The contents of the returned data is set to zero. |
318 | | * |
319 | | * The data will be freed when its reference count drops to |
320 | | * zero. |
321 | | * |
322 | | * The allocated data is guaranteed to be suitably aligned for any |
323 | | * built-in type. |
324 | | * |
325 | | * Returns: (transfer full) (not nullable): a pointer to the allocated memory |
326 | | * |
327 | | * Since: 2.58 |
328 | | */ |
329 | | gpointer |
330 | | g_rc_box_alloc0 (gsize block_size) |
331 | 0 | { |
332 | 0 | g_return_val_if_fail (block_size > 0, NULL); |
333 | | |
334 | 0 | return g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, FALSE, TRUE); |
335 | 0 | } |
336 | | |
337 | | /** |
338 | | * g_rc_box_new: |
339 | | * @type: the type to allocate, typically a structure name |
340 | | * |
341 | | * A convenience macro to allocate reference counted data with |
342 | | * the size of the given @type. |
343 | | * |
344 | | * This macro calls g_rc_box_alloc() with `sizeof (@type)` and |
345 | | * casts the returned pointer to a pointer of the given @type, |
346 | | * avoiding a type cast in the source code. |
347 | | * |
348 | | * Returns: (transfer full) (not nullable): a pointer to the |
349 | | * allocated memory, cast to a pointer for the given @type |
350 | | * |
351 | | * Since: 2.58 |
352 | | */ |
353 | | |
354 | | /** |
355 | | * g_rc_box_new0: |
356 | | * @type: the type to allocate, typically a structure name |
357 | | * |
358 | | * A convenience macro to allocate reference counted data with |
359 | | * the size of the given @type, and set its contents to zero. |
360 | | * |
361 | | * This macro calls g_rc_box_alloc0() with `sizeof (@type)` and |
362 | | * casts the returned pointer to a pointer of the given @type, |
363 | | * avoiding a type cast in the source code. |
364 | | * |
365 | | * Returns: (transfer full) (not nullable): a pointer to the |
366 | | * allocated memory, cast to a pointer for the given @type |
367 | | * |
368 | | * Since: 2.58 |
369 | | */ |
370 | | |
371 | | /** |
372 | | * g_rc_box_dup: |
373 | | * @block_size: the number of bytes to copy, must be greater than 0 |
374 | | * @mem_block: (not nullable): the memory to copy |
375 | | * |
376 | | * Allocates a new block of data with reference counting |
377 | | * semantics, and copies @block_size bytes of @mem_block |
378 | | * into it. |
379 | | * |
380 | | * Returns: (transfer full) (not nullable): a pointer to the allocated |
381 | | * memory |
382 | | * |
383 | | * Since: 2.58 |
384 | | */ |
385 | | gpointer |
386 | | (g_rc_box_dup) (gsize block_size, |
387 | | gconstpointer mem_block) |
388 | 0 | { |
389 | 0 | gpointer res; |
390 | |
|
391 | 0 | g_return_val_if_fail (block_size > 0, NULL); |
392 | 0 | g_return_val_if_fail (mem_block != NULL, NULL); |
393 | | |
394 | 0 | res = g_rc_box_alloc_full (block_size, STRUCT_ALIGNMENT, FALSE, FALSE); |
395 | 0 | memcpy (res, mem_block, block_size); |
396 | |
|
397 | 0 | return res; |
398 | 0 | } |
399 | | |
400 | | /** |
401 | | * g_rc_box_acquire: |
402 | | * @mem_block: (not nullable): a pointer to reference counted data |
403 | | * |
404 | | * Acquires a reference on the data pointed by @mem_block. |
405 | | * |
406 | | * Returns: (transfer full) (not nullable): a pointer to the data, |
407 | | * with its reference count increased |
408 | | * |
409 | | * Since: 2.58 |
410 | | */ |
411 | | gpointer |
412 | | (g_rc_box_acquire) (gpointer mem_block) |
413 | 0 | { |
414 | 0 | GRcBox *real_box = G_RC_BOX (mem_block); |
415 | |
|
416 | 0 | g_return_val_if_fail (mem_block != NULL, NULL); |
417 | 0 | #ifndef G_DISABLE_ASSERT |
418 | 0 | g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, NULL); |
419 | 0 | #endif |
420 | | |
421 | 0 | g_ref_count_inc (&real_box->ref_count); |
422 | |
|
423 | 0 | TRACE (GLIB_RCBOX_ACQUIRE (mem_block, 0)); |
424 | |
|
425 | 0 | return mem_block; |
426 | 0 | } |
427 | | |
428 | | /** |
429 | | * g_rc_box_release: |
430 | | * @mem_block: (transfer full) (not nullable): a pointer to reference counted data |
431 | | * |
432 | | * Releases a reference on the data pointed by @mem_block. |
433 | | * |
434 | | * If the reference was the last one, it will free the |
435 | | * resources allocated for @mem_block. |
436 | | * |
437 | | * Since: 2.58 |
438 | | */ |
439 | | void |
440 | | g_rc_box_release (gpointer mem_block) |
441 | 0 | { |
442 | 0 | g_rc_box_release_full (mem_block, NULL); |
443 | 0 | } |
444 | | |
445 | | /** |
446 | | * g_rc_box_release_full: |
447 | | * @mem_block: (transfer full) (not nullable): a pointer to reference counted data |
448 | | * @clear_func: (not nullable): a function to call when clearing the data |
449 | | * |
450 | | * Releases a reference on the data pointed by @mem_block. |
451 | | * |
452 | | * If the reference was the last one, it will call @clear_func |
453 | | * to clear the contents of @mem_block, and then will free the |
454 | | * resources allocated for @mem_block. |
455 | | * |
456 | | * Since: 2.58 |
457 | | */ |
458 | | void |
459 | | g_rc_box_release_full (gpointer mem_block, |
460 | | GDestroyNotify clear_func) |
461 | 0 | { |
462 | 0 | GRcBox *real_box = G_RC_BOX (mem_block); |
463 | |
|
464 | 0 | g_return_if_fail (mem_block != NULL); |
465 | 0 | #ifndef G_DISABLE_ASSERT |
466 | 0 | g_return_if_fail (real_box->magic == G_BOX_MAGIC); |
467 | 0 | #endif |
468 | | |
469 | 0 | if (g_ref_count_dec (&real_box->ref_count)) |
470 | 0 | { |
471 | 0 | char *real_mem = (char *) real_box - real_box->private_offset; |
472 | |
|
473 | 0 | TRACE (GLIB_RCBOX_RELEASE (mem_block, 0)); |
474 | |
|
475 | 0 | if (clear_func != NULL) |
476 | 0 | clear_func (mem_block); |
477 | |
|
478 | 0 | TRACE (GLIB_RCBOX_FREE (mem_block)); |
479 | 0 | g_free (real_mem); |
480 | 0 | } |
481 | 0 | } |
482 | | |
483 | | /** |
484 | | * g_rc_box_get_size: |
485 | | * @mem_block: (not nullable): a pointer to reference counted data |
486 | | * |
487 | | * Retrieves the size of the reference counted data pointed by @mem_block. |
488 | | * |
489 | | * Returns: the size of the data, in bytes |
490 | | * |
491 | | * Since: 2.58 |
492 | | */ |
493 | | gsize |
494 | | g_rc_box_get_size (gpointer mem_block) |
495 | 0 | { |
496 | 0 | GRcBox *real_box = G_RC_BOX (mem_block); |
497 | |
|
498 | 0 | g_return_val_if_fail (mem_block != NULL, 0); |
499 | 0 | #ifndef G_DISABLE_ASSERT |
500 | 0 | g_return_val_if_fail (real_box->magic == G_BOX_MAGIC, 0); |
501 | 0 | #endif |
502 | | |
503 | 0 | return real_box->mem_size; |
504 | 0 | } |