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