LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/base - heimbase.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 93 401 23.2 %
Date: 2024-04-21 15:09:00 Functions: 12 36 33.3 %

          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             : 

Generated by: LCOV version 1.14