xref: /illumos-gate/usr/src/lib/libuutil/common/uu_list.c (revision 0dee7919e2f2a6479d16b370af93747b9416b242)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "libuutil_common.h"
30 
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/time.h>
35 
36 #define	ELEM_TO_NODE(lp, e) \
37 	((uu_list_node_impl_t *)((uintptr_t)(e) + (lp)->ul_offset))
38 
39 #define	NODE_TO_ELEM(lp, n) \
40 	((void *)((uintptr_t)(n) - (lp)->ul_offset))
41 
42 /*
43  * uu_list_index_ts define a location for insertion.  They are simply a
44  * pointer to the object after the insertion point.  We store a mark
45  * in the low-bits of the index, to help prevent mistakes.
46  *
47  * When debugging, the index mark changes on every insert and delete, to
48  * catch stale references.
49  */
50 #define	INDEX_MAX		(sizeof (uintptr_t) - 1)
51 #define	INDEX_NEXT(m)		(((m) == INDEX_MAX)? 1 : ((m) + 1) & INDEX_MAX)
52 
53 #define	INDEX_TO_NODE(i)	((uu_list_node_impl_t *)((i) & ~INDEX_MAX))
54 #define	NODE_TO_INDEX(p, n)	(((uintptr_t)(n) & ~INDEX_MAX) | (p)->ul_index)
55 #define	INDEX_VALID(p, i)	(((i) & INDEX_MAX) == (p)->ul_index)
56 #define	INDEX_CHECK(i)		(((i) & INDEX_MAX) != 0)
57 
58 #define	POOL_TO_MARKER(pp) ((void *)((uintptr_t)(pp) | 1))
59 
60 static uu_list_pool_t	uu_null_lpool = { &uu_null_lpool, &uu_null_lpool };
61 static pthread_mutex_t	uu_lpool_list_lock = PTHREAD_MUTEX_INITIALIZER;
62 
63 uu_list_pool_t *
64 uu_list_pool_create(const char *name, size_t objsize,
65     size_t nodeoffset, uu_compare_fn_t *compare_func, uint32_t flags)
66 {
67 	uu_list_pool_t *pp, *next, *prev;
68 
69 	if (name == NULL ||
70 	    uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
71 	    nodeoffset + sizeof (uu_list_node_t) > objsize) {
72 		uu_set_error(UU_ERROR_INVALID_ARGUMENT);
73 		return (NULL);
74 	}
75 
76 	if (flags & ~UU_LIST_POOL_DEBUG) {
77 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
78 		return (NULL);
79 	}
80 
81 	pp = uu_zalloc(sizeof (uu_list_pool_t));
82 	if (pp == NULL) {
83 		uu_set_error(UU_ERROR_NO_MEMORY);
84 		return (NULL);
85 	}
86 
87 	(void) strlcpy(pp->ulp_name, name, sizeof (pp->ulp_name));
88 	pp->ulp_nodeoffset = nodeoffset;
89 	pp->ulp_objsize = objsize;
90 	pp->ulp_cmp = compare_func;
91 	if (flags & UU_LIST_POOL_DEBUG)
92 		pp->ulp_debug = 1;
93 	pp->ulp_last_index = 0;
94 
95 	(void) pthread_mutex_init(&pp->ulp_lock, NULL);
96 
97 	pp->ulp_null_list.ul_next_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
98 	pp->ulp_null_list.ul_prev_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
99 
100 	(void) pthread_mutex_lock(&uu_lpool_list_lock);
101 	pp->ulp_next = next = &uu_null_lpool;
102 	pp->ulp_prev = prev = next->ulp_prev;
103 	next->ulp_prev = pp;
104 	prev->ulp_next = pp;
105 	(void) pthread_mutex_unlock(&uu_lpool_list_lock);
106 
107 	return (pp);
108 }
109 
110 void
111 uu_list_pool_destroy(uu_list_pool_t *pp)
112 {
113 	if (pp->ulp_debug) {
114 		if (pp->ulp_null_list.ul_next_enc !=
115 		    UU_PTR_ENCODE(&pp->ulp_null_list) ||
116 		    pp->ulp_null_list.ul_prev_enc !=
117 		    UU_PTR_ENCODE(&pp->ulp_null_list)) {
118 			uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has "
119 			    "outstanding lists, or is corrupt.\n",
120 			    sizeof (pp->ulp_name), pp->ulp_name, pp);
121 		}
122 	}
123 	(void) pthread_mutex_lock(&uu_lpool_list_lock);
124 	pp->ulp_next->ulp_prev = pp->ulp_prev;
125 	pp->ulp_prev->ulp_next = pp->ulp_next;
126 	(void) pthread_mutex_unlock(&uu_lpool_list_lock);
127 	pp->ulp_prev = NULL;
128 	pp->ulp_next = NULL;
129 	uu_free(pp);
130 }
131 
132 void
133 uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
134 {
135 	uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
136 
137 	if (pp->ulp_debug) {
138 		uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
139 		if (offset + sizeof (*np) > pp->ulp_objsize) {
140 			uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
141 			    "offset %ld doesn't fit in object (size %ld)\n",
142 			    base, np, pp, pp->ulp_name, offset,
143 			    pp->ulp_objsize);
144 		}
145 		if (offset != pp->ulp_nodeoffset) {
146 			uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
147 			    "offset %ld doesn't match pool's offset (%ld)\n",
148 			    base, np, pp, pp->ulp_name, offset,
149 			    pp->ulp_objsize);
150 		}
151 	}
152 	np->uln_next = POOL_TO_MARKER(pp);
153 	np->uln_prev = NULL;
154 }
155 
156 void
157 uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
158 {
159 	uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
160 
161 	if (pp->ulp_debug) {
162 		if (np->uln_next == NULL &&
163 		    np->uln_prev == NULL) {
164 			uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
165 			    "node already finied\n",
166 			    base, np_arg, pp, pp->ulp_name);
167 		}
168 		if (np->uln_next != POOL_TO_MARKER(pp) ||
169 		    np->uln_prev != NULL) {
170 			uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
171 			    "node corrupt or on list\n",
172 			    base, np_arg, pp, pp->ulp_name);
173 		}
174 	}
175 	np->uln_next = NULL;
176 	np->uln_prev = NULL;
177 }
178 
179 uu_list_t *
180 uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)
181 {
182 	uu_list_t *lp, *next, *prev;
183 
184 	if (flags & ~(UU_LIST_DEBUG | UU_LIST_SORTED)) {
185 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
186 		return (NULL);
187 	}
188 
189 	if ((flags & UU_LIST_SORTED) && pp->ulp_cmp == NULL) {
190 		if (pp->ulp_debug)
191 			uu_panic("uu_list_create(%p, ...): requested "
192 			    "UU_LIST_SORTED, but pool has no comparison func\n",
193 			    pp);
194 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
195 		return (NULL);
196 	}
197 
198 	lp = uu_zalloc(sizeof (*lp));
199 	if (lp == NULL) {
200 		uu_set_error(UU_ERROR_NO_MEMORY);
201 		return (NULL);
202 	}
203 
204 	lp->ul_pool = pp;
205 	lp->ul_parent_enc = UU_PTR_ENCODE(parent);
206 	lp->ul_offset = pp->ulp_nodeoffset;
207 	lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG);
208 	lp->ul_sorted = (flags & UU_LIST_SORTED);
209 	lp->ul_numnodes = 0;
210 	lp->ul_index = (pp->ulp_last_index = INDEX_NEXT(pp->ulp_last_index));
211 
212 	lp->ul_null_node.uln_next = &lp->ul_null_node;
213 	lp->ul_null_node.uln_prev = &lp->ul_null_node;
214 
215 	lp->ul_null_walk.ulw_next = &lp->ul_null_walk;
216 	lp->ul_null_walk.ulw_prev = &lp->ul_null_walk;
217 
218 	(void) pthread_mutex_lock(&pp->ulp_lock);
219 	next = &pp->ulp_null_list;
220 	prev = UU_PTR_DECODE(next->ul_prev_enc);
221 	lp->ul_next_enc = UU_PTR_ENCODE(next);
222 	lp->ul_prev_enc = UU_PTR_ENCODE(prev);
223 	next->ul_prev_enc = UU_PTR_ENCODE(lp);
224 	prev->ul_next_enc = UU_PTR_ENCODE(lp);
225 	(void) pthread_mutex_unlock(&pp->ulp_lock);
226 
227 	return (lp);
228 }
229 
230 void
231 uu_list_destroy(uu_list_t *lp)
232 {
233 	uu_list_pool_t *pp = lp->ul_pool;
234 
235 	if (lp->ul_debug) {
236 		if (lp->ul_null_node.uln_next != &lp->ul_null_node ||
237 		    lp->ul_null_node.uln_prev != &lp->ul_null_node) {
238 			uu_panic("uu_list_destroy(%p):  list not empty\n",
239 			    lp);
240 		}
241 		if (lp->ul_numnodes != 0) {
242 			uu_panic("uu_list_destroy(%p):  numnodes is nonzero, "
243 			    "but list is empty\n", lp);
244 		}
245 		if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||
246 		    lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {
247 			uu_panic("uu_list_destroy(%p):  outstanding walkers\n",
248 			    lp);
249 		}
250 	}
251 
252 	(void) pthread_mutex_lock(&pp->ulp_lock);
253 	UU_LIST_PTR(lp->ul_next_enc)->ul_prev_enc = lp->ul_prev_enc;
254 	UU_LIST_PTR(lp->ul_prev_enc)->ul_next_enc = lp->ul_next_enc;
255 	(void) pthread_mutex_unlock(&pp->ulp_lock);
256 	lp->ul_prev_enc = UU_PTR_ENCODE(NULL);
257 	lp->ul_next_enc = UU_PTR_ENCODE(NULL);
258 	lp->ul_pool = NULL;
259 	uu_free(lp);
260 }
261 
262 static void
263 list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,
264     uu_list_node_impl_t *next)
265 {
266 	if (lp->ul_debug) {
267 		if (next->uln_prev != prev || prev->uln_next != next)
268 			uu_panic("insert(%p): internal error: %p and %p not "
269 			    "neighbors\n", lp, next, prev);
270 
271 		if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||
272 		    np->uln_prev != NULL) {
273 			uu_panic("insert(%p): elem %p node %p corrupt, "
274 			    "not initialized, or already in a list.\n",
275 			    lp, NODE_TO_ELEM(lp, np), np);
276 		}
277 		/*
278 		 * invalidate outstanding uu_list_index_ts.
279 		 */
280 		lp->ul_index = INDEX_NEXT(lp->ul_index);
281 	}
282 	np->uln_next = next;
283 	np->uln_prev = prev;
284 	next->uln_prev = np;
285 	prev->uln_next = np;
286 
287 	lp->ul_numnodes++;
288 }
289 
290 void
291 uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)
292 {
293 	uu_list_node_impl_t *np;
294 
295 	np = INDEX_TO_NODE(idx);
296 	if (np == NULL)
297 		np = &lp->ul_null_node;
298 
299 	if (lp->ul_debug) {
300 		if (!INDEX_VALID(lp, idx))
301 			uu_panic("uu_list_insert(%p, %p, %p): %s\n",
302 			    lp, elem, idx,
303 			    INDEX_CHECK(idx)? "outdated index" :
304 			    "invalid index");
305 		if (np->uln_prev == NULL)
306 			uu_panic("uu_list_insert(%p, %p, %p): out-of-date "
307 			    "index\n", lp, elem, idx);
308 	}
309 
310 	list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
311 }
312 
313 void *
314 uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
315 {
316 	int sorted = lp->ul_sorted;
317 	uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
318 	uu_list_node_impl_t *np;
319 
320 	if (func == NULL) {
321 		if (out != NULL)
322 			*out = 0;
323 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
324 		return (NULL);
325 	}
326 	for (np = lp->ul_null_node.uln_next; np != &lp->ul_null_node;
327 	    np = np->uln_next) {
328 		void *ep = NODE_TO_ELEM(lp, np);
329 		int cmp = func(ep, elem, private);
330 		if (cmp == 0) {
331 			if (out != NULL)
332 				*out = NODE_TO_INDEX(lp, np);
333 			return (ep);
334 		}
335 		if (sorted && cmp > 0) {
336 			if (out != NULL)
337 				*out = NODE_TO_INDEX(lp, np);
338 			return (NULL);
339 		}
340 	}
341 	if (out != NULL)
342 		*out = NODE_TO_INDEX(lp, 0);
343 	return (NULL);
344 }
345 
346 void *
347 uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)
348 {
349 	uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
350 
351 	if (np == NULL)
352 		np = &lp->ul_null_node;
353 
354 	if (lp->ul_debug) {
355 		if (!INDEX_VALID(lp, idx))
356 			uu_panic("uu_list_nearest_next(%p, %p): %s\n",
357 			    lp, idx, INDEX_CHECK(idx)? "outdated index" :
358 			    "invalid index");
359 		if (np->uln_prev == NULL)
360 			uu_panic("uu_list_nearest_next(%p, %p): out-of-date "
361 			    "index\n", lp, idx);
362 	}
363 
364 	if (np == &lp->ul_null_node)
365 		return (NULL);
366 	else
367 		return (NODE_TO_ELEM(lp, np));
368 }
369 
370 void *
371 uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)
372 {
373 	uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
374 
375 	if (np == NULL)
376 		np = &lp->ul_null_node;
377 
378 	if (lp->ul_debug) {
379 		if (!INDEX_VALID(lp, idx))
380 			uu_panic("uu_list_nearest_prev(%p, %p): %s\n",
381 			    lp, idx, INDEX_CHECK(idx)? "outdated index" :
382 			    "invalid index");
383 		if (np->uln_prev == NULL)
384 			uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "
385 			    "index\n", lp, idx);
386 	}
387 
388 	if ((np = np->uln_prev) == &lp->ul_null_node)
389 		return (NULL);
390 	else
391 		return (NODE_TO_ELEM(lp, np));
392 }
393 
394 static void
395 list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)
396 {
397 	uu_list_walk_t *next, *prev;
398 
399 	int robust = (flags & UU_WALK_ROBUST);
400 	int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
401 
402 	(void) memset(wp, 0, sizeof (*wp));
403 	wp->ulw_list = lp;
404 	wp->ulw_robust = robust;
405 	wp->ulw_dir = direction;
406 	if (direction > 0)
407 		wp->ulw_next_result = lp->ul_null_node.uln_next;
408 	else
409 		wp->ulw_next_result = lp->ul_null_node.uln_prev;
410 
411 	if (lp->ul_debug || robust) {
412 		wp->ulw_next = next = &lp->ul_null_walk;
413 		wp->ulw_prev = prev = next->ulw_prev;
414 		next->ulw_prev = wp;
415 		prev->ulw_next = wp;
416 	}
417 }
418 
419 static uu_list_node_impl_t *
420 list_walk_advance(uu_list_walk_t *wp, uu_list_t *lp)
421 {
422 	uu_list_node_impl_t *np = wp->ulw_next_result;
423 	uu_list_node_impl_t *next;
424 
425 	if (np == &lp->ul_null_node)
426 		return (NULL);
427 
428 	next = (wp->ulw_dir > 0)? np->uln_next : np->uln_prev;
429 
430 	wp->ulw_next_result = next;
431 	return (np);
432 }
433 
434 static void
435 list_walk_fini(uu_list_walk_t *wp)
436 {
437 	/* GLXXX debugging? */
438 	if (wp->ulw_next != NULL) {
439 		wp->ulw_next->ulw_prev = wp->ulw_prev;
440 		wp->ulw_prev->ulw_next = wp->ulw_next;
441 		wp->ulw_next = NULL;
442 		wp->ulw_prev = NULL;
443 	}
444 	wp->ulw_list = NULL;
445 	wp->ulw_next_result = NULL;
446 }
447 
448 uu_list_walk_t *
449 uu_list_walk_start(uu_list_t *lp, uint32_t flags)
450 {
451 	uu_list_walk_t *wp;
452 
453 	if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
454 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
455 		return (NULL);
456 	}
457 
458 	wp = uu_zalloc(sizeof (*wp));
459 	if (wp == NULL) {
460 		uu_set_error(UU_ERROR_NO_MEMORY);
461 		return (NULL);
462 	}
463 
464 	list_walk_init(wp, lp, flags);
465 	return (wp);
466 }
467 
468 void *
469 uu_list_walk_next(uu_list_walk_t *wp)
470 {
471 	uu_list_t *lp = wp->ulw_list;
472 	uu_list_node_impl_t *np = list_walk_advance(wp, lp);
473 
474 	if (np == NULL)
475 		return (NULL);
476 
477 	return (NODE_TO_ELEM(lp, np));
478 }
479 
480 void
481 uu_list_walk_end(uu_list_walk_t *wp)
482 {
483 	list_walk_fini(wp);
484 	uu_free(wp);
485 }
486 
487 int
488 uu_list_walk(uu_list_t *lp, uu_walk_fn_t *func, void *private, uint32_t flags)
489 {
490 	uu_list_node_impl_t *np;
491 
492 	int status = UU_WALK_NEXT;
493 
494 	int robust = (flags & UU_WALK_ROBUST);
495 	int reverse = (flags & UU_WALK_REVERSE);
496 
497 	if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
498 		uu_set_error(UU_ERROR_UNKNOWN_FLAG);
499 		return (-1);
500 	}
501 
502 	if (lp->ul_debug || robust) {
503 		uu_list_walk_t my_walk;
504 		void *e;
505 
506 		list_walk_init(&my_walk, lp, flags);
507 		while (status == UU_WALK_NEXT &&
508 		    (e = uu_list_walk_next(&my_walk)) != NULL)
509 			status = (*func)(e, private);
510 		list_walk_fini(&my_walk);
511 	} else {
512 		if (!reverse) {
513 			for (np = lp->ul_null_node.uln_next;
514 			    status == UU_WALK_NEXT && np != &lp->ul_null_node;
515 			    np = np->uln_next) {
516 				status = (*func)(NODE_TO_ELEM(lp, np), private);
517 			}
518 		} else {
519 			for (np = lp->ul_null_node.uln_prev;
520 			    status == UU_WALK_NEXT && np != &lp->ul_null_node;
521 			    np = np->uln_prev) {
522 				status = (*func)(NODE_TO_ELEM(lp, np), private);
523 			}
524 		}
525 	}
526 	if (status >= 0)
527 		return (0);
528 	uu_set_error(UU_ERROR_CALLBACK_FAILED);
529 	return (-1);
530 }
531 
532 void
533 uu_list_remove(uu_list_t *lp, void *elem)
534 {
535 	uu_list_node_impl_t *np = ELEM_TO_NODE(lp, elem);
536 	uu_list_walk_t *wp;
537 
538 	if (lp->ul_debug) {
539 		if (np->uln_prev == NULL)
540 			uu_panic("uu_list_remove(%p, %p): elem not on list\n",
541 			    lp, elem);
542 		/*
543 		 * invalidate outstanding uu_list_index_ts.
544 		 */
545 		lp->ul_index = INDEX_NEXT(lp->ul_index);
546 	}
547 
548 	/*
549 	 * robust walkers must be advanced.  In debug mode, non-robust
550 	 * walkers are also on the list.  If there are any, it's an error.
551 	 */
552 	for (wp = lp->ul_null_walk.ulw_next; wp != &lp->ul_null_walk;
553 	    wp = wp->ulw_next) {
554 		if (wp->ulw_robust) {
555 			if (np == wp->ulw_next_result)
556 				(void) list_walk_advance(wp, lp);
557 		} else if (wp->ulw_next_result != NULL) {
558 			uu_panic("uu_list_remove(%p, %p): active non-robust "
559 			    "walker\n", lp, elem);
560 		}
561 	}
562 
563 	np->uln_next->uln_prev = np->uln_prev;
564 	np->uln_prev->uln_next = np->uln_next;
565 
566 	lp->ul_numnodes--;
567 
568 	np->uln_next = POOL_TO_MARKER(lp->ul_pool);
569 	np->uln_prev = NULL;
570 }
571 
572 void *
573 uu_list_teardown(uu_list_t *lp, void **cookie)
574 {
575 	void *ep;
576 
577 	/*
578 	 * XXX: disable list modification until list is empty
579 	 */
580 	if (lp->ul_debug && *cookie != NULL)
581 		uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n", lp,
582 		    cookie);
583 
584 	ep = uu_list_first(lp);
585 	if (ep)
586 		uu_list_remove(lp, ep);
587 	return (ep);
588 }
589 
590 int
591 uu_list_insert_before(uu_list_t *lp, void *target, void *elem)
592 {
593 	uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
594 
595 	if (target == NULL)
596 		np = &lp->ul_null_node;
597 
598 	if (lp->ul_debug) {
599 		if (np->uln_prev == NULL)
600 			uu_panic("uu_list_insert_before(%p, %p, %p): %p is "
601 			    "not currently on a list\n",
602 			    lp, target, elem, target);
603 	}
604 	if (lp->ul_sorted) {
605 		if (lp->ul_debug)
606 			uu_panic("uu_list_insert_before(%p, ...): list is "
607 			    "UU_LIST_SORTED\n", lp);
608 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
609 		return (-1);
610 	}
611 
612 	list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
613 	return (0);
614 }
615 
616 int
617 uu_list_insert_after(uu_list_t *lp, void *target, void *elem)
618 {
619 	uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
620 
621 	if (target == NULL)
622 		np = &lp->ul_null_node;
623 
624 	if (lp->ul_debug) {
625 		if (np->uln_prev == NULL)
626 			uu_panic("uu_list_insert_after(%p, %p, %p): %p is "
627 			    "not currently on a list\n",
628 			    lp, target, elem, target);
629 	}
630 	if (lp->ul_sorted) {
631 		if (lp->ul_debug)
632 			uu_panic("uu_list_insert_after(%p, ...): list is "
633 			    "UU_LIST_SORTED\n", lp);
634 		uu_set_error(UU_ERROR_NOT_SUPPORTED);
635 		return (-1);
636 	}
637 
638 	list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);
639 	return (0);
640 }
641 
642 size_t
643 uu_list_numnodes(uu_list_t *lp)
644 {
645 	return (lp->ul_numnodes);
646 }
647 
648 void *
649 uu_list_first(uu_list_t *lp)
650 {
651 	uu_list_node_impl_t *n = lp->ul_null_node.uln_next;
652 	if (n == &lp->ul_null_node)
653 		return (NULL);
654 	return (NODE_TO_ELEM(lp, n));
655 }
656 
657 void *
658 uu_list_last(uu_list_t *lp)
659 {
660 	uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;
661 	if (n == &lp->ul_null_node)
662 		return (NULL);
663 	return (NODE_TO_ELEM(lp, n));
664 }
665 
666 void *
667 uu_list_next(uu_list_t *lp, void *elem)
668 {
669 	uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
670 
671 	n = n->uln_next;
672 	if (n == &lp->ul_null_node)
673 		return (NULL);
674 	return (NODE_TO_ELEM(lp, n));
675 }
676 
677 void *
678 uu_list_prev(uu_list_t *lp, void *elem)
679 {
680 	uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
681 
682 	n = n->uln_prev;
683 	if (n == &lp->ul_null_node)
684 		return (NULL);
685 	return (NODE_TO_ELEM(lp, n));
686 }
687 
688 /*
689  * called from uu_lockup() and uu_release(), as part of our fork1()-safety.
690  */
691 void
692 uu_list_lockup(void)
693 {
694 	uu_list_pool_t *pp;
695 
696 	(void) pthread_mutex_lock(&uu_lpool_list_lock);
697 	for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
698 	    pp = pp->ulp_next)
699 		(void) pthread_mutex_lock(&pp->ulp_lock);
700 }
701 
702 void
703 uu_list_release(void)
704 {
705 	uu_list_pool_t *pp;
706 
707 	for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
708 	    pp = pp->ulp_next)
709 		(void) pthread_mutex_unlock(&pp->ulp_lock);
710 	(void) pthread_mutex_unlock(&uu_lpool_list_lock);
711 }
712