1226031Sstas/* 2226031Sstas * Copyright (c) 2010 Kungliga Tekniska H��gskolan 3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7226031Sstas * 8226031Sstas * Redistribution and use in source and binary forms, with or without 9226031Sstas * modification, are permitted provided that the following conditions 10226031Sstas * are met: 11226031Sstas * 12226031Sstas * 1. Redistributions of source code must retain the above copyright 13226031Sstas * notice, this list of conditions and the following disclaimer. 14226031Sstas * 15226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 16226031Sstas * notice, this list of conditions and the following disclaimer in the 17226031Sstas * documentation and/or other materials provided with the distribution. 18226031Sstas * 19226031Sstas * 3. Neither the name of the Institute nor the names of its contributors 20226031Sstas * may be used to endorse or promote products derived from this software 21226031Sstas * without specific prior written permission. 22226031Sstas * 23226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33226031Sstas * SUCH DAMAGE. 34226031Sstas */ 35226031Sstas 36226031Sstas#include "baselocl.h" 37226031Sstas#include <syslog.h> 38226031Sstas 39226031Sstasstatic heim_base_atomic_type tidglobal = HEIM_TID_USER; 40226031Sstas 41226031Sstasstruct heim_base { 42226031Sstas heim_type_t isa; 43226031Sstas heim_base_atomic_type ref_cnt; 44226031Sstas HEIM_TAILQ_ENTRY(heim_base) autorel; 45226031Sstas heim_auto_release_t autorelpool; 46226031Sstas uintptr_t isaextra[3]; 47226031Sstas}; 48226031Sstas 49226031Sstas/* specialized version of base */ 50226031Sstasstruct heim_base_mem { 51226031Sstas heim_type_t isa; 52226031Sstas heim_base_atomic_type ref_cnt; 53226031Sstas HEIM_TAILQ_ENTRY(heim_base) autorel; 54226031Sstas heim_auto_release_t autorelpool; 55226031Sstas const char *name; 56226031Sstas void (*dealloc)(void *); 57226031Sstas uintptr_t isaextra[1]; 58226031Sstas}; 59226031Sstas 60226031Sstas#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1) 61226031Sstas#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1)) 62226031Sstas 63226031Sstas#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX 64226031SstasHEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; 65226031Sstas#endif 66226031Sstas 67226031Sstas/* 68226031Sstas * Auto release structure 69226031Sstas */ 70226031Sstas 71226031Sstasstruct heim_auto_release { 72226031Sstas HEIM_TAILQ_HEAD(, heim_base) pool; 73226031Sstas HEIMDAL_MUTEX pool_mutex; 74226031Sstas struct heim_auto_release *parent; 75226031Sstas}; 76226031Sstas 77226031Sstas 78226031Sstas/** 79226031Sstas * Retain object 80226031Sstas * 81226031Sstas * @param object to be released, NULL is ok 82226031Sstas * 83226031Sstas * @return the same object as passed in 84226031Sstas */ 85226031Sstas 86226031Sstasvoid * 87226031Sstasheim_retain(void *ptr) 88226031Sstas{ 89226031Sstas struct heim_base *p = PTR2BASE(ptr); 90226031Sstas 91226031Sstas if (ptr == NULL || heim_base_is_tagged(ptr)) 92226031Sstas return ptr; 93226031Sstas 94226031Sstas if (p->ref_cnt == heim_base_atomic_max) 95226031Sstas return ptr; 96226031Sstas 97226031Sstas if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0) 98226031Sstas heim_abort("resurection"); 99226031Sstas return ptr; 100226031Sstas} 101226031Sstas 102226031Sstas/** 103226031Sstas * Release object, free is reference count reaches zero 104226031Sstas * 105226031Sstas * @param object to be released 106226031Sstas */ 107226031Sstas 108226031Sstasvoid 109226031Sstasheim_release(void *ptr) 110226031Sstas{ 111226031Sstas heim_base_atomic_type old; 112226031Sstas struct heim_base *p = PTR2BASE(ptr); 113226031Sstas 114226031Sstas if (ptr == NULL || heim_base_is_tagged(ptr)) 115226031Sstas return; 116226031Sstas 117226031Sstas if (p->ref_cnt == heim_base_atomic_max) 118226031Sstas return; 119226031Sstas 120226031Sstas old = heim_base_atomic_dec(&p->ref_cnt) + 1; 121226031Sstas 122226031Sstas if (old > 1) 123226031Sstas return; 124226031Sstas 125226031Sstas if (old == 1) { 126226031Sstas heim_auto_release_t ar = p->autorelpool; 127226031Sstas /* remove from autorel pool list */ 128226031Sstas if (ar) { 129226031Sstas p->autorelpool = NULL; 130226031Sstas HEIMDAL_MUTEX_lock(&ar->pool_mutex); 131226031Sstas HEIM_TAILQ_REMOVE(&ar->pool, p, autorel); 132226031Sstas HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 133226031Sstas } 134226031Sstas if (p->isa->dealloc) 135226031Sstas p->isa->dealloc(ptr); 136226031Sstas free(p); 137226031Sstas } else 138226031Sstas heim_abort("over release"); 139226031Sstas} 140226031Sstas 141226031Sstasstatic heim_type_t tagged_isa[9] = { 142226031Sstas &_heim_number_object, 143226031Sstas &_heim_null_object, 144226031Sstas &_heim_bool_object, 145226031Sstas 146226031Sstas NULL, 147226031Sstas NULL, 148226031Sstas NULL, 149226031Sstas 150226031Sstas NULL, 151226031Sstas NULL, 152226031Sstas NULL 153226031Sstas}; 154226031Sstas 155226031Sstasheim_type_t 156226031Sstas_heim_get_isa(heim_object_t ptr) 157226031Sstas{ 158226031Sstas struct heim_base *p; 159226031Sstas if (heim_base_is_tagged(ptr)) { 160226031Sstas if (heim_base_is_tagged_object(ptr)) 161226031Sstas return tagged_isa[heim_base_tagged_object_tid(ptr)]; 162226031Sstas heim_abort("not a supported tagged type"); 163226031Sstas } 164226031Sstas p = PTR2BASE(ptr); 165226031Sstas return p->isa; 166226031Sstas} 167226031Sstas 168226031Sstas/** 169226031Sstas * Get type ID of object 170226031Sstas * 171226031Sstas * @param object object to get type id of 172226031Sstas * 173226031Sstas * @return type id of object 174226031Sstas */ 175226031Sstas 176226031Sstasheim_tid_t 177226031Sstasheim_get_tid(heim_object_t ptr) 178226031Sstas{ 179226031Sstas heim_type_t isa = _heim_get_isa(ptr); 180226031Sstas return isa->tid; 181226031Sstas} 182226031Sstas 183226031Sstas/** 184226031Sstas * Get hash value of object 185226031Sstas * 186226031Sstas * @param object object to get hash value for 187226031Sstas * 188226031Sstas * @return a hash value 189226031Sstas */ 190226031Sstas 191226031Sstasunsigned long 192226031Sstasheim_get_hash(heim_object_t ptr) 193226031Sstas{ 194226031Sstas heim_type_t isa = _heim_get_isa(ptr); 195226031Sstas if (isa->hash) 196226031Sstas return isa->hash(ptr); 197226031Sstas return (unsigned long)ptr; 198226031Sstas} 199226031Sstas 200226031Sstas/** 201226031Sstas * Compare two objects, returns 0 if equal, can use used for qsort() 202226031Sstas * and friends. 203226031Sstas * 204226031Sstas * @param a first object to compare 205226031Sstas * @param b first object to compare 206226031Sstas * 207226031Sstas * @return 0 if objects are equal 208226031Sstas */ 209226031Sstas 210226031Sstasint 211226031Sstasheim_cmp(heim_object_t a, heim_object_t b) 212226031Sstas{ 213226031Sstas heim_tid_t ta, tb; 214226031Sstas heim_type_t isa; 215226031Sstas 216226031Sstas ta = heim_get_tid(a); 217226031Sstas tb = heim_get_tid(b); 218226031Sstas 219226031Sstas if (ta != tb) 220226031Sstas return ta - tb; 221226031Sstas 222226031Sstas isa = _heim_get_isa(a); 223226031Sstas 224226031Sstas if (isa->cmp) 225226031Sstas return isa->cmp(a, b); 226226031Sstas 227226031Sstas return (uintptr_t)a - (uintptr_t)b; 228226031Sstas} 229226031Sstas 230226031Sstas/* 231226031Sstas * Private - allocates an memory object 232226031Sstas */ 233226031Sstas 234226031Sstasstatic void 235226031Sstasmemory_dealloc(void *ptr) 236226031Sstas{ 237226031Sstas struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr); 238226031Sstas if (p->dealloc) 239226031Sstas p->dealloc(ptr); 240226031Sstas} 241226031Sstas 242226031Sstasstruct heim_type_data memory_object = { 243226031Sstas HEIM_TID_MEMORY, 244226031Sstas "memory-object", 245226031Sstas NULL, 246226031Sstas memory_dealloc, 247226031Sstas NULL, 248226031Sstas NULL, 249226031Sstas NULL 250226031Sstas}; 251226031Sstas 252226031Sstasvoid * 253226031Sstasheim_alloc(size_t size, const char *name, heim_type_dealloc dealloc) 254226031Sstas{ 255226031Sstas /* XXX use posix_memalign */ 256226031Sstas 257226031Sstas struct heim_base_mem *p = calloc(1, size + sizeof(*p)); 258226031Sstas if (p == NULL) 259226031Sstas return NULL; 260226031Sstas p->isa = &memory_object; 261226031Sstas p->ref_cnt = 1; 262226031Sstas p->name = name; 263226031Sstas p->dealloc = dealloc; 264226031Sstas return BASE2PTR(p); 265226031Sstas} 266226031Sstas 267226031Sstasheim_type_t 268226031Sstas_heim_create_type(const char *name, 269226031Sstas heim_type_init init, 270226031Sstas heim_type_dealloc dealloc, 271226031Sstas heim_type_copy copy, 272226031Sstas heim_type_cmp cmp, 273226031Sstas heim_type_hash hash) 274226031Sstas{ 275226031Sstas heim_type_t type; 276226031Sstas 277226031Sstas type = calloc(1, sizeof(*type)); 278226031Sstas if (type == NULL) 279226031Sstas return NULL; 280226031Sstas 281226031Sstas type->tid = heim_base_atomic_inc(&tidglobal); 282226031Sstas type->name = name; 283226031Sstas type->init = init; 284226031Sstas type->dealloc = dealloc; 285226031Sstas type->copy = copy; 286226031Sstas type->cmp = cmp; 287226031Sstas type->hash = hash; 288226031Sstas 289226031Sstas return type; 290226031Sstas} 291226031Sstas 292226031Sstasheim_object_t 293226031Sstas_heim_alloc_object(heim_type_t type, size_t size) 294226031Sstas{ 295226031Sstas /* XXX should use posix_memalign */ 296226031Sstas struct heim_base *p = calloc(1, size + sizeof(*p)); 297226031Sstas if (p == NULL) 298226031Sstas return NULL; 299226031Sstas p->isa = type; 300226031Sstas p->ref_cnt = 1; 301226031Sstas 302226031Sstas return BASE2PTR(p); 303226031Sstas} 304226031Sstas 305226031Sstasheim_tid_t 306226031Sstas_heim_type_get_tid(heim_type_t type) 307226031Sstas{ 308226031Sstas return type->tid; 309226031Sstas} 310226031Sstas 311226031Sstas/** 312226031Sstas * Call func once and only once 313226031Sstas * 314226031Sstas * @param once pointer to a heim_base_once_t 315226031Sstas * @param ctx context passed to func 316226031Sstas * @param func function to be called 317226031Sstas */ 318226031Sstas 319226031Sstasvoid 320226031Sstasheim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *)) 321226031Sstas{ 322226031Sstas#ifdef HAVE_DISPATCH_DISPATCH_H 323226031Sstas dispatch_once_f(once, ctx, func); 324226031Sstas#else 325226031Sstas static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER; 326226031Sstas HEIMDAL_MUTEX_lock(&mutex); 327226031Sstas if (*once == 0) { 328226031Sstas *once = 1; 329226031Sstas HEIMDAL_MUTEX_unlock(&mutex); 330226031Sstas func(ctx); 331226031Sstas HEIMDAL_MUTEX_lock(&mutex); 332226031Sstas *once = 2; 333226031Sstas HEIMDAL_MUTEX_unlock(&mutex); 334226031Sstas } else if (*once == 2) { 335226031Sstas HEIMDAL_MUTEX_unlock(&mutex); 336226031Sstas } else { 337226031Sstas HEIMDAL_MUTEX_unlock(&mutex); 338226031Sstas while (1) { 339226031Sstas struct timeval tv = { 0, 1000 }; 340226031Sstas select(0, NULL, NULL, NULL, &tv); 341226031Sstas HEIMDAL_MUTEX_lock(&mutex); 342226031Sstas if (*once == 2) 343226031Sstas break; 344226031Sstas HEIMDAL_MUTEX_unlock(&mutex); 345226031Sstas } 346226031Sstas HEIMDAL_MUTEX_unlock(&mutex); 347226031Sstas } 348226031Sstas#endif 349226031Sstas} 350226031Sstas 351226031Sstas/** 352226031Sstas * Abort and log the failure (using syslog) 353226031Sstas */ 354226031Sstas 355226031Sstasvoid 356226031Sstasheim_abort(const char *fmt, ...) 357226031Sstas{ 358226031Sstas va_list ap; 359226031Sstas va_start(ap, fmt); 360226031Sstas heim_abortv(fmt, ap); 361226031Sstas va_end(ap); 362226031Sstas} 363226031Sstas 364226031Sstas/** 365226031Sstas * Abort and log the failure (using syslog) 366226031Sstas */ 367226031Sstas 368226031Sstasvoid 369226031Sstasheim_abortv(const char *fmt, va_list ap) 370226031Sstas{ 371226031Sstas static char str[1024]; 372226031Sstas 373226031Sstas vsnprintf(str, sizeof(str), fmt, ap); 374226031Sstas syslog(LOG_ERR, "heim_abort: %s", str); 375226031Sstas abort(); 376226031Sstas} 377226031Sstas 378226031Sstas/* 379226031Sstas * 380226031Sstas */ 381226031Sstas 382226031Sstasstatic int ar_created = 0; 383226031Sstasstatic HEIMDAL_thread_key ar_key; 384226031Sstas 385226031Sstasstruct ar_tls { 386226031Sstas struct heim_auto_release *head; 387226031Sstas struct heim_auto_release *current; 388226031Sstas HEIMDAL_MUTEX tls_mutex; 389226031Sstas}; 390226031Sstas 391226031Sstasstatic void 392226031Sstasar_tls_delete(void *ptr) 393226031Sstas{ 394226031Sstas struct ar_tls *tls = ptr; 395226031Sstas if (tls->head) 396226031Sstas heim_release(tls->head); 397226031Sstas free(tls); 398226031Sstas} 399226031Sstas 400226031Sstasstatic void 401226031Sstasinit_ar_tls(void *ptr) 402226031Sstas{ 403226031Sstas int ret; 404226031Sstas HEIMDAL_key_create(&ar_key, ar_tls_delete, ret); 405226031Sstas if (ret == 0) 406226031Sstas ar_created = 1; 407226031Sstas} 408226031Sstas 409226031Sstasstatic struct ar_tls * 410226031Sstasautorel_tls(void) 411226031Sstas{ 412226031Sstas static heim_base_once_t once = HEIM_BASE_ONCE_INIT; 413226031Sstas struct ar_tls *arp; 414226031Sstas int ret; 415226031Sstas 416226031Sstas heim_base_once_f(&once, NULL, init_ar_tls); 417226031Sstas if (!ar_created) 418226031Sstas return NULL; 419226031Sstas 420226031Sstas arp = HEIMDAL_getspecific(ar_key); 421226031Sstas if (arp == NULL) { 422226031Sstas 423226031Sstas arp = calloc(1, sizeof(*arp)); 424226031Sstas if (arp == NULL) 425226031Sstas return NULL; 426226031Sstas HEIMDAL_setspecific(ar_key, arp, ret); 427226031Sstas if (ret) { 428226031Sstas free(arp); 429226031Sstas return NULL; 430226031Sstas } 431226031Sstas } 432226031Sstas return arp; 433226031Sstas 434226031Sstas} 435226031Sstas 436226031Sstasstatic void 437226031Sstasautorel_dealloc(void *ptr) 438226031Sstas{ 439226031Sstas heim_auto_release_t ar = ptr; 440226031Sstas struct ar_tls *tls; 441226031Sstas 442226031Sstas tls = autorel_tls(); 443226031Sstas if (tls == NULL) 444226031Sstas heim_abort("autorelease pool released on thread w/o autorelease inited"); 445226031Sstas 446226031Sstas heim_auto_release_drain(ar); 447226031Sstas 448226031Sstas if (!HEIM_TAILQ_EMPTY(&ar->pool)) 449226031Sstas heim_abort("pool not empty after draining"); 450226031Sstas 451226031Sstas HEIMDAL_MUTEX_lock(&tls->tls_mutex); 452226031Sstas if (tls->current != ptr) 453226031Sstas heim_abort("autorelease not releaseing top pool"); 454226031Sstas 455226031Sstas if (tls->current != tls->head) 456226031Sstas tls->current = ar->parent; 457226031Sstas HEIMDAL_MUTEX_unlock(&tls->tls_mutex); 458226031Sstas} 459226031Sstas 460226031Sstasstatic int 461226031Sstasautorel_cmp(void *a, void *b) 462226031Sstas{ 463226031Sstas return (a == b); 464226031Sstas} 465226031Sstas 466226031Sstasstatic unsigned long 467226031Sstasautorel_hash(void *ptr) 468226031Sstas{ 469226031Sstas return (unsigned long)ptr; 470226031Sstas} 471226031Sstas 472226031Sstas 473226031Sstasstatic struct heim_type_data _heim_autorel_object = { 474226031Sstas HEIM_TID_AUTORELEASE, 475226031Sstas "autorelease-pool", 476226031Sstas NULL, 477226031Sstas autorel_dealloc, 478226031Sstas NULL, 479226031Sstas autorel_cmp, 480226031Sstas autorel_hash 481226031Sstas}; 482226031Sstas 483226031Sstas/** 484226031Sstas * 485226031Sstas */ 486226031Sstas 487226031Sstasheim_auto_release_t 488226031Sstasheim_auto_release_create(void) 489226031Sstas{ 490226031Sstas struct ar_tls *tls = autorel_tls(); 491226031Sstas heim_auto_release_t ar; 492226031Sstas 493226031Sstas if (tls == NULL) 494226031Sstas heim_abort("Failed to create/get autorelease head"); 495226031Sstas 496226031Sstas ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release)); 497226031Sstas if (ar) { 498226031Sstas HEIMDAL_MUTEX_lock(&tls->tls_mutex); 499226031Sstas if (tls->head == NULL) 500226031Sstas tls->head = ar; 501226031Sstas ar->parent = tls->current; 502226031Sstas tls->current = ar; 503226031Sstas HEIMDAL_MUTEX_unlock(&tls->tls_mutex); 504226031Sstas } 505226031Sstas 506226031Sstas return ar; 507226031Sstas} 508226031Sstas 509226031Sstas/** 510226031Sstas * Mark the current object as a 511226031Sstas */ 512226031Sstas 513226031Sstasvoid 514226031Sstasheim_auto_release(heim_object_t ptr) 515226031Sstas{ 516226031Sstas struct heim_base *p = PTR2BASE(ptr); 517226031Sstas struct ar_tls *tls = autorel_tls(); 518226031Sstas heim_auto_release_t ar; 519226031Sstas 520226031Sstas if (ptr == NULL || heim_base_is_tagged(ptr)) 521226031Sstas return; 522226031Sstas 523226031Sstas /* drop from old pool */ 524226031Sstas if ((ar = p->autorelpool) != NULL) { 525226031Sstas HEIMDAL_MUTEX_lock(&ar->pool_mutex); 526226031Sstas HEIM_TAILQ_REMOVE(&ar->pool, p, autorel); 527226031Sstas p->autorelpool = NULL; 528226031Sstas HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 529226031Sstas } 530226031Sstas 531226031Sstas if (tls == NULL || (ar = tls->current) == NULL) 532226031Sstas heim_abort("no auto relase pool in place, would leak"); 533226031Sstas 534226031Sstas HEIMDAL_MUTEX_lock(&ar->pool_mutex); 535226031Sstas HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel); 536226031Sstas p->autorelpool = ar; 537226031Sstas HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 538226031Sstas} 539226031Sstas 540226031Sstas/** 541226031Sstas * 542226031Sstas */ 543226031Sstas 544226031Sstasvoid 545226031Sstasheim_auto_release_drain(heim_auto_release_t autorel) 546226031Sstas{ 547226031Sstas heim_object_t obj; 548226031Sstas 549226031Sstas /* release all elements on the tail queue */ 550226031Sstas 551226031Sstas HEIMDAL_MUTEX_lock(&autorel->pool_mutex); 552226031Sstas while(!HEIM_TAILQ_EMPTY(&autorel->pool)) { 553226031Sstas obj = HEIM_TAILQ_FIRST(&autorel->pool); 554226031Sstas HEIMDAL_MUTEX_unlock(&autorel->pool_mutex); 555226031Sstas heim_release(BASE2PTR(obj)); 556226031Sstas HEIMDAL_MUTEX_lock(&autorel->pool_mutex); 557226031Sstas } 558226031Sstas HEIMDAL_MUTEX_unlock(&autorel->pool_mutex); 559226031Sstas} 560