2
* Copyright (c) 2010 Kungliga Tekniska Högskolan
3
* (Royal Institute of Technology, Stockholm, Sweden).
6
* Portions Copyright (c) 2010 Apple Inc. All rights reserved.
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
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.
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.
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
39
static heim_base_atomic_type tidglobal = HEIM_TID_USER;
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];
49
/* specialized version of base */
50
struct heim_base_mem {
52
heim_base_atomic_type ref_cnt;
53
HEIM_TAILQ_ENTRY(heim_base) autorel;
54
heim_auto_release_t autorelpool;
56
void (*dealloc)(void *);
57
uintptr_t isaextra[1];
60
#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
61
#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
63
#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
64
HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
68
* Auto release structure
71
struct heim_auto_release {
72
HEIM_TAILQ_HEAD(, heim_base) pool;
73
HEIMDAL_MUTEX pool_mutex;
74
struct heim_auto_release *parent;
81
* @param object to be released, NULL is ok
83
* @return the same object as passed in
87
heim_retain(void *ptr)
89
struct heim_base *p = PTR2BASE(ptr);
91
if (ptr == NULL || heim_base_is_tagged(ptr))
94
if (p->ref_cnt == heim_base_atomic_max)
97
if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
98
heim_abort("resurection");
103
* Release object, free is reference count reaches zero
105
* @param object to be released
109
heim_release(void *ptr)
111
heim_base_atomic_type old;
112
struct heim_base *p = PTR2BASE(ptr);
114
if (ptr == NULL || heim_base_is_tagged(ptr))
117
if (p->ref_cnt == heim_base_atomic_max)
120
old = heim_base_atomic_dec(&p->ref_cnt) + 1;
126
heim_auto_release_t ar = p->autorelpool;
127
/* remove from autorel pool list */
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);
135
p->isa->dealloc(ptr);
138
heim_abort("over release");
141
static heim_type_t tagged_isa[9] = {
142
&_heim_number_object,
156
_heim_get_isa(heim_object_t ptr)
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");
169
* Get type ID of object
171
* @param object object to get type id of
173
* @return type id of object
177
heim_get_tid(heim_object_t ptr)
179
heim_type_t isa = _heim_get_isa(ptr);
184
* Get hash value of object
186
* @param object object to get hash value for
188
* @return a hash value
192
heim_get_hash(heim_object_t ptr)
194
heim_type_t isa = _heim_get_isa(ptr);
196
return isa->hash(ptr);
197
return (unsigned long)ptr;
201
* Compare two objects, returns 0 if equal, can use used for qsort()
204
* @param a first object to compare
205
* @param b first object to compare
207
* @return 0 if objects are equal
211
heim_cmp(heim_object_t a, heim_object_t b)
216
ta = heim_get_tid(a);
217
tb = heim_get_tid(b);
222
isa = _heim_get_isa(a);
225
return isa->cmp(a, b);
227
return (uintptr_t)a - (uintptr_t)b;
231
* Private - allocates an memory object
235
memory_dealloc(void *ptr)
237
struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
242
struct heim_type_data memory_object = {
253
heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
255
/* XXX use posix_memalign */
257
struct heim_base_mem *p = calloc(1, size + sizeof(*p));
260
p->isa = &memory_object;
263
p->dealloc = dealloc;
268
_heim_create_type(const char *name,
270
heim_type_dealloc dealloc,
277
type = calloc(1, sizeof(*type));
281
type->tid = heim_base_atomic_inc(&tidglobal);
284
type->dealloc = dealloc;
293
_heim_alloc_object(heim_type_t type, size_t size)
295
/* XXX should use posix_memalign */
296
struct heim_base *p = calloc(1, size + sizeof(*p));
306
_heim_type_get_tid(heim_type_t type)
312
* Call func once and only once
314
* @param once pointer to a heim_base_once_t
315
* @param ctx context passed to func
316
* @param func function to be called
320
heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
322
#ifdef HAVE_DISPATCH_DISPATCH_H
323
dispatch_once_f(once, ctx, func);
325
static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
326
HEIMDAL_MUTEX_lock(&mutex);
329
HEIMDAL_MUTEX_unlock(&mutex);
331
HEIMDAL_MUTEX_lock(&mutex);
333
HEIMDAL_MUTEX_unlock(&mutex);
334
} else if (*once == 2) {
335
HEIMDAL_MUTEX_unlock(&mutex);
337
HEIMDAL_MUTEX_unlock(&mutex);
339
struct timeval tv = { 0, 1000 };
340
select(0, NULL, NULL, NULL, &tv);
341
HEIMDAL_MUTEX_lock(&mutex);
344
HEIMDAL_MUTEX_unlock(&mutex);
346
HEIMDAL_MUTEX_unlock(&mutex);
352
* Abort and log the failure (using syslog)
356
heim_abort(const char *fmt, ...)
360
heim_abortv(fmt, ap);
365
* Abort and log the failure (using syslog)
369
heim_abortv(const char *fmt, va_list ap)
371
static char str[1024];
373
vsnprintf(str, sizeof(str), fmt, ap);
374
syslog(LOG_ERR, "heim_abort: %s", str);
382
static int ar_created = 0;
383
static HEIMDAL_thread_key ar_key;
386
struct heim_auto_release *head;
387
struct heim_auto_release *current;
388
HEIMDAL_MUTEX tls_mutex;
392
ar_tls_delete(void *ptr)
394
struct ar_tls *tls = ptr;
396
heim_release(tls->head);
401
init_ar_tls(void *ptr)
404
HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
409
static struct ar_tls *
412
static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
416
heim_base_once_f(&once, NULL, init_ar_tls);
420
arp = HEIMDAL_getspecific(ar_key);
423
arp = calloc(1, sizeof(*arp));
426
HEIMDAL_setspecific(ar_key, arp, ret);
437
autorel_dealloc(void *ptr)
439
heim_auto_release_t ar = ptr;
444
heim_abort("autorelease pool released on thread w/o autorelease inited");
446
heim_auto_release_drain(ar);
448
if (!HEIM_TAILQ_EMPTY(&ar->pool))
449
heim_abort("pool not empty after draining");
451
HEIMDAL_MUTEX_lock(&tls->tls_mutex);
452
if (tls->current != ptr)
453
heim_abort("autorelease not releaseing top pool");
455
if (tls->current != tls->head)
456
tls->current = ar->parent;
457
HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
461
autorel_cmp(void *a, void *b)
467
autorel_hash(void *ptr)
469
return (unsigned long)ptr;
473
static struct heim_type_data _heim_autorel_object = {
474
HEIM_TID_AUTORELEASE,
488
heim_auto_release_create(void)
490
struct ar_tls *tls = autorel_tls();
491
heim_auto_release_t ar;
494
heim_abort("Failed to create/get autorelease head");
496
ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release));
498
HEIMDAL_MUTEX_lock(&tls->tls_mutex);
499
if (tls->head == NULL)
501
ar->parent = tls->current;
503
HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
510
* Mark the current object as a
514
heim_auto_release(heim_object_t ptr)
516
struct heim_base *p = PTR2BASE(ptr);
517
struct ar_tls *tls = autorel_tls();
518
heim_auto_release_t ar;
520
if (ptr == NULL || heim_base_is_tagged(ptr))
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);
531
if (tls == NULL || (ar = tls->current) == NULL)
532
heim_abort("no auto relase pool in place, would leak");
534
HEIMDAL_MUTEX_lock(&ar->pool_mutex);
535
HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel);
537
HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
545
heim_auto_release_drain(heim_auto_release_t autorel)
549
/* release all elements on the tail queue */
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);
558
HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);