Line data Source code
1 : /*
2 : * Copyright (c) 2010 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
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 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include "baselocl.h"
37 : #include <syslog.h>
38 :
39 : static heim_base_atomic_integer_type tidglobal = HEIM_TID_USER;
40 :
41 : struct heim_base {
42 : heim_type_t isa;
43 : heim_base_atomic_integer_type ref_cnt;
44 : HEIM_TAILQ_ENTRY(heim_base) autorel;
45 : heim_auto_release_t autorelpool;
46 : uintptr_t isaextra[3];
47 : };
48 :
49 : /* specialized version of base */
50 : struct heim_base_mem {
51 : heim_type_t isa;
52 : heim_base_atomic_integer_type ref_cnt;
53 : HEIM_TAILQ_ENTRY(heim_base) autorel;
54 : heim_auto_release_t autorelpool;
55 : const char *name;
56 : void (HEIM_CALLCONV *dealloc)(void *);
57 : uintptr_t isaextra[1];
58 : };
59 :
60 : #define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
61 : #define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
62 :
63 : #ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
64 : HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
65 : #endif
66 :
67 : /*
68 : * Auto release structure
69 : */
70 :
71 : struct heim_auto_release {
72 : HEIM_TAILQ_HEAD(, heim_base) pool;
73 : HEIMDAL_MUTEX pool_mutex;
74 : struct heim_auto_release *parent;
75 : };
76 :
77 :
78 : /**
79 : * Retain object (i.e., take a reference)
80 : *
81 : * @param object to be released, NULL is ok
82 : *
83 : * @return the same object as passed in
84 : */
85 :
86 : heim_object_t
87 5119592 : heim_retain(heim_object_t ptr)
88 : {
89 : struct heim_base *p;
90 :
91 5119592 : if (ptr == NULL || heim_base_is_tagged(ptr))
92 119505 : return ptr;
93 :
94 5000087 : p = PTR2BASE(ptr);
95 :
96 5000087 : if (heim_base_atomic_load(&p->ref_cnt) == heim_base_atomic_integer_max)
97 48333 : return ptr;
98 :
99 4951754 : if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
100 0 : heim_abort("resurection");
101 4951754 : return ptr;
102 : }
103 :
104 : /**
105 : * Release object, free if reference count reaches zero
106 : *
107 : * @param object to be released
108 : */
109 :
110 : void
111 10682350 : heim_release(void *ptr)
112 : {
113 : heim_base_atomic_integer_type old;
114 : struct heim_base *p;
115 :
116 10682350 : if (ptr == NULL || heim_base_is_tagged(ptr))
117 7156068 : return;
118 :
119 9580191 : p = PTR2BASE(ptr);
120 :
121 9580191 : if (heim_base_atomic_load(&p->ref_cnt) == heim_base_atomic_integer_max)
122 0 : return;
123 :
124 9580191 : old = heim_base_atomic_dec(&p->ref_cnt) + 1;
125 :
126 9580191 : if (old > 1)
127 4951750 : return;
128 :
129 4628441 : if (old == 1) {
130 4628441 : heim_auto_release_t ar = p->autorelpool;
131 : /* remove from autorel pool list */
132 4628441 : if (ar) {
133 0 : p->autorelpool = NULL;
134 : HEIMDAL_MUTEX_lock(&ar->pool_mutex);
135 0 : HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
136 : HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
137 : }
138 4628441 : if (p->isa->dealloc)
139 4628441 : p->isa->dealloc(ptr);
140 4628441 : free(p);
141 : } else
142 0 : heim_abort("over release");
143 : }
144 :
145 : /**
146 : * If used require wrapped in autorelease pool
147 : */
148 :
149 : heim_string_t
150 0 : heim_description(heim_object_t ptr)
151 : {
152 0 : struct heim_base *p = PTR2BASE(ptr);
153 0 : if (p->isa->desc == NULL)
154 0 : return heim_auto_release(heim_string_ref_create(p->isa->name, NULL));
155 0 : return heim_auto_release(p->isa->desc(ptr));
156 : }
157 :
158 :
159 : void
160 16111 : _heim_make_permanent(heim_object_t ptr)
161 : {
162 16111 : struct heim_base *p = PTR2BASE(ptr);
163 16111 : heim_base_atomic_store(&p->ref_cnt, heim_base_atomic_integer_max);
164 16111 : }
165 :
166 :
167 : static heim_type_t tagged_isa[9] = {
168 : &_heim_number_object,
169 : &_heim_null_object,
170 : &_heim_bool_object,
171 :
172 : NULL,
173 : NULL,
174 : NULL,
175 :
176 : NULL,
177 : NULL,
178 : NULL
179 : };
180 :
181 : heim_type_t
182 10166974 : _heim_get_isa(heim_object_t ptr)
183 : {
184 : struct heim_base *p;
185 10166974 : if (heim_base_is_tagged(ptr)) {
186 72741 : if (heim_base_is_tagged_object(ptr))
187 72741 : return tagged_isa[heim_base_tagged_object_tid(ptr)];
188 0 : heim_abort("not a supported tagged type");
189 : }
190 10094233 : p = PTR2BASE(ptr);
191 10094233 : return p->isa;
192 : }
193 :
194 : /**
195 : * Get type ID of object
196 : *
197 : * @param object object to get type id of
198 : *
199 : * @return type id of object
200 : */
201 :
202 : heim_tid_t
203 4444265 : heim_get_tid(heim_object_t ptr)
204 : {
205 4444265 : heim_type_t isa = _heim_get_isa(ptr);
206 4444265 : return isa->tid;
207 : }
208 :
209 : /**
210 : * Get hash value of object
211 : *
212 : * @param object object to get hash value for
213 : *
214 : * @return a hash value
215 : */
216 :
217 : uintptr_t
218 3801509 : heim_get_hash(heim_object_t ptr)
219 : {
220 3801509 : heim_type_t isa = _heim_get_isa(ptr);
221 3801509 : if (isa->hash)
222 3801509 : return isa->hash(ptr);
223 0 : return (uintptr_t)ptr;
224 : }
225 :
226 : /**
227 : * Compare two objects, returns 0 if equal, can use used for qsort()
228 : * and friends.
229 : *
230 : * @param a first object to compare
231 : * @param b first object to compare
232 : *
233 : * @return 0 if objects are equal
234 : */
235 :
236 : int
237 1921200 : heim_cmp(heim_object_t a, heim_object_t b)
238 : {
239 : heim_tid_t ta, tb;
240 : heim_type_t isa;
241 :
242 1921200 : ta = heim_get_tid(a);
243 1921200 : tb = heim_get_tid(b);
244 :
245 1921200 : if (ta != tb)
246 0 : return ta - tb;
247 :
248 1921200 : isa = _heim_get_isa(a);
249 :
250 1921200 : if (isa->cmp)
251 1921200 : return isa->cmp(a, b);
252 :
253 0 : return (uintptr_t)a - (uintptr_t)b;
254 : }
255 :
256 : /*
257 : * Private - allocates an memory object
258 : */
259 :
260 : static void HEIM_CALLCONV
261 124811 : memory_dealloc(void *ptr)
262 : {
263 124811 : struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
264 124811 : if (p->dealloc)
265 124811 : p->dealloc(ptr);
266 124811 : }
267 :
268 : struct heim_type_data memory_object = {
269 : HEIM_TID_MEMORY,
270 : "memory-object",
271 : NULL,
272 : memory_dealloc,
273 : NULL,
274 : NULL,
275 : NULL,
276 : NULL
277 : };
278 :
279 : /**
280 : * Allocate memory for an object of anonymous type
281 : *
282 : * @param size size of object to be allocated
283 : * @param name name of ad-hoc type
284 : * @param dealloc destructor function
285 : *
286 : * Objects allocated with this interface do not serialize.
287 : *
288 : * @return allocated object
289 : */
290 :
291 : void *
292 157175 : heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
293 : {
294 : /* XXX use posix_memalign */
295 :
296 157175 : struct heim_base_mem *p = calloc(1, size + sizeof(*p));
297 157175 : if (p == NULL)
298 0 : return NULL;
299 157175 : p->isa = &memory_object;
300 157175 : p->ref_cnt = 1;
301 157175 : p->name = name;
302 157175 : p->dealloc = dealloc;
303 157175 : return BASE2PTR(p);
304 : }
305 :
306 : heim_type_t
307 0 : _heim_create_type(const char *name,
308 : heim_type_init init,
309 : heim_type_dealloc dealloc,
310 : heim_type_copy copy,
311 : heim_type_cmp cmp,
312 : heim_type_hash hash,
313 : heim_type_description desc)
314 : {
315 : heim_type_t type;
316 :
317 0 : type = calloc(1, sizeof(*type));
318 0 : if (type == NULL)
319 0 : return NULL;
320 :
321 0 : type->tid = heim_base_atomic_inc(&tidglobal);
322 0 : type->name = name;
323 0 : type->init = init;
324 0 : type->dealloc = dealloc;
325 0 : type->copy = copy;
326 0 : type->cmp = cmp;
327 0 : type->hash = hash;
328 0 : type->desc = desc;
329 :
330 0 : return type;
331 : }
332 :
333 : heim_object_t
334 4663323 : _heim_alloc_object(heim_type_t type, size_t size)
335 : {
336 : /* XXX should use posix_memalign */
337 4663323 : struct heim_base *p = calloc(1, size + sizeof(*p));
338 4663323 : if (p == NULL)
339 0 : return NULL;
340 4663323 : p->isa = type;
341 4663323 : p->ref_cnt = 1;
342 :
343 4663323 : return BASE2PTR(p);
344 : }
345 :
346 : void *
347 2315413 : _heim_get_isaextra(heim_object_t ptr, size_t idx)
348 : {
349 : struct heim_base *p;
350 :
351 2315413 : heim_assert(ptr != NULL, "internal error");
352 2315413 : p = (struct heim_base *)PTR2BASE(ptr);
353 2315413 : if (p->isa == &memory_object)
354 0 : return NULL;
355 2315413 : heim_assert(idx < 3, "invalid private heim_base extra data index");
356 2315413 : return &p->isaextra[idx];
357 : }
358 :
359 : heim_tid_t
360 0 : _heim_type_get_tid(heim_type_t type)
361 : {
362 0 : return type->tid;
363 : }
364 :
365 : #if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
366 : static pthread_once_t once_arg_key_once = PTHREAD_ONCE_INIT;
367 : static pthread_key_t once_arg_key;
368 :
369 : static void
370 : once_arg_key_once_init(void)
371 : {
372 : errno = pthread_key_create(&once_arg_key, NULL);
373 : if (errno != 0) {
374 : fprintf(stderr,
375 : "Error: pthread_key_create() failed, cannot continue: %s\n",
376 : strerror(errno));
377 : abort();
378 : }
379 : }
380 :
381 : struct once_callback {
382 : void (*fn)(void *);
383 : void *data;
384 : };
385 :
386 : static void
387 : once_callback_caller(void)
388 : {
389 : struct once_callback *once_callback = pthread_getspecific(once_arg_key);
390 :
391 : if (once_callback == NULL) {
392 : fprintf(stderr, "Error: pthread_once() calls callback on "
393 : "different thread?! Cannot continue.\n");
394 : abort();
395 : }
396 : once_callback->fn(once_callback->data);
397 : }
398 : #endif
399 :
400 : /**
401 : * Call func once and only once
402 : *
403 : * @param once pointer to a heim_base_once_t
404 : * @param ctx context passed to func
405 : * @param func function to be called
406 : */
407 :
408 : void
409 2443004 : heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
410 : {
411 : #if defined(WIN32)
412 : /*
413 : * With a libroken wrapper for some CAS function and a libroken yield()
414 : * wrapper we could make this the default implementation when we have
415 : * neither Grand Central nor POSX threads.
416 : *
417 : * We could also adapt the double-checked lock pattern with CAS
418 : * providing the necessary memory barriers in the absence of
419 : * portable explicit memory barrier APIs.
420 : */
421 : /*
422 : * We use CAS operations in large part to provide implied memory
423 : * barriers.
424 : *
425 : * State 0 means that func() has never executed.
426 : * State 1 means that func() is executing.
427 : * State 2 means that func() has completed execution.
428 : */
429 : if (InterlockedCompareExchange(once, 1L, 0L) == 0L) {
430 : /* State is now 1 */
431 : (*func)(ctx);
432 : (void)InterlockedExchange(once, 2L);
433 : /* State is now 2 */
434 : } else {
435 : /*
436 : * The InterlockedCompareExchange is being used to fetch
437 : * the current state under a full memory barrier. As long
438 : * as the current state is 1 continue to spin.
439 : */
440 : while (InterlockedCompareExchange(once, 2L, 0L) == 1L)
441 : SwitchToThread();
442 : }
443 : #elif defined(HAVE_DISPATCH_DISPATCH_H)
444 : dispatch_once_f(once, ctx, func);
445 : #elif defined(ENABLE_PTHREAD_SUPPORT)
446 : struct once_callback once_callback;
447 :
448 : once_callback.fn = func;
449 : once_callback.data = ctx;
450 :
451 : errno = pthread_once(&once_arg_key_once, once_arg_key_once_init);
452 : if (errno != 0) {
453 : fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
454 : strerror(errno));
455 : abort();
456 : }
457 : errno = pthread_setspecific(once_arg_key, &once_callback);
458 : if (errno != 0) {
459 : fprintf(stderr,
460 : "Error: pthread_setspecific() failed, cannot continue: %s\n",
461 : strerror(errno));
462 : abort();
463 : }
464 : errno = pthread_once(once, once_callback_caller);
465 : if (errno != 0) {
466 : fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
467 : strerror(errno));
468 : abort();
469 : }
470 : #else
471 : static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
472 : HEIMDAL_MUTEX_lock(&mutex);
473 2443004 : if (*once == 0) {
474 78316 : *once = 1;
475 : HEIMDAL_MUTEX_unlock(&mutex);
476 78316 : func(ctx);
477 : HEIMDAL_MUTEX_lock(&mutex);
478 78316 : *once = 2;
479 : HEIMDAL_MUTEX_unlock(&mutex);
480 2364688 : } else if (*once == 2) {
481 : HEIMDAL_MUTEX_unlock(&mutex);
482 : } else {
483 : HEIMDAL_MUTEX_unlock(&mutex);
484 0 : while (1) {
485 0 : struct timeval tv = { 0, 1000 };
486 0 : select(0, NULL, NULL, NULL, &tv);
487 : HEIMDAL_MUTEX_lock(&mutex);
488 0 : if (*once == 2)
489 0 : break;
490 : HEIMDAL_MUTEX_unlock(&mutex);
491 : }
492 : HEIMDAL_MUTEX_unlock(&mutex);
493 : }
494 : #endif
495 2443004 : }
496 :
497 : /**
498 : * Abort and log the failure (using syslog)
499 : */
500 :
501 : void
502 0 : heim_abort(const char *fmt, ...)
503 : {
504 : va_list ap;
505 0 : va_start(ap, fmt);
506 0 : heim_abortv(fmt, ap);
507 : va_end(ap);
508 : }
509 :
510 : /**
511 : * Abort and log the failure (using syslog)
512 : */
513 :
514 : void
515 0 : heim_abortv(const char *fmt, va_list ap)
516 : {
517 : static char str[1024];
518 :
519 0 : vsnprintf(str, sizeof(str), fmt, ap);
520 0 : syslog(LOG_ERR, "heim_abort: %s", str);
521 0 : abort();
522 : }
523 :
524 : /*
525 : *
526 : */
527 :
528 : static int ar_created = 0;
529 : static HEIMDAL_thread_key ar_key;
530 :
531 : struct ar_tls {
532 : struct heim_auto_release *head;
533 : struct heim_auto_release *current;
534 : HEIMDAL_MUTEX tls_mutex;
535 : };
536 :
537 : static void
538 0 : ar_tls_delete(void *ptr)
539 : {
540 0 : struct ar_tls *tls = ptr;
541 0 : heim_auto_release_t next = NULL;
542 :
543 0 : if (tls == NULL)
544 0 : return;
545 0 : for (; tls->current != NULL; tls->current = next) {
546 0 : next = tls->current->parent;
547 0 : heim_release(tls->current);
548 : }
549 0 : free(tls);
550 : }
551 :
552 : static void
553 0 : init_ar_tls(void *ptr)
554 : {
555 : int ret;
556 0 : HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
557 0 : if (ret == 0)
558 0 : ar_created = 1;
559 0 : }
560 :
561 : static struct ar_tls *
562 0 : autorel_tls(void)
563 : {
564 : static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
565 : struct ar_tls *arp;
566 : int ret;
567 :
568 0 : heim_base_once_f(&once, NULL, init_ar_tls);
569 0 : if (!ar_created)
570 0 : return NULL;
571 :
572 0 : arp = HEIMDAL_getspecific(ar_key);
573 0 : if (arp == NULL) {
574 :
575 0 : arp = calloc(1, sizeof(*arp));
576 0 : if (arp == NULL)
577 0 : return NULL;
578 0 : HEIMDAL_setspecific(ar_key, arp, ret);
579 0 : if (ret) {
580 0 : free(arp);
581 0 : return NULL;
582 : }
583 : }
584 0 : return arp;
585 :
586 : }
587 :
588 : static void HEIM_CALLCONV
589 0 : autorel_dealloc(void *ptr)
590 : {
591 0 : heim_auto_release_t ar = ptr;
592 : struct ar_tls *tls;
593 :
594 0 : tls = autorel_tls();
595 0 : if (tls == NULL)
596 0 : heim_abort("autorelease pool released on thread w/o autorelease inited");
597 :
598 0 : heim_auto_release_drain(ar);
599 :
600 0 : if (!HEIM_TAILQ_EMPTY(&ar->pool))
601 0 : heim_abort("pool not empty after draining");
602 :
603 : HEIMDAL_MUTEX_lock(&tls->tls_mutex);
604 0 : if (tls->current != ptr)
605 0 : heim_abort("autorelease not releaseing top pool");
606 :
607 0 : tls->current = ar->parent;
608 : HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
609 0 : }
610 :
611 : static int
612 0 : autorel_cmp(void *a, void *b)
613 : {
614 0 : return (a == b);
615 : }
616 :
617 : static uintptr_t
618 0 : autorel_hash(void *ptr)
619 : {
620 0 : return (uintptr_t)ptr;
621 : }
622 :
623 :
624 : static struct heim_type_data _heim_autorel_object = {
625 : HEIM_TID_AUTORELEASE,
626 : "autorelease-pool",
627 : NULL,
628 : autorel_dealloc,
629 : NULL,
630 : autorel_cmp,
631 : autorel_hash,
632 : NULL
633 : };
634 :
635 : /**
636 : * Create thread-specific object auto-release pool
637 : *
638 : * Objects placed on the per-thread auto-release pool (with
639 : * heim_auto_release()) can be released in one fell swoop by calling
640 : * heim_auto_release_drain().
641 : */
642 :
643 : heim_auto_release_t
644 0 : heim_auto_release_create(void)
645 : {
646 0 : struct ar_tls *tls = autorel_tls();
647 : heim_auto_release_t ar;
648 :
649 0 : if (tls == NULL)
650 0 : heim_abort("Failed to create/get autorelease head");
651 :
652 0 : ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release));
653 0 : if (ar) {
654 : HEIMDAL_MUTEX_lock(&tls->tls_mutex);
655 0 : if (tls->head == NULL)
656 0 : tls->head = ar;
657 0 : ar->parent = tls->current;
658 0 : tls->current = ar;
659 : HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
660 : }
661 :
662 0 : return ar;
663 : }
664 :
665 : /**
666 : * Place the current object on the thread's auto-release pool
667 : *
668 : * @param ptr object
669 : */
670 :
671 : heim_object_t
672 0 : heim_auto_release(heim_object_t ptr)
673 : {
674 : struct heim_base *p;
675 0 : struct ar_tls *tls = autorel_tls();
676 : heim_auto_release_t ar;
677 :
678 0 : if (ptr == NULL || heim_base_is_tagged(ptr))
679 0 : return ptr;
680 :
681 0 : p = PTR2BASE(ptr);
682 :
683 : /* drop from old pool */
684 0 : if ((ar = p->autorelpool) != NULL) {
685 : HEIMDAL_MUTEX_lock(&ar->pool_mutex);
686 0 : HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
687 0 : p->autorelpool = NULL;
688 : HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
689 : }
690 :
691 0 : if (tls == NULL || (ar = tls->current) == NULL)
692 0 : heim_abort("no auto relase pool in place, would leak");
693 :
694 : HEIMDAL_MUTEX_lock(&ar->pool_mutex);
695 0 : HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel);
696 0 : p->autorelpool = ar;
697 : HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
698 :
699 0 : return ptr;
700 : }
701 :
702 : /**
703 : * Release all objects on the given auto-release pool
704 : */
705 :
706 : void
707 0 : heim_auto_release_drain(heim_auto_release_t autorel)
708 : {
709 : heim_object_t obj;
710 :
711 : /* release all elements on the tail queue */
712 :
713 : HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
714 0 : while(!HEIM_TAILQ_EMPTY(&autorel->pool)) {
715 0 : obj = HEIM_TAILQ_FIRST(&autorel->pool);
716 : HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
717 0 : heim_release(BASE2PTR(obj));
718 : HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
719 : }
720 : HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
721 0 : }
722 :
723 : /*
724 : * Helper for heim_path_vget() and heim_path_delete(). On success
725 : * outputs the node named by the path and the parent node and key
726 : * (useful for heim_path_delete()).
727 : */
728 :
729 : static heim_object_t
730 0 : heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
731 : heim_error_t *error, va_list ap)
732 : {
733 : heim_object_t path_element;
734 : heim_object_t node, next_node;
735 : heim_tid_t node_type;
736 :
737 0 : *parent = NULL;
738 0 : *key = NULL;
739 0 : if (ptr == NULL)
740 0 : return NULL;
741 :
742 0 : for (node = ptr; node != NULL; ) {
743 0 : path_element = va_arg(ap, heim_object_t);
744 0 : if (path_element == NULL) {
745 0 : *parent = node;
746 0 : *key = path_element;
747 0 : return node;
748 : }
749 :
750 0 : node_type = heim_get_tid(node);
751 0 : switch (node_type) {
752 0 : case HEIM_TID_ARRAY:
753 : case HEIM_TID_DICT:
754 : case HEIM_TID_DB:
755 0 : break;
756 0 : default:
757 0 : if (node == ptr)
758 0 : heim_abort("heim_path_get() only operates on container types");
759 0 : return NULL;
760 : }
761 :
762 0 : if (node_type == HEIM_TID_DICT) {
763 0 : next_node = heim_dict_get_value(node, path_element);
764 0 : } else if (node_type == HEIM_TID_DB) {
765 0 : next_node = _heim_db_get_value(node, NULL, path_element, NULL);
766 : } else {
767 0 : int idx = -1;
768 :
769 : /* node_type == HEIM_TID_ARRAY */
770 0 : if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
771 0 : idx = heim_number_get_int(path_element);
772 0 : if (idx < 0) {
773 0 : if (error)
774 0 : *error = heim_error_create(EINVAL,
775 : "heim_path_get() path elements "
776 : "for array nodes must be "
777 : "numeric and positive");
778 0 : return NULL;
779 : }
780 0 : next_node = heim_array_get_value(node, idx);
781 : }
782 0 : node = next_node;
783 : }
784 0 : return NULL;
785 : }
786 :
787 : /**
788 : * Get a node in a heim_object tree by path
789 : *
790 : * @param ptr tree
791 : * @param error error (output)
792 : * @param ap NULL-terminated va_list of heim_object_ts that form a path
793 : *
794 : * @return object (not retained) if found
795 : *
796 : * @addtogroup heimbase
797 : */
798 :
799 : heim_object_t
800 0 : heim_path_vget(heim_object_t ptr, heim_error_t *error, va_list ap)
801 : {
802 : heim_object_t p, k;
803 :
804 0 : return heim_path_vget2(ptr, &p, &k, error, ap);
805 : }
806 :
807 : /**
808 : * Get a node in a tree by path, with retained reference
809 : *
810 : * @param ptr tree
811 : * @param error error (output)
812 : * @param ap NULL-terminated va_list of heim_object_ts that form a path
813 : *
814 : * @return retained object if found
815 : *
816 : * @addtogroup heimbase
817 : */
818 :
819 : heim_object_t
820 0 : heim_path_vcopy(heim_object_t ptr, heim_error_t *error, va_list ap)
821 : {
822 : heim_object_t p, k;
823 :
824 0 : return heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
825 : }
826 :
827 : /**
828 : * Get a node in a tree by path
829 : *
830 : * @param ptr tree
831 : * @param error error (output)
832 : * @param ... NULL-terminated va_list of heim_object_ts that form a path
833 : *
834 : * @return object (not retained) if found
835 : *
836 : * @addtogroup heimbase
837 : */
838 :
839 : heim_object_t
840 0 : heim_path_get(heim_object_t ptr, heim_error_t *error, ...)
841 : {
842 : heim_object_t o;
843 : heim_object_t p, k;
844 : va_list ap;
845 :
846 0 : if (ptr == NULL)
847 0 : return NULL;
848 :
849 0 : va_start(ap, error);
850 0 : o = heim_path_vget2(ptr, &p, &k, error, ap);
851 0 : va_end(ap);
852 0 : return o;
853 : }
854 :
855 : /**
856 : * Get a node in a tree by path, with retained reference
857 : *
858 : * @param ptr tree
859 : * @param error error (output)
860 : * @param ... NULL-terminated va_list of heim_object_ts that form a path
861 : *
862 : * @return retained object if found
863 : *
864 : * @addtogroup heimbase
865 : */
866 :
867 : heim_object_t
868 0 : heim_path_copy(heim_object_t ptr, heim_error_t *error, ...)
869 : {
870 : heim_object_t o;
871 : heim_object_t p, k;
872 : va_list ap;
873 :
874 0 : if (ptr == NULL)
875 0 : return NULL;
876 :
877 0 : va_start(ap, error);
878 0 : o = heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
879 0 : va_end(ap);
880 0 : return o;
881 : }
882 :
883 : /**
884 : * Create a path in a heim_object_t tree
885 : *
886 : * @param ptr the tree
887 : * @param size the size of the heim_dict_t nodes to be created
888 : * @param leaf leaf node to be added, if any
889 : * @param error error (output)
890 : * @param ap NULL-terminated of path component objects
891 : *
892 : * Create a path of heim_dict_t interior nodes in a given heim_object_t
893 : * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
894 : * then the leaf is not deleted).
895 : *
896 : * @return 0 on success, else a system error
897 : *
898 : * @addtogroup heimbase
899 : */
900 :
901 : int
902 0 : heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
903 : heim_error_t *error, va_list ap)
904 : {
905 0 : heim_object_t path_element = va_arg(ap, heim_object_t);
906 0 : heim_object_t next_path_element = NULL;
907 0 : heim_object_t node = ptr;
908 0 : heim_object_t next_node = NULL;
909 : heim_tid_t node_type;
910 0 : int ret = 0;
911 :
912 0 : if (ptr == NULL)
913 0 : heim_abort("heim_path_vcreate() does not create root nodes");
914 :
915 0 : while (path_element != NULL) {
916 0 : next_path_element = va_arg(ap, heim_object_t);
917 0 : node_type = heim_get_tid(node);
918 :
919 0 : if (node_type == HEIM_TID_DICT) {
920 0 : next_node = heim_dict_get_value(node, path_element);
921 0 : } else if (node_type == HEIM_TID_ARRAY) {
922 0 : int idx = -1;
923 :
924 0 : if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
925 0 : idx = heim_number_get_int(path_element);
926 0 : if (idx < 0) {
927 0 : if (error)
928 0 : *error = heim_error_create(EINVAL,
929 : "heim_path() path elements for "
930 : "array nodes must be numeric "
931 : "and positive");
932 0 : return EINVAL;
933 : }
934 0 : if (idx < heim_array_get_length(node))
935 0 : next_node = heim_array_get_value(node, idx);
936 : else
937 0 : next_node = NULL;
938 0 : } else if (node_type == HEIM_TID_DB && next_path_element != NULL) {
939 0 : if (error)
940 0 : *error = heim_error_create(EINVAL, "Interior node is a DB");
941 0 : return EINVAL;
942 : }
943 :
944 0 : if (next_path_element == NULL)
945 0 : break;
946 :
947 : /* Create missing interior node */
948 0 : if (next_node == NULL) {
949 0 : next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
950 0 : if (next_node == NULL) {
951 0 : ret = ENOMEM;
952 0 : goto err;
953 : }
954 :
955 0 : if (node_type == HEIM_TID_DICT) {
956 0 : ret = heim_dict_set_value(node, path_element, next_node);
957 0 : } else if (node_type == HEIM_TID_ARRAY &&
958 0 : heim_number_get_int(path_element) <= heim_array_get_length(node)) {
959 0 : ret = heim_array_insert_value(node,
960 0 : heim_number_get_int(path_element),
961 : next_node);
962 : } else {
963 0 : ret = EINVAL;
964 0 : if (error)
965 0 : *error = heim_error_create(ret, "Node in path not a "
966 : "container");
967 : }
968 0 : heim_release(next_node);
969 0 : if (ret)
970 0 : goto err;
971 : }
972 :
973 0 : path_element = next_path_element;
974 0 : node = next_node;
975 0 : next_node = NULL;
976 : }
977 :
978 0 : if (path_element == NULL)
979 0 : goto err;
980 :
981 : /* Add the leaf */
982 0 : if (leaf != NULL) {
983 0 : if (node_type == HEIM_TID_DICT)
984 0 : ret = heim_dict_set_value(node, path_element, leaf);
985 : else
986 0 : ret = heim_array_insert_value(node,
987 0 : heim_number_get_int(path_element),
988 : leaf);
989 : }
990 0 : return ret;
991 :
992 0 : err:
993 0 : if (error && !*error) {
994 0 : if (ret == ENOMEM)
995 0 : *error = heim_error_create_enomem();
996 : else
997 0 : *error = heim_error_create(ret, "Could not set "
998 : "dict value");
999 : }
1000 0 : return ret;
1001 : }
1002 :
1003 : /**
1004 : * Create a path in a heim_object_t tree
1005 : *
1006 : * @param ptr the tree
1007 : * @param size the size of the heim_dict_t nodes to be created
1008 : * @param leaf leaf node to be added, if any
1009 : * @param error error (output)
1010 : * @param ... NULL-terminated list of path component objects
1011 : *
1012 : * Create a path of heim_dict_t interior nodes in a given heim_object_t
1013 : * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
1014 : * then the leaf is not deleted).
1015 : *
1016 : * @return 0 on success, else a system error
1017 : *
1018 : * @addtogroup heimbase
1019 : */
1020 :
1021 : int
1022 0 : heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
1023 : heim_error_t *error, ...)
1024 : {
1025 : va_list ap;
1026 : int ret;
1027 :
1028 0 : va_start(ap, error);
1029 0 : ret = heim_path_vcreate(ptr, size, leaf, error, ap);
1030 0 : va_end(ap);
1031 0 : return ret;
1032 : }
1033 :
1034 : /**
1035 : * Delete leaf node named by a path in a heim_object_t tree
1036 : *
1037 : * @param ptr the tree
1038 : * @param error error (output)
1039 : * @param ap NULL-terminated list of path component objects
1040 : *
1041 : * @addtogroup heimbase
1042 : */
1043 :
1044 : void
1045 0 : heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap)
1046 : {
1047 : heim_object_t parent, key, child;
1048 :
1049 0 : child = heim_path_vget2(ptr, &parent, &key, error, ap);
1050 0 : if (child != NULL) {
1051 0 : if (heim_get_tid(parent) == HEIM_TID_DICT)
1052 0 : heim_dict_delete_key(parent, key);
1053 0 : else if (heim_get_tid(parent) == HEIM_TID_DB)
1054 0 : heim_db_delete_key(parent, NULL, key, error);
1055 0 : else if (heim_get_tid(parent) == HEIM_TID_ARRAY)
1056 0 : heim_array_delete_value(parent, heim_number_get_int(key));
1057 0 : heim_release(child);
1058 : }
1059 0 : }
1060 :
1061 : /**
1062 : * Delete leaf node named by a path in a heim_object_t tree
1063 : *
1064 : * @param ptr the tree
1065 : * @param error error (output)
1066 : * @param ap NULL-terminated list of path component objects
1067 : *
1068 : * @addtogroup heimbase
1069 : */
1070 :
1071 : void
1072 0 : heim_path_delete(heim_object_t ptr, heim_error_t *error, ...)
1073 : {
1074 : va_list ap;
1075 :
1076 0 : va_start(ap, error);
1077 0 : heim_path_vdelete(ptr, error, ap);
1078 0 : va_end(ap);
1079 0 : return;
1080 : }
1081 :
|