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