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