prop_object.c revision 1.42 1 /* $NetBSD: prop_object.c,v 1.42 2025/05/14 03:25:46 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2006, 2007, 2025 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "prop_object_impl.h"
33 #include <prop/prop_object.h>
34
35 #ifdef _PROP_NEED_REFCNT_MTX
36 static pthread_mutex_t _prop_refcnt_mtx = PTHREAD_MUTEX_INITIALIZER;
37 #endif /* _PROP_NEED_REFCNT_MTX */
38
39 #if !defined(_KERNEL) && !defined(_STANDALONE)
40 #include <sys/mman.h>
41 #include <sys/stat.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <limits.h>
45 #include <unistd.h>
46 #endif
47
48 #ifdef _STANDALONE
49 void *
50 _prop_standalone_calloc(size_t size)
51 {
52 void *rv;
53
54 rv = alloc(size);
55 if (rv != NULL)
56 memset(rv, 0, size);
57
58 return (rv);
59 }
60
61 void *
62 _prop_standalone_realloc(void *v, size_t size)
63 {
64 void *rv;
65
66 rv = alloc(size);
67 if (rv != NULL) {
68 memcpy(rv, v, size); /* XXX */
69 dealloc(v, 0); /* XXX */
70 }
71
72 return (rv);
73 }
74 #endif /* _STANDALONE */
75
76 /*
77 * _prop_object_init --
78 * Initialize an object. Called when sub-classes create
79 * an instance.
80 */
81 void
82 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
83 {
84
85 po->po_type = pot;
86 po->po_refcnt = 1;
87 }
88
89 /*
90 * _prop_object_fini --
91 * Finalize an object. Called when sub-classes destroy
92 * an instance.
93 */
94 /*ARGSUSED*/
95 void
96 _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
97 {
98 /* Nothing to do, currently. */
99 }
100
101 /*
102 * prop_object_retain --
103 * Increment the reference count on an object.
104 */
105 _PROP_EXPORT void
106 prop_object_retain(prop_object_t obj)
107 {
108 struct _prop_object *po = obj;
109 uint32_t ncnt __unused;
110
111 _PROP_ATOMIC_INC32_NV(&po->po_refcnt, ncnt);
112 _PROP_ASSERT(ncnt != 0);
113 }
114
115 /*
116 * prop_object_release_emergency
117 * A direct free with prop_object_release failed.
118 * Walk down the tree until a leaf is found and
119 * free that. Do not recurse to avoid stack overflows.
120 *
121 * This is a slow edge condition, but necessary to
122 * guarantee that an object can always be freed.
123 */
124 static void
125 prop_object_release_emergency(prop_object_t obj)
126 {
127 struct _prop_object *po;
128 void (*unlock)(void);
129 prop_object_t parent = NULL;
130 uint32_t ocnt;
131
132 for (;;) {
133 po = obj;
134 _PROP_ASSERT(obj);
135
136 if (po->po_type->pot_lock != NULL)
137 po->po_type->pot_lock();
138
139 /* Save pointerto unlock function */
140 unlock = po->po_type->pot_unlock;
141
142 /* Dance a bit to make sure we always get the non-racy ocnt */
143 _PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt);
144 ocnt++;
145 _PROP_ASSERT(ocnt != 0);
146
147 if (ocnt != 1) {
148 if (unlock != NULL)
149 unlock();
150 break;
151 }
152
153 _PROP_ASSERT(po->po_type);
154 if ((po->po_type->pot_free)(NULL, &obj) ==
155 _PROP_OBJECT_FREE_DONE) {
156 if (unlock != NULL)
157 unlock();
158 break;
159 }
160
161 if (unlock != NULL)
162 unlock();
163
164 parent = po;
165 _PROP_ATOMIC_INC32(&po->po_refcnt);
166 }
167 _PROP_ASSERT(parent);
168 /* One object was just freed. */
169 po = parent;
170 (*po->po_type->pot_emergency_free)(parent);
171 }
172
173 /*
174 * prop_object_release --
175 * Decrement the reference count on an object.
176 *
177 * Free the object if we are releasing the final
178 * reference.
179 */
180 _PROP_EXPORT void
181 prop_object_release(prop_object_t obj)
182 {
183 struct _prop_object *po;
184 struct _prop_stack stack;
185 void (*unlock)(void);
186 int ret;
187 uint32_t ocnt;
188
189 _prop_stack_init(&stack);
190
191 do {
192 do {
193 po = obj;
194 _PROP_ASSERT(obj);
195
196 if (po->po_type->pot_lock != NULL)
197 po->po_type->pot_lock();
198
199 /* Save pointer to object unlock function */
200 unlock = po->po_type->pot_unlock;
201
202 _PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt);
203 ocnt++;
204 _PROP_ASSERT(ocnt != 0);
205
206 if (ocnt != 1) {
207 ret = 0;
208 if (unlock != NULL)
209 unlock();
210 break;
211 }
212
213 ret = (po->po_type->pot_free)(&stack, &obj);
214
215 if (unlock != NULL)
216 unlock();
217
218 if (ret == _PROP_OBJECT_FREE_DONE)
219 break;
220
221 _PROP_ATOMIC_INC32(&po->po_refcnt);
222 } while (ret == _PROP_OBJECT_FREE_RECURSE);
223 if (ret == _PROP_OBJECT_FREE_FAILED)
224 prop_object_release_emergency(obj);
225 } while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
226 }
227
228 /*
229 * prop_object_type --
230 * Return the type of an object.
231 */
232 _PROP_EXPORT prop_type_t
233 prop_object_type(prop_object_t obj)
234 {
235 struct _prop_object *po = obj;
236
237 if (obj == NULL)
238 return (PROP_TYPE_UNKNOWN);
239
240 return (po->po_type->pot_type);
241 }
242
243 /*
244 * prop_object_equals --
245 * Returns true if thw two objects are equivalent.
246 */
247 _PROP_EXPORT bool
248 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
249 {
250 return (prop_object_equals_with_error(obj1, obj2, NULL));
251 }
252
253 _PROP_EXPORT bool
254 prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
255 bool *error_flag)
256 {
257 struct _prop_object *po1;
258 struct _prop_object *po2;
259 void *stored_pointer1, *stored_pointer2;
260 prop_object_t next_obj1, next_obj2;
261 struct _prop_stack stack;
262 _prop_object_equals_rv_t ret;
263
264 _prop_stack_init(&stack);
265 if (error_flag)
266 *error_flag = false;
267
268 start_subtree:
269 stored_pointer1 = NULL;
270 stored_pointer2 = NULL;
271 po1 = obj1;
272 po2 = obj2;
273
274 if (po1->po_type != po2->po_type)
275 return (false);
276
277 continue_subtree:
278 ret = (*po1->po_type->pot_equals)(obj1, obj2,
279 &stored_pointer1, &stored_pointer2,
280 &next_obj1, &next_obj2);
281 if (ret == _PROP_OBJECT_EQUALS_FALSE)
282 goto finish;
283 if (ret == _PROP_OBJECT_EQUALS_TRUE) {
284 if (!_prop_stack_pop(&stack, &obj1, &obj2,
285 &stored_pointer1, &stored_pointer2))
286 return true;
287 po1 = obj1;
288 po2 = obj2;
289 goto continue_subtree;
290 }
291 _PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
292
293 if (!_prop_stack_push(&stack, obj1, obj2,
294 stored_pointer1, stored_pointer2)) {
295 if (error_flag)
296 *error_flag = true;
297 goto finish;
298 }
299 obj1 = next_obj1;
300 obj2 = next_obj2;
301 goto start_subtree;
302
303 finish:
304 while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
305 po1 = obj1;
306 (*po1->po_type->pot_equals_finish)(obj1, obj2);
307 }
308 return (false);
309 }
310
311 /*
312 * prop_object_iterator_next --
313 * Return the next item during an iteration.
314 */
315 _PROP_EXPORT prop_object_t
316 prop_object_iterator_next(prop_object_iterator_t pi)
317 {
318
319 return ((*pi->pi_next_object)(pi));
320 }
321
322 /*
323 * prop_object_iterator_reset --
324 * Reset the iterator to the first object so as to restart
325 * iteration.
326 */
327 _PROP_EXPORT void
328 prop_object_iterator_reset(prop_object_iterator_t pi)
329 {
330
331 (*pi->pi_reset)(pi);
332 }
333
334 /*
335 * prop_object_iterator_release --
336 * Release the object iterator.
337 */
338 _PROP_EXPORT void
339 prop_object_iterator_release(prop_object_iterator_t pi)
340 {
341
342 prop_object_release(pi->pi_obj);
343 _PROP_FREE(pi, M_TEMP);
344 }
345