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#define HEIM_BASE_INTERNAL 1 37 38#include "baselocl.h" 39#include <syslog.h> 40 41static heim_base_atomic_type tidglobal = HEIM_TID_USER; 42 43#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1) 44#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1)) 45 46#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX 47HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; 48#endif 49 50/* 51 * Auto release structure 52 */ 53 54struct heim_auto_release { 55 HEIM_TAILQ_HEAD(, heim_base) pool; 56 HEIMDAL_MUTEX pool_mutex; 57 struct heim_auto_release *parent; 58}; 59 60 61/** 62 * Retain object 63 * 64 * @param object to be released, NULL is ok 65 * 66 * @return the same object as passed in 67 */ 68 69void * 70heim_retain(void *ptr) 71{ 72 struct heim_base *p = PTR2BASE(ptr); 73 74 if (ptr == NULL || heim_base_is_tagged(ptr)) 75 return ptr; 76 77 if (p->ref_cnt == heim_base_atomic_max) 78 return ptr; 79 80 if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0) 81 heim_abort("resurection"); 82 return ptr; 83} 84 85/** 86 * Release object, free is reference count reaches zero 87 * 88 * @param object to be released 89 */ 90 91void 92heim_release(void *ptr) 93{ 94 heim_base_atomic_type old; 95 struct heim_base *p = PTR2BASE(ptr); 96 97 if (ptr == NULL || heim_base_is_tagged(ptr)) 98 return; 99 100 if (p->ref_cnt == heim_base_atomic_max) 101 return; 102 103 old = heim_base_atomic_dec(&p->ref_cnt) + 1; 104 105 if (old > 1) 106 return; 107 108 if (old == 1) { 109 heim_auto_release_t ar = p->autorelpool; 110 /* remove from autorel pool list */ 111 if (ar) { 112 p->autorelpool = NULL; 113 HEIMDAL_MUTEX_lock(&ar->pool_mutex); 114 HEIM_TAILQ_REMOVE(&ar->pool, p, autorel); 115 HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 116 } 117 if (p->isa->dealloc) 118 p->isa->dealloc(ptr); 119 free(p); 120 } else 121 heim_abort("over release"); 122} 123 124static heim_type_t tagged_isa[9] = { 125 &_heim_number_object, 126 &_heim_null_object, 127 &_heim_bool_object, 128 129 NULL, 130 NULL, 131 NULL, 132 133 NULL, 134 NULL, 135 NULL 136}; 137 138heim_type_t 139_heim_get_isa(heim_object_t ptr) 140{ 141 struct heim_base *p; 142 if (heim_base_is_tagged(ptr)) { 143 if (heim_base_is_tagged_object(ptr)) 144 return tagged_isa[heim_base_tagged_object_tid(ptr)]; 145 heim_abort("not a supported tagged type"); 146 } 147 p = PTR2BASE(ptr); 148 return p->isa; 149} 150 151/** 152 * Get type ID of object 153 * 154 * @param object object to get type id of 155 * 156 * @return type id of object 157 */ 158 159heim_tid_t 160heim_get_tid(heim_object_t ptr) 161{ 162 heim_type_t isa = _heim_get_isa(ptr); 163 return isa->tid; 164} 165 166/** 167 * Get hash value of object 168 * 169 * @param object object to get hash value for 170 * 171 * @return a hash value 172 */ 173 174unsigned long 175heim_get_hash(heim_object_t ptr) 176{ 177 heim_type_t isa = _heim_get_isa(ptr); 178 if (isa->hash) 179 return isa->hash(ptr); 180 return (unsigned long)ptr; 181} 182 183/** 184 * Compare two objects, returns 0 if equal, can use used for qsort() 185 * and friends. 186 * 187 * @param a first object to compare 188 * @param b first object to compare 189 * 190 * @return 0 if objects are equal 191 */ 192 193int 194heim_cmp(heim_object_t a, heim_object_t b) 195{ 196 heim_tid_t ta, tb; 197 heim_type_t isa; 198 199 ta = heim_get_tid(a); 200 tb = heim_get_tid(b); 201 202 if (ta != tb) 203 return ta - tb; 204 205 isa = _heim_get_isa(a); 206 207 if (isa->cmp) 208 return isa->cmp(a, b); 209 210 if (a == b) 211 return 0; 212 uintptr_t ai = (uintptr_t)a; 213 uintptr_t bi = (uintptr_t)b; 214 while (((int)(ai - bi)) == 0) { 215 ai = ai >> 8; 216 bi = bi >> 8; 217 } 218 int diff = (int)(ai - bi); 219 heim_assert(diff != 0, "pointers are the same ?"); 220 221 return diff; 222} 223 224/* 225 * Private - allocates an memory object 226 */ 227 228static void 229memory_dealloc(void *ptr) 230{ 231 struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr); 232 if (p->dealloc) 233 p->dealloc(ptr); 234} 235 236static struct heim_type_data memory_object = { 237 HEIM_TID_MEMORY, 238 "memory-object", 239 NULL, 240 memory_dealloc, 241 NULL, 242 NULL, 243 NULL 244}; 245 246void * 247heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc) 248{ 249 /* XXX use posix_memalign */ 250 251 struct heim_base_mem *p = calloc(1, size + sizeof(*p)); 252 if (p == NULL) 253 return NULL; 254 p->isa = &memory_object; 255 p->ref_cnt = 1; 256 p->name = name; 257 p->dealloc = dealloc; 258 return BASE2PTR(p); 259} 260 261heim_type_t 262_heim_create_type(const char *name, 263 heim_type_init init, 264 heim_type_dealloc dealloc, 265 heim_type_copy copy, 266 heim_type_cmp cmp, 267 heim_type_hash hash) 268{ 269 heim_type_t type; 270 271 type = calloc(1, sizeof(*type)); 272 if (type == NULL) 273 return NULL; 274 275 type->tid = heim_base_atomic_inc(&tidglobal); 276 type->name = name; 277 type->init = init; 278 type->dealloc = dealloc; 279 type->copy = copy; 280 type->cmp = cmp; 281 type->hash = hash; 282 283 return type; 284} 285 286heim_object_t 287_heim_alloc_object(heim_type_t type, size_t size) 288{ 289 /* XXX should use posix_memalign */ 290 struct heim_base *p = calloc(1, size + sizeof(*p)); 291 if (p == NULL) 292 return NULL; 293 p->isa = type; 294 p->ref_cnt = 1; 295 296 return BASE2PTR(p); 297} 298 299heim_tid_t 300_heim_type_get_tid(heim_type_t type) 301{ 302 return type->tid; 303} 304 305/** 306 * Call func once and only once 307 * 308 * @param once pointer to a heim_base_once_t 309 * @param ctx context passed to func 310 * @param func function to be called 311 */ 312 313void 314heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *)) 315{ 316#ifdef HAVE_DISPATCH_DISPATCH_H 317 dispatch_once_f(once, ctx, func); 318#else 319 static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER; 320 HEIMDAL_MUTEX_lock(&mutex); 321 if (*once == 0) { 322 *once = 1; 323 HEIMDAL_MUTEX_unlock(&mutex); 324 func(ctx); 325 HEIMDAL_MUTEX_lock(&mutex); 326 *once = 2; 327 HEIMDAL_MUTEX_unlock(&mutex); 328 } else if (*once == 2) { 329 HEIMDAL_MUTEX_unlock(&mutex); 330 } else { 331 HEIMDAL_MUTEX_unlock(&mutex); 332 while (1) { 333 struct timeval tv = { 0, 1000 }; 334 select(0, NULL, NULL, NULL, &tv); 335 HEIMDAL_MUTEX_lock(&mutex); 336 if (*once == 2) 337 break; 338 HEIMDAL_MUTEX_unlock(&mutex); 339 } 340 HEIMDAL_MUTEX_unlock(&mutex); 341 } 342#endif 343} 344 345/* 346 * 347 */ 348 349#ifdef __APPLE_PRIVATE__ 350const char *__crashreporter_info__ = NULL; 351asm(".desc ___crashreporter_info__, 0x10"); 352#endif 353 354 355/** 356 * Abort and log the failure (using syslog) 357 */ 358 359void 360heim_abort(const char *fmt, ...) 361 HEIMDAL_NORETURN_ATTRIBUTE 362 HEIMDAL_PRINTF_ATTRIBUTE((printf, 1, 2)) 363{ 364 va_list ap; 365 va_start(ap, fmt); 366 heim_abortv(fmt, ap); 367 va_end(ap); 368} 369 370/** 371 * Abort and log the failure (using syslog) 372 */ 373 374void 375heim_abortv(const char *fmt, va_list ap) 376 HEIMDAL_NORETURN_ATTRIBUTE 377 HEIMDAL_PRINTF_ATTRIBUTE((printf, 1, 0)) 378{ 379 char *str = NULL; 380 int ret; 381 382 ret = vasprintf(&str, fmt, ap); 383 if (ret > 0 && str) { 384 syslog(LOG_ERR, "heim_abort: %s", str); 385 386#ifdef __APPLE_PRIVATE__ 387 __crashreporter_info__ = str; 388#endif 389 } 390 abort(); 391} 392 393/* 394 * 395 */ 396 397static int ar_created = 0; 398static HEIMDAL_thread_key ar_key; 399 400struct ar_tls { 401 struct heim_auto_release *head; 402 struct heim_auto_release *current; 403 HEIMDAL_MUTEX tls_mutex; 404}; 405 406static void 407ar_tls_delete(void *ptr) 408{ 409 struct ar_tls *tls = ptr; 410 if (tls->head) 411 heim_release(tls->head); 412 free(tls); 413} 414 415static void 416init_ar_tls(void *ptr) 417{ 418 int ret; 419 HEIMDAL_key_create(&ar_key, ar_tls_delete, ret); 420 if (ret == 0) 421 ar_created = 1; 422} 423 424static struct ar_tls * 425autorel_tls(void) 426{ 427 static heim_base_once_t once = HEIM_BASE_ONCE_INIT; 428 struct ar_tls *arp; 429 int ret; 430 431 heim_base_once_f(&once, NULL, init_ar_tls); 432 if (!ar_created) 433 return NULL; 434 435 arp = HEIMDAL_getspecific(ar_key); 436 if (arp == NULL) { 437 438 arp = calloc(1, sizeof(*arp)); 439 if (arp == NULL) 440 return NULL; 441 HEIMDAL_setspecific(ar_key, arp, ret); 442 if (ret) { 443 free(arp); 444 return NULL; 445 } 446 } 447 return arp; 448 449} 450 451static void 452autorel_dealloc(void *ptr) 453{ 454 heim_auto_release_t ar = ptr; 455 struct ar_tls *tls; 456 457 tls = autorel_tls(); 458 if (tls == NULL) 459 heim_abort("autorelease pool released on thread w/o autorelease inited"); 460 461 heim_auto_release_drain(ar); 462 463 if (!HEIM_TAILQ_EMPTY(&ar->pool)) 464 heim_abort("pool not empty after draining"); 465 466 HEIMDAL_MUTEX_lock(&tls->tls_mutex); 467 if (tls->current != ptr) 468 heim_abort("autorelease not releaseing top pool"); 469 470 if (tls->current != tls->head) 471 tls->current = ar->parent; 472 HEIMDAL_MUTEX_unlock(&tls->tls_mutex); 473} 474 475static int 476autorel_cmp(void *a, void *b) 477{ 478 return (a == b); 479} 480 481static unsigned long 482autorel_hash(void *ptr) 483{ 484 return (unsigned long)ptr; 485} 486 487 488static struct heim_type_data _heim_autorel_object = { 489 HEIM_TID_AUTORELEASE, 490 "autorelease-pool", 491 NULL, 492 autorel_dealloc, 493 NULL, 494 autorel_cmp, 495 autorel_hash 496}; 497 498/** 499 * 500 */ 501 502heim_auto_release_t 503heim_auto_release_create(void) 504{ 505 struct ar_tls *tls = autorel_tls(); 506 heim_auto_release_t ar; 507 508 if (tls == NULL) 509 heim_abort("Failed to create/get autorelease head"); 510 511 ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release)); 512 if (ar) { 513 HEIMDAL_MUTEX_lock(&tls->tls_mutex); 514 if (tls->head == NULL) 515 tls->head = ar; 516 ar->parent = tls->current; 517 tls->current = ar; 518 HEIMDAL_MUTEX_unlock(&tls->tls_mutex); 519 } 520 521 return ar; 522} 523 524/** 525 * Mark the current object as a 526 */ 527 528void 529heim_auto_release(heim_object_t ptr) 530{ 531 struct heim_base *p = PTR2BASE(ptr); 532 struct ar_tls *tls = autorel_tls(); 533 heim_auto_release_t ar; 534 535 if (ptr == NULL || heim_base_is_tagged(ptr)) 536 return; 537 538 /* drop from old pool */ 539 if ((ar = p->autorelpool) != NULL) { 540 HEIMDAL_MUTEX_lock(&ar->pool_mutex); 541 HEIM_TAILQ_REMOVE(&ar->pool, p, autorel); 542 p->autorelpool = NULL; 543 HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 544 } 545 546 if (tls == NULL || (ar = tls->current) == NULL) 547 heim_abort("no auto relase pool in place, would leak"); 548 549 HEIMDAL_MUTEX_lock(&ar->pool_mutex); 550 HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel); 551 p->autorelpool = ar; 552 HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 553} 554 555/** 556 * 557 */ 558 559void 560heim_auto_release_drain(heim_auto_release_t autorel) 561{ 562 heim_object_t obj; 563 564 /* release all elements on the tail queue */ 565 566 HEIMDAL_MUTEX_lock(&autorel->pool_mutex); 567 while(!HEIM_TAILQ_EMPTY(&autorel->pool)) { 568 obj = HEIM_TAILQ_FIRST(&autorel->pool); 569 HEIMDAL_MUTEX_unlock(&autorel->pool_mutex); 570 heim_release(BASE2PTR(obj)); 571 HEIMDAL_MUTEX_lock(&autorel->pool_mutex); 572 } 573 HEIMDAL_MUTEX_unlock(&autorel->pool_mutex); 574} 575 576/** 577 * 578 */ 579 580static struct heim_type_data data_object = { 581 HEIM_TID_DATA, 582 "data-object", 583 NULL, 584 NULL, 585 NULL, 586 NULL, 587 NULL 588}; 589 590/** 591 * Allocate an data 592 * 593 * @return A new allocated data object, free with heim_release() 594 */ 595 596heim_data_t 597heim_data_create(void *indata, size_t len) 598{ 599 heim_data_t data; 600 601 data = _heim_alloc_object(&data_object, sizeof(*data) + len); 602 if (data == NULL) 603 return NULL; 604 605 data->data = ((uint8_t *)data) + sizeof(*data); 606 data->length = len; 607 608 memcpy(data->data, indata, data->length); 609 610 return data; 611} 612 613/** 614 * Get type id of an data object 615 * 616 * @return the type id 617 */ 618 619heim_tid_t 620heim_data_get_type_id(void) 621{ 622 return HEIM_TID_DATA; 623} 624