etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / base / heimbase.c
blobf5ed22b46258311f77d1acc7205eec6cca88c8ea
1 /* $NetBSD: heimbase.c,v 1.1.1.2 2014/04/24 12:45:26 pettai Exp $ */
3 /*
4 * Copyright (c) 2010 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
38 #include "baselocl.h"
39 #include <syslog.h>
41 static heim_base_atomic_type tidglobal = HEIM_TID_USER;
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];
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];
62 #define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
63 #define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
65 #ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
66 HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
67 #endif
70 * Auto release structure
73 struct heim_auto_release {
74 HEIM_TAILQ_HEAD(, heim_base) pool;
75 HEIMDAL_MUTEX pool_mutex;
76 struct heim_auto_release *parent;
80 /**
81 * Retain object
83 * @param object to be released, NULL is ok
85 * @return the same object as passed in
88 void *
89 heim_retain(void *ptr)
91 struct heim_base *p = PTR2BASE(ptr);
93 if (ptr == NULL || heim_base_is_tagged(ptr))
94 return ptr;
96 if (p->ref_cnt == heim_base_atomic_max)
97 return ptr;
99 if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
100 heim_abort("resurection");
101 return ptr;
105 * Release object, free is reference count reaches zero
107 * @param object to be released
110 void
111 heim_release(void *ptr)
113 heim_base_atomic_type old;
114 struct heim_base *p = PTR2BASE(ptr);
116 if (ptr == NULL || heim_base_is_tagged(ptr))
117 return;
119 if (p->ref_cnt == heim_base_atomic_max)
120 return;
122 old = heim_base_atomic_dec(&p->ref_cnt) + 1;
124 if (old > 1)
125 return;
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);
136 if (p->isa->dealloc)
137 p->isa->dealloc(ptr);
138 free(p);
139 } else
140 heim_abort("over release");
143 static heim_type_t tagged_isa[9] = {
144 &_heim_number_object,
145 &_heim_null_object,
146 &_heim_bool_object,
148 NULL,
149 NULL,
150 NULL,
152 NULL,
153 NULL,
154 NULL
157 heim_type_t
158 _heim_get_isa(heim_object_t ptr)
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");
166 p = PTR2BASE(ptr);
167 return p->isa;
171 * Get type ID of object
173 * @param object object to get type id of
175 * @return type id of object
178 heim_tid_t
179 heim_get_tid(heim_object_t ptr)
181 heim_type_t isa = _heim_get_isa(ptr);
182 return isa->tid;
186 * Get hash value of object
188 * @param object object to get hash value for
190 * @return a hash value
193 unsigned long
194 heim_get_hash(heim_object_t ptr)
196 heim_type_t isa = _heim_get_isa(ptr);
197 if (isa->hash)
198 return isa->hash(ptr);
199 return (unsigned long)ptr;
203 * Compare two objects, returns 0 if equal, can use used for qsort()
204 * and friends.
206 * @param a first object to compare
207 * @param b first object to compare
209 * @return 0 if objects are equal
213 heim_cmp(heim_object_t a, heim_object_t b)
215 heim_tid_t ta, tb;
216 heim_type_t isa;
218 ta = heim_get_tid(a);
219 tb = heim_get_tid(b);
221 if (ta != tb)
222 return ta - tb;
224 isa = _heim_get_isa(a);
226 if (isa->cmp)
227 return isa->cmp(a, b);
229 return (uintptr_t)a - (uintptr_t)b;
233 * Private - allocates an memory object
236 static void
237 memory_dealloc(void *ptr)
239 struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
240 if (p->dealloc)
241 p->dealloc(ptr);
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
254 void *
255 heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
257 /* XXX use posix_memalign */
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);
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)
277 heim_type_t type;
279 type = calloc(1, sizeof(*type));
280 if (type == NULL)
281 return NULL;
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;
291 return type;
294 heim_object_t
295 _heim_alloc_object(heim_type_t type, size_t size)
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;
304 return BASE2PTR(p);
307 heim_tid_t
308 _heim_type_get_tid(heim_type_t type)
310 return type->tid;
314 * Call func once and only once
316 * @param once pointer to a heim_base_once_t
317 * @param ctx context passed to func
318 * @param func function to be called
321 void
322 heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
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);
348 HEIMDAL_MUTEX_unlock(&mutex);
350 #endif
354 * Abort and log the failure (using syslog)
357 void
358 heim_abort(const char *fmt, ...)
360 va_list ap;
361 va_start(ap, fmt);
362 heim_abortv(fmt, ap);
363 va_end(ap);
367 * Abort and log the failure (using syslog)
370 void
371 heim_abortv(const char *fmt, va_list ap)
373 static char str[1024];
375 vsnprintf(str, sizeof(str), fmt, ap);
376 syslog(LOG_ERR, "heim_abort: %s", str);
377 abort();
384 static int ar_created = 0;
385 static HEIMDAL_thread_key ar_key;
387 struct ar_tls {
388 struct heim_auto_release *head;
389 struct heim_auto_release *current;
390 HEIMDAL_MUTEX tls_mutex;
393 static void
394 ar_tls_delete(void *ptr)
396 struct ar_tls *tls = ptr;
397 if (tls->head)
398 heim_release(tls->head);
399 free(tls);
402 static void
403 init_ar_tls(void *ptr)
405 int ret;
406 HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
407 if (ret == 0)
408 ar_created = 1;
411 static struct ar_tls *
412 autorel_tls(void)
414 static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
415 struct ar_tls *arp;
416 int ret;
418 heim_base_once_f(&once, NULL, init_ar_tls);
419 if (!ar_created)
420 return NULL;
422 arp = HEIMDAL_getspecific(ar_key);
423 if (arp == NULL) {
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;
434 return arp;
438 static void
439 autorel_dealloc(void *ptr)
441 heim_auto_release_t ar = ptr;
442 struct ar_tls *tls;
444 tls = autorel_tls();
445 if (tls == NULL)
446 heim_abort("autorelease pool released on thread w/o autorelease inited");
448 heim_auto_release_drain(ar);
450 if (!HEIM_TAILQ_EMPTY(&ar->pool))
451 heim_abort("pool not empty after draining");
453 HEIMDAL_MUTEX_lock(&tls->tls_mutex);
454 if (tls->current != ptr)
455 heim_abort("autorelease not releaseing top pool");
457 if (tls->current != tls->head)
458 tls->current = ar->parent;
459 HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
462 static int
463 autorel_cmp(void *a, void *b)
465 return (a == b);
468 static unsigned long
469 autorel_hash(void *ptr)
471 return (unsigned long)ptr;
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
489 heim_auto_release_t
490 heim_auto_release_create(void)
492 struct ar_tls *tls = autorel_tls();
493 heim_auto_release_t ar;
495 if (tls == NULL)
496 heim_abort("Failed to create/get autorelease head");
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);
508 return ar;
512 * Mark the current object as a
515 void
516 heim_auto_release(heim_object_t ptr)
518 struct heim_base *p = PTR2BASE(ptr);
519 struct ar_tls *tls = autorel_tls();
520 heim_auto_release_t ar;
522 if (ptr == NULL || heim_base_is_tagged(ptr))
523 return;
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);
533 if (tls == NULL || (ar = tls->current) == NULL)
534 heim_abort("no auto relase pool in place, would leak");
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);
546 void
547 heim_auto_release_drain(heim_auto_release_t autorel)
549 heim_object_t obj;
551 /* release all elements on the tail queue */
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);
560 HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);