source: heimdal/trunk/base/heimbase.c@ 4

Last change on this file since 4 was 4, checked in by Paul Smedley, 10 years ago

heimdal: applied os2 patches, added conf.cmd

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