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