1/* rcbox.c: 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 <glib.h>
20
21typedef struct {
22 float x, y;
23} Point;
24
25static Point *global_point;
26
27/* test_rcbox_new: Test g_rc_box_new() */
28static void
29test_rcbox_new (void)
30{
31 Point *a = g_rc_box_new (Point);
32
33 g_assert_nonnull (a);
34 g_assert_cmpuint (g_rc_box_get_size (a), ==, sizeof (Point));
35
36 g_rc_box_release (mem_block: a);
37
38 a = g_rc_box_new0 (Point);
39 g_assert_nonnull (a);
40 g_assert_cmpfloat (a->x, ==, 0.f);
41 g_assert_cmpfloat (a->y, ==, 0.f);
42
43 g_rc_box_release (mem_block: a);
44}
45
46/* test_atomic_rcbox_new: Test g_atomic_rc_box_new() */
47static void
48test_atomic_rcbox_new (void)
49{
50 Point *a = g_atomic_rc_box_new (Point);
51
52 g_assert_nonnull (a);
53 g_assert_cmpuint (g_atomic_rc_box_get_size (a), ==, sizeof (Point));
54
55 g_atomic_rc_box_release (mem_block: a);
56
57 a = g_atomic_rc_box_new0 (Point);
58 g_assert_nonnull (a);
59 g_assert_cmpfloat (a->x, ==, 0.f);
60 g_assert_cmpfloat (a->y, ==, 0.f);
61
62 g_atomic_rc_box_release (mem_block: a);
63}
64
65static void
66point_clear (Point *p)
67{
68 g_assert_nonnull (p);
69 g_assert_true (global_point == p);
70
71 g_assert_cmpfloat (p->x, ==, 42.0f);
72 g_assert_cmpfloat (p->y, ==, 47.0f);
73
74 g_test_message (format: "global_point = %p", p);
75 global_point = NULL;
76}
77
78/* test_rcbox_release_full: Verify that g_rc_box_release_full() calls
79 * the clear function only when the last reference is released
80 */
81static void
82test_rcbox_release_full (void)
83{
84 Point *p = g_rc_box_new (Point);
85
86 g_assert_nonnull (p);
87 global_point = p;
88
89 p->x = 42.0f;
90 p->y = 47.0f;
91
92 g_assert_true (g_rc_box_acquire (p) == p);
93
94 g_rc_box_release_full (mem_block: p, clear_func: (GDestroyNotify) point_clear);
95 g_assert_nonnull (global_point);
96 g_assert_true (p == global_point);
97
98 g_rc_box_release_full (mem_block: p, clear_func: (GDestroyNotify) point_clear);
99 g_assert_null (global_point);
100}
101
102/* test_atomic_rcbox_release_full: Verify that g_atomic_rc_box_release_full()
103 * calls the clear function only when the last reference is released
104 */
105static void
106test_atomic_rcbox_release_full (void)
107{
108 Point *p = g_atomic_rc_box_new (Point);
109
110 g_assert_nonnull (p);
111 global_point = p;
112
113 p->x = 42.0f;
114 p->y = 47.0f;
115
116 g_assert_true (g_atomic_rc_box_acquire (p) == p);
117
118 g_atomic_rc_box_release_full (mem_block: p, clear_func: (GDestroyNotify) point_clear);
119 g_assert_nonnull (global_point);
120 g_assert_true (p == global_point);
121
122 g_atomic_rc_box_release_full (mem_block: p, clear_func: (GDestroyNotify) point_clear);
123 g_assert_null (global_point);
124}
125
126static Point *global_point_a;
127static Point *global_point_b;
128
129static void
130point_clear_dup_a (Point *a)
131{
132 g_assert_true (a == global_point_a);
133
134 g_test_message (format: "global_point_a = %p", a);
135 global_point_a = NULL;
136}
137
138static void
139point_clear_dup_b (Point *b)
140{
141 g_assert_true (b == global_point_b);
142
143 g_test_message (format: "global_point_b = %p", b);
144 global_point_b = NULL;
145}
146
147/* test_rcbox_dup: Verify that g_rc_box_dup() copies only the
148 * data and does not change the reference count of the original
149 */
150static void
151test_rcbox_dup (void)
152{
153 Point *a, *b;
154
155 a = g_rc_box_new (Point);
156 a->x = 10.f;
157 a->y = 5.f;
158
159 b = g_rc_box_dup (sizeof (Point), a);
160 g_assert_true (a != b);
161 g_assert_cmpfloat (a->x, ==, b->x);
162 g_assert_cmpfloat (a->y, ==, b->y);
163
164 global_point_a = a;
165 global_point_b = b;
166
167 a->x = 1.f;
168 a->y = 1.f;
169 g_assert_cmpfloat (a->x, !=, b->x);
170 g_assert_cmpfloat (a->y, !=, b->y);
171
172 b->x = 5.f;
173 b->y = 10.f;
174 g_assert_cmpfloat (a->x, !=, b->x);
175 g_assert_cmpfloat (a->y, !=, b->y);
176
177 g_rc_box_release_full (mem_block: a, clear_func: (GDestroyNotify) point_clear_dup_a);
178 g_assert_null (global_point_a);
179 g_assert_nonnull (global_point_b);
180
181 g_rc_box_release_full (mem_block: b, clear_func: (GDestroyNotify) point_clear_dup_b);
182 g_assert_null (global_point_b);
183}
184
185/* test_atomic_rcbox_dup: Verify that g_atomic_rc_box_dup() copies
186 * only the data and does not change the reference count of the original
187 */
188static void
189test_atomic_rcbox_dup (void)
190{
191 Point *a, *b;
192
193 a = g_atomic_rc_box_new (Point);
194 a->x = 10.f;
195 a->y = 5.f;
196
197 b = g_atomic_rc_box_dup (sizeof (Point), a);
198 g_assert_true (a != b);
199 g_assert_cmpfloat (a->x, ==, b->x);
200 g_assert_cmpfloat (a->y, ==, b->y);
201
202 global_point_a = a;
203 global_point_b = b;
204
205 a->x = 1.f;
206 a->y = 1.f;
207 g_assert_cmpfloat (a->x, !=, b->x);
208 g_assert_cmpfloat (a->y, !=, b->y);
209
210 b->x = 5.f;
211 b->y = 10.f;
212 g_assert_cmpfloat (a->x, !=, b->x);
213 g_assert_cmpfloat (a->y, !=, b->y);
214
215 g_atomic_rc_box_release_full (mem_block: a, clear_func: (GDestroyNotify) point_clear_dup_a);
216 g_assert_null (global_point_a);
217 g_assert_nonnull (global_point_b);
218
219 g_atomic_rc_box_release_full (mem_block: b, clear_func: (GDestroyNotify) point_clear_dup_b);
220 g_assert_null (global_point_b);
221}
222
223/* The expected alignment of the refcounted data, absent any other
224 * alignment requirement, is `2 * sizeof(void*)`; GLib only really
225 * supports void* sized 8 or 4 (see the comment in gatomic.h)
226 */
227#if GLIB_SIZEOF_VOID_P == 8
228static const gsize rcbox_alignment = 16;
229#else
230static const gsize rcbox_alignment = 8;
231#endif
232
233/* verify that the refcounted allocation is properly aligned */
234static void
235test_rcbox_alignment (void)
236{
237 const gsize block_sizes[] = {
238 1,
239 2,
240 4,
241 sizeof (gint32) * 3,
242 };
243
244 gsize i;
245
246 for (i = 0; i < G_N_ELEMENTS (block_sizes); i++)
247 {
248 gpointer p = g_rc_box_alloc0 (block_size: block_sizes[i]);
249
250 g_assert_nonnull (p);
251 g_assert_true (((guintptr) p & (rcbox_alignment - 1)) == 0);
252
253 g_rc_box_release (mem_block: p);
254 }
255}
256
257/* verify that the atomically refcounted allocation is properly aligned */
258static void
259test_atomic_rcbox_alignment (void)
260{
261 const gsize block_sizes[] = {
262 1,
263 2,
264 4,
265 sizeof (gint32) * 3,
266 };
267
268 gsize i;
269
270 for (i = 0; i < G_N_ELEMENTS (block_sizes); i++)
271 {
272 gpointer p = g_atomic_rc_box_alloc0 (block_size: block_sizes[i]);
273
274 g_assert_nonnull (p);
275 g_assert_true (((guintptr) p & (rcbox_alignment - 1)) == 0);
276
277 g_atomic_rc_box_release (mem_block: p);
278 }
279}
280
281int
282main (int argc,
283 char *argv[])
284{
285 g_test_init (argc: &argc, argv: &argv, NULL);
286
287 g_test_add_func (testpath: "/rcbox/new", test_func: test_rcbox_new);
288 g_test_add_func (testpath: "/rcbox/release-full", test_func: test_rcbox_release_full);
289 g_test_add_func (testpath: "/rcbox/dup", test_func: test_rcbox_dup);
290 g_test_add_func (testpath: "/rcbox/alignment", test_func: test_rcbox_alignment);
291
292 g_test_add_func (testpath: "/atomic-rcbox/new", test_func: test_atomic_rcbox_new);
293 g_test_add_func (testpath: "/atomic-rcbox/release-full", test_func: test_atomic_rcbox_release_full);
294 g_test_add_func (testpath: "/atomic-rcbox/dup", test_func: test_atomic_rcbox_dup);
295 g_test_add_func (testpath: "/atomic-rcbox/alignment", test_func: test_atomic_rcbox_alignment);
296
297 return g_test_run ();
298}
299

source code of gtk/subprojects/glib/glib/tests/rcbox.c