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 | |
21 | typedef struct { |
22 | float x, y; |
23 | } Point; |
24 | |
25 | static Point *global_point; |
26 | |
27 | /* test_rcbox_new: Test g_rc_box_new() */ |
28 | static void |
29 | test_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() */ |
47 | static void |
48 | test_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 | |
65 | static void |
66 | point_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 | */ |
81 | static void |
82 | test_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 | */ |
105 | static void |
106 | test_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 | |
126 | static Point *global_point_a; |
127 | static Point *global_point_b; |
128 | |
129 | static void |
130 | point_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 | |
138 | static void |
139 | point_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 | */ |
150 | static void |
151 | test_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 | */ |
188 | static void |
189 | test_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 |
228 | static const gsize rcbox_alignment = 16; |
229 | #else |
230 | static const gsize rcbox_alignment = 8; |
231 | #endif |
232 | |
233 | /* verify that the refcounted allocation is properly aligned */ |
234 | static void |
235 | test_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 */ |
258 | static void |
259 | test_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 | |
281 | int |
282 | main (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 | |