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