1 /* grefcount.c: Reference counting
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 "grefcount.h"
24
25 #include "gatomic.h"
26 #include "gmessages.h"
27
28 /**
29 * grefcount:
30 *
31 * A type for implementing non-atomic reference count semantics.
32 *
33 * Use g_ref_count_init() to initialize it; g_ref_count_inc() to
34 * increase the counter, and g_ref_count_dec() to decrease it.
35 *
36 * It is safe to use #grefcount only if you're expecting to operate
37 * on the reference counter from a single thread. It is entirely up
38 * to you to ensure that all reference count changes happen in the
39 * same thread.
40 *
41 * See also: #gatomicrefcount
42 *
43 * Since: 2.58
44 */
45
46 /**
47 * gatomicrefcount:
48 *
49 * A type for implementing atomic reference count semantics.
50 *
51 * Use g_atomic_ref_count_init() to initialize it; g_atomic_ref_count_inc()
52 * to increase the counter, and g_atomic_ref_count_dec() to decrease it.
53 *
54 * It is safe to use #gatomicrefcount if you're expecting to operate on the
55 * reference counter from multiple threads.
56 *
57 * See also: #grefcount
58 *
59 * Since: 2.58
60 */
61
62 /**
63 * g_ref_count_init:
64 * @rc: the address of a reference count variable
65 *
66 * Initializes a reference count variable to 1.
67 *
68 * Since: 2.58
69 */
70 void
71 (g_ref_count_init) (grefcount *rc)
72 {
73 g_return_if_fail (rc != NULL);
74
75 /* Non-atomic refcounting is implemented using the negative range
76 * of signed integers:
77 *
78 * G_MININT Z¯< 0 > Z⁺ G_MAXINT
79 * |----------------------------|----------------------------|
80 *
81 * Acquiring a reference moves us towards MININT, and releasing a
82 * reference moves us towards 0.
83 */
84 *rc = -1;
85 }
86
87 /**
88 * g_ref_count_inc:
89 * @rc: the address of a reference count variable
90 *
91 * Increases the reference count.
92 *
93 * Since: 2.58
94 */
95 void
96 (g_ref_count_inc) (grefcount *rc)
97 {
98 grefcount rrc;
99
100 g_return_if_fail (rc != NULL);
101
102 rrc = *rc;
103
104 g_return_if_fail (rrc < 0);
105
106 /* Check for saturation */
107 if (rrc == G_MININT)
108 {
109 g_critical ("Reference count %p has reached saturation", rc);
110 return;
111 }
112
113 rrc -= 1;
114
115 *rc = rrc;
116 }
117
118 /**
119 * g_ref_count_dec:
120 * @rc: the address of a reference count variable
121 *
122 * Decreases the reference count.
123 *
124 * If %TRUE is returned, the reference count reached 0. After this point, @rc
125 * is an undefined state and must be reinitialized with
126 * g_ref_count_init() to be used again.
127 *
128 * Returns: %TRUE if the reference count reached 0, and %FALSE otherwise
129 *
130 * Since: 2.58
131 */
132 gboolean
133 (g_ref_count_dec) (grefcount *rc)
134 {
135 grefcount rrc;
136
137 g_return_val_if_fail (rc != NULL, FALSE);
138
139 rrc = *rc;
140
141 g_return_val_if_fail (rrc < 0, FALSE);
142
143 rrc += 1;
144 if (rrc == 0)
145 return TRUE;
146
147 *rc = rrc;
148
149 return FALSE;
150 }
151
152 /**
153 * g_ref_count_compare:
154 * @rc: the address of a reference count variable
155 * @val: the value to compare
156 *
157 * Compares the current value of @rc with @val.
158 *
159 * Returns: %TRUE if the reference count is the same
160 * as the given value
161 *
162 * Since: 2.58
163 */
164 gboolean
165 (g_ref_count_compare) (grefcount *rc,
166 gint val)
167 {
168 grefcount rrc;
169
170 g_return_val_if_fail (rc != NULL, FALSE);
171 g_return_val_if_fail (val >= 0, FALSE);
172
173 rrc = *rc;
174
175 if (val == G_MAXINT)
176 return rrc == G_MININT;
177
178 return rrc == -val;
179 }
180
181 /**
182 * g_atomic_ref_count_init:
183 * @arc: the address of an atomic reference count variable
184 *
185 * Initializes a reference count variable to 1.
186 *
187 * Since: 2.58
188 */
189 void
190 (g_atomic_ref_count_init) (gatomicrefcount *arc)
191 {
192 g_return_if_fail (arc != NULL);
193
194 /* Atomic refcounting is implemented using the positive range
195 * of signed integers:
196 *
197 * G_MININT Z¯< 0 > Z⁺ G_MAXINT
198 * |----------------------------|----------------------------|
199 *
200 * Acquiring a reference moves us towards MAXINT, and releasing a
201 * reference moves us towards 0.
202 */
203 *arc = 1;
204 }
205
206 /**
207 * g_atomic_ref_count_inc:
208 * @arc: the address of an atomic reference count variable
209 *
210 * Atomically increases the reference count.
211 *
212 * Since: 2.58
213 */
214 void
215 (g_atomic_ref_count_inc) (gatomicrefcount *arc)
216 {
217 gint old_value;
218
219 g_return_if_fail (arc != NULL);
220 old_value = g_atomic_int_add (arc, 1);
221 g_return_if_fail (old_value > 0);
222
223 if (old_value == G_MAXINT)
224 g_critical ("Reference count has reached saturation");
225 }
226
227 /**
228 * g_atomic_ref_count_dec:
229 * @arc: the address of an atomic reference count variable
230 *
231 * Atomically decreases the reference count.
232 *
233 * If %TRUE is returned, the reference count reached 0. After this point, @arc
234 * is an undefined state and must be reinitialized with
235 * g_atomic_ref_count_init() to be used again.
236 *
237 * Returns: %TRUE if the reference count reached 0, and %FALSE otherwise
238 *
239 * Since: 2.58
240 */
241 gboolean
242 (g_atomic_ref_count_dec) (gatomicrefcount *arc)
243 {
244 gint old_value;
245
246 g_return_val_if_fail (arc != NULL, FALSE);
247 old_value = g_atomic_int_add (arc, -1);
248 g_return_val_if_fail (old_value > 0, FALSE);
249
250 return old_value == 1;
251 }
252
253 /**
254 * g_atomic_ref_count_compare:
255 * @arc: the address of an atomic reference count variable
256 * @val: the value to compare
257 *
258 * Atomically compares the current value of @arc with @val.
259 *
260 * Returns: %TRUE if the reference count is the same
261 * as the given value
262 *
263 * Since: 2.58
264 */
265 gboolean
266 (g_atomic_ref_count_compare) (gatomicrefcount *arc,
267 gint val)
268 {
269 g_return_val_if_fail (arc != NULL, FALSE);
270 g_return_val_if_fail (val >= 0, FALSE);
271
272 return g_atomic_int_get (arc) == val;
273 }