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