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