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