1 /* 2 * Copyright (c) 2010 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "baselocl.h" 37 #include <syslog.h> 38 39 static heim_base_atomic_type tidglobal = HEIM_TID_USER; 40 41 struct heim_base { 42 heim_type_t isa; 43 heim_base_atomic_type ref_cnt; 44 HEIM_TAILQ_ENTRY(heim_base) autorel; 45 heim_auto_release_t autorelpool; 46 uintptr_t isaextra[3]; 47 }; 48 49 /* specialized version of base */ 50 struct heim_base_mem { 51 heim_type_t isa; 52 heim_base_atomic_type ref_cnt; 53 HEIM_TAILQ_ENTRY(heim_base) autorel; 54 heim_auto_release_t autorelpool; 55 const char *name; 56 void (*dealloc)(void *); 57 uintptr_t isaextra[1]; 58 }; 59 60 #define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1) 61 #define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1)) 62 63 #ifdef HEIM_BASE_NEED_ATOMIC_MUTEX 64 HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER; 65 #endif 66 67 /* 68 * Auto release structure 69 */ 70 71 struct heim_auto_release { 72 HEIM_TAILQ_HEAD(, heim_base) pool; 73 HEIMDAL_MUTEX pool_mutex; 74 struct heim_auto_release *parent; 75 }; 76 77 78 /** 79 * Retain object 80 * 81 * @param object to be released, NULL is ok 82 * 83 * @return the same object as passed in 84 */ 85 86 void * 87 heim_retain(void *ptr) 88 { 89 struct heim_base *p = PTR2BASE(ptr); 90 91 if (ptr == NULL || heim_base_is_tagged(ptr)) 92 return ptr; 93 94 if (p->ref_cnt == heim_base_atomic_max) 95 return ptr; 96 97 if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0) 98 heim_abort("resurection"); 99 return ptr; 100 } 101 102 /** 103 * Release object, free is reference count reaches zero 104 * 105 * @param object to be released 106 */ 107 108 void 109 heim_release(void *ptr) 110 { 111 heim_base_atomic_type old; 112 struct heim_base *p = PTR2BASE(ptr); 113 114 if (ptr == NULL || heim_base_is_tagged(ptr)) 115 return; 116 117 if (p->ref_cnt == heim_base_atomic_max) 118 return; 119 120 old = heim_base_atomic_dec(&p->ref_cnt) + 1; 121 122 if (old > 1) 123 return; 124 125 if (old == 1) { 126 heim_auto_release_t ar = p->autorelpool; 127 /* remove from autorel pool list */ 128 if (ar) { 129 p->autorelpool = NULL; 130 HEIMDAL_MUTEX_lock(&ar->pool_mutex); 131 HEIM_TAILQ_REMOVE(&ar->pool, p, autorel); 132 HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 133 } 134 if (p->isa->dealloc) 135 p->isa->dealloc(ptr); 136 free(p); 137 } else 138 heim_abort("over release"); 139 } 140 141 static heim_type_t tagged_isa[9] = { 142 &_heim_number_object, 143 &_heim_null_object, 144 &_heim_bool_object, 145 146 NULL, 147 NULL, 148 NULL, 149 150 NULL, 151 NULL, 152 NULL 153 }; 154 155 heim_type_t 156 _heim_get_isa(heim_object_t ptr) 157 { 158 struct heim_base *p; 159 if (heim_base_is_tagged(ptr)) { 160 if (heim_base_is_tagged_object(ptr)) 161 return tagged_isa[heim_base_tagged_object_tid(ptr)]; 162 heim_abort("not a supported tagged type"); 163 } 164 p = PTR2BASE(ptr); 165 return p->isa; 166 } 167 168 /** 169 * Get type ID of object 170 * 171 * @param object object to get type id of 172 * 173 * @return type id of object 174 */ 175 176 heim_tid_t 177 heim_get_tid(heim_object_t ptr) 178 { 179 heim_type_t isa = _heim_get_isa(ptr); 180 return isa->tid; 181 } 182 183 /** 184 * Get hash value of object 185 * 186 * @param object object to get hash value for 187 * 188 * @return a hash value 189 */ 190 191 unsigned long 192 heim_get_hash(heim_object_t ptr) 193 { 194 heim_type_t isa = _heim_get_isa(ptr); 195 if (isa->hash) 196 return isa->hash(ptr); 197 return (unsigned long)ptr; 198 } 199 200 /** 201 * Compare two objects, returns 0 if equal, can use used for qsort() 202 * and friends. 203 * 204 * @param a first object to compare 205 * @param b first object to compare 206 * 207 * @return 0 if objects are equal 208 */ 209 210 int 211 heim_cmp(heim_object_t a, heim_object_t b) 212 { 213 heim_tid_t ta, tb; 214 heim_type_t isa; 215 216 ta = heim_get_tid(a); 217 tb = heim_get_tid(b); 218 219 if (ta != tb) 220 return ta - tb; 221 222 isa = _heim_get_isa(a); 223 224 if (isa->cmp) 225 return isa->cmp(a, b); 226 227 return (uintptr_t)a - (uintptr_t)b; 228 } 229 230 /* 231 * Private - allocates an memory object 232 */ 233 234 static void 235 memory_dealloc(void *ptr) 236 { 237 struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr); 238 if (p->dealloc) 239 p->dealloc(ptr); 240 } 241 242 struct heim_type_data memory_object = { 243 HEIM_TID_MEMORY, 244 "memory-object", 245 NULL, 246 memory_dealloc, 247 NULL, 248 NULL, 249 NULL 250 }; 251 252 void * 253 heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc) 254 { 255 /* XXX use posix_memalign */ 256 257 struct heim_base_mem *p = calloc(1, size + sizeof(*p)); 258 if (p == NULL) 259 return NULL; 260 p->isa = &memory_object; 261 p->ref_cnt = 1; 262 p->name = name; 263 p->dealloc = dealloc; 264 return BASE2PTR(p); 265 } 266 267 heim_type_t 268 _heim_create_type(const char *name, 269 heim_type_init init, 270 heim_type_dealloc dealloc, 271 heim_type_copy copy, 272 heim_type_cmp cmp, 273 heim_type_hash hash) 274 { 275 heim_type_t type; 276 277 type = calloc(1, sizeof(*type)); 278 if (type == NULL) 279 return NULL; 280 281 type->tid = heim_base_atomic_inc(&tidglobal); 282 type->name = name; 283 type->init = init; 284 type->dealloc = dealloc; 285 type->copy = copy; 286 type->cmp = cmp; 287 type->hash = hash; 288 289 return type; 290 } 291 292 heim_object_t 293 _heim_alloc_object(heim_type_t type, size_t size) 294 { 295 /* XXX should use posix_memalign */ 296 struct heim_base *p = calloc(1, size + sizeof(*p)); 297 if (p == NULL) 298 return NULL; 299 p->isa = type; 300 p->ref_cnt = 1; 301 302 return BASE2PTR(p); 303 } 304 305 heim_tid_t 306 _heim_type_get_tid(heim_type_t type) 307 { 308 return type->tid; 309 } 310 311 /** 312 * Call func once and only once 313 * 314 * @param once pointer to a heim_base_once_t 315 * @param ctx context passed to func 316 * @param func function to be called 317 */ 318 319 void 320 heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *)) 321 { 322 #ifdef HAVE_DISPATCH_DISPATCH_H 323 dispatch_once_f(once, ctx, func); 324 #else 325 static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER; 326 HEIMDAL_MUTEX_lock(&mutex); 327 if (*once == 0) { 328 *once = 1; 329 HEIMDAL_MUTEX_unlock(&mutex); 330 func(ctx); 331 HEIMDAL_MUTEX_lock(&mutex); 332 *once = 2; 333 HEIMDAL_MUTEX_unlock(&mutex); 334 } else if (*once == 2) { 335 HEIMDAL_MUTEX_unlock(&mutex); 336 } else { 337 HEIMDAL_MUTEX_unlock(&mutex); 338 while (1) { 339 struct timeval tv = { 0, 1000 }; 340 select(0, NULL, NULL, NULL, &tv); 341 HEIMDAL_MUTEX_lock(&mutex); 342 if (*once == 2) 343 break; 344 HEIMDAL_MUTEX_unlock(&mutex); 345 } 346 HEIMDAL_MUTEX_unlock(&mutex); 347 } 348 #endif 349 } 350 351 /** 352 * Abort and log the failure (using syslog) 353 */ 354 355 void 356 heim_abort(const char *fmt, ...) 357 { 358 va_list ap; 359 va_start(ap, fmt); 360 heim_abortv(fmt, ap); 361 va_end(ap); 362 } 363 364 /** 365 * Abort and log the failure (using syslog) 366 */ 367 368 void 369 heim_abortv(const char *fmt, va_list ap) 370 { 371 static char str[1024]; 372 373 vsnprintf(str, sizeof(str), fmt, ap); 374 syslog(LOG_ERR, "heim_abort: %s", str); 375 abort(); 376 } 377 378 /* 379 * 380 */ 381 382 static int ar_created = 0; 383 static HEIMDAL_thread_key ar_key; 384 385 struct ar_tls { 386 struct heim_auto_release *head; 387 struct heim_auto_release *current; 388 HEIMDAL_MUTEX tls_mutex; 389 }; 390 391 static void 392 ar_tls_delete(void *ptr) 393 { 394 struct ar_tls *tls = ptr; 395 if (tls->head) 396 heim_release(tls->head); 397 free(tls); 398 } 399 400 static void 401 init_ar_tls(void *ptr) 402 { 403 int ret; 404 HEIMDAL_key_create(&ar_key, ar_tls_delete, ret); 405 if (ret == 0) 406 ar_created = 1; 407 } 408 409 static struct ar_tls * 410 autorel_tls(void) 411 { 412 static heim_base_once_t once = HEIM_BASE_ONCE_INIT; 413 struct ar_tls *arp; 414 int ret; 415 416 heim_base_once_f(&once, NULL, init_ar_tls); 417 if (!ar_created) 418 return NULL; 419 420 arp = HEIMDAL_getspecific(ar_key); 421 if (arp == NULL) { 422 423 arp = calloc(1, sizeof(*arp)); 424 if (arp == NULL) 425 return NULL; 426 HEIMDAL_setspecific(ar_key, arp, ret); 427 if (ret) { 428 free(arp); 429 return NULL; 430 } 431 } 432 return arp; 433 434 } 435 436 static void 437 autorel_dealloc(void *ptr) 438 { 439 heim_auto_release_t ar = ptr; 440 struct ar_tls *tls; 441 442 tls = autorel_tls(); 443 if (tls == NULL) 444 heim_abort("autorelease pool released on thread w/o autorelease inited"); 445 446 heim_auto_release_drain(ar); 447 448 if (!HEIM_TAILQ_EMPTY(&ar->pool)) 449 heim_abort("pool not empty after draining"); 450 451 HEIMDAL_MUTEX_lock(&tls->tls_mutex); 452 if (tls->current != ptr) 453 heim_abort("autorelease not releaseing top pool"); 454 455 if (tls->current != tls->head) 456 tls->current = ar->parent; 457 HEIMDAL_MUTEX_unlock(&tls->tls_mutex); 458 } 459 460 static int 461 autorel_cmp(void *a, void *b) 462 { 463 return (a == b); 464 } 465 466 static unsigned long 467 autorel_hash(void *ptr) 468 { 469 return (unsigned long)ptr; 470 } 471 472 473 static struct heim_type_data _heim_autorel_object = { 474 HEIM_TID_AUTORELEASE, 475 "autorelease-pool", 476 NULL, 477 autorel_dealloc, 478 NULL, 479 autorel_cmp, 480 autorel_hash 481 }; 482 483 /** 484 * 485 */ 486 487 heim_auto_release_t 488 heim_auto_release_create(void) 489 { 490 struct ar_tls *tls = autorel_tls(); 491 heim_auto_release_t ar; 492 493 if (tls == NULL) 494 heim_abort("Failed to create/get autorelease head"); 495 496 ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release)); 497 if (ar) { 498 HEIMDAL_MUTEX_lock(&tls->tls_mutex); 499 if (tls->head == NULL) 500 tls->head = ar; 501 ar->parent = tls->current; 502 tls->current = ar; 503 HEIMDAL_MUTEX_unlock(&tls->tls_mutex); 504 } 505 506 return ar; 507 } 508 509 /** 510 * Mark the current object as a 511 */ 512 513 void 514 heim_auto_release(heim_object_t ptr) 515 { 516 struct heim_base *p = PTR2BASE(ptr); 517 struct ar_tls *tls = autorel_tls(); 518 heim_auto_release_t ar; 519 520 if (ptr == NULL || heim_base_is_tagged(ptr)) 521 return; 522 523 /* drop from old pool */ 524 if ((ar = p->autorelpool) != NULL) { 525 HEIMDAL_MUTEX_lock(&ar->pool_mutex); 526 HEIM_TAILQ_REMOVE(&ar->pool, p, autorel); 527 p->autorelpool = NULL; 528 HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 529 } 530 531 if (tls == NULL || (ar = tls->current) == NULL) 532 heim_abort("no auto relase pool in place, would leak"); 533 534 HEIMDAL_MUTEX_lock(&ar->pool_mutex); 535 HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel); 536 p->autorelpool = ar; 537 HEIMDAL_MUTEX_unlock(&ar->pool_mutex); 538 } 539 540 /** 541 * 542 */ 543 544 void 545 heim_auto_release_drain(heim_auto_release_t autorel) 546 { 547 heim_object_t obj; 548 549 /* release all elements on the tail queue */ 550 551 HEIMDAL_MUTEX_lock(&autorel->pool_mutex); 552 while(!HEIM_TAILQ_EMPTY(&autorel->pool)) { 553 obj = HEIM_TAILQ_FIRST(&autorel->pool); 554 HEIMDAL_MUTEX_unlock(&autorel->pool_mutex); 555 heim_release(BASE2PTR(obj)); 556 HEIMDAL_MUTEX_lock(&autorel->pool_mutex); 557 } 558 HEIMDAL_MUTEX_unlock(&autorel->pool_mutex); 559 } 560