Home | History | Annotate | Line # | Download | only in libprop
      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