|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.0 (the 'License'). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License."
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: #ifdef SHLIB
25: #include "shlib.h"
26: #endif
27: /*
28: except.c
29:
30: This file implements the exception raising scheme.
31: It is thread safe, although Alt nodes may not behave
32: as expected.
33:
34: Copyright (c) 1988, 1991 NeXT, Inc. as an unpublished work.
35: All rights reserved.
36: */
37:
38: #ifdef KERNEL
39: #import <mach/mach_types.h>
40: #import <kernserv/prototypes.h>
41: #else /* KERNEL */
42: #import <mach/cthreads.h>
43: #import <stdio.h>
44: #endif /* KERNEL */
45:
46: #import "error.h"
47: #ifndef KERNEL
48: #import <stdlib.h>
49: #endif
50:
51: extern void _NXLogError (const char *format, ...);
52:
53: #ifdef KERNEL
54:
55: #import <mach/machine/simple_lock.h>
56:
57: #ifdef hppa
58: #define SIMPLE_LOCK_INITIALIZER { 1, 1, 1, 1 }
59: #else
60: #define SIMPLE_LOCK_INITIALIZER { 1 }
61: #endif
62:
63: #define LOCK_T simple_lock_data_t
64: #define LOCK_INITIALIZER SIMPLE_LOCK_INITIALIZER
65: #define LOCK(x) simple_lock(&(x))
66: #define UNLOCK(x) simple_unlock(&(x))
67:
68: #define cthread_t thread_t
69: #define cthread_self() current_thread()
70:
71: #else /* KERNEL */
72:
73: #define LOCK_T struct mutex
74: #define LOCK_INITIALIZER MUTEX_INITIALIZER
75: #define LOCK(x) mutex_lock(&x)
76: #define UNLOCK(x) mutex_unlock(&x)
77:
78: #endif /* KERNEL */
79:
80: #ifdef KERNEL
81: //int _setjmp(jmp_buf env) { return setjmp(env); }
82: //void _longjmp(jmp_buf env, int val) { longjmp(env, val); }
83: #define _setjmp(env) { return setjmp(env); }
84: #define _longjmp(env, val) { longjmp(env, val); }
85:
86: NXUncaughtExceptionHandler *_NXUncaughtExceptionHandler;
87:
88: #endif /* KERNEL */
89:
90: typedef void AltProc(void *context, int code, const void *data1, const void *data2);
91:
92: /* These nodes represent handlers that are called as normal procedures
93: instead of longjmp'ed to. When these procs return, the next handler
94: in the chain is processed.
95: */
96: typedef struct { /* an alternative node in the handler chain */
97: struct _NXHandler *next; /* ptr to next handler */
98: AltProc *proc; /* proc to call */
99: void *context; /* blind data for client */
100: } AltHandler;
101:
102: static int ErrorBufferSize = 0;
103: static NXExceptionRaiser *ExceptionRaiser = &NXDefaultExceptionRaiser;
104:
105: /* Multiple thread support.
106: Basic strategy is to collect globals into a struct; singly link them
107: (probably not worth it put them in a hashtable) using the cthread id
108: as a key. Upon use, look up proper stack of handlers...
109: */
110:
111: typedef struct xxx {
112: NXHandler *handlerStack; /* start of handler chain */
113: AltHandler *altHandlers;
114: int altHandlersAlloced;
115: int altHandlersUsed;
116: cthread_t thread; /* key */
117: struct xxx *next; /* link */
118: } ExceptionHandlerStack;
119:
120: /* Allocate the handler stack for the first thread statically so that
121: we don't keep an extra page in the heap hot (the libsys data is
122: already hot). Also statically allocate a small number of altHandlers.
123: This should be the maximum typical depth of lockFocus'es. */
124:
125: static AltHandler BaseAltHandlers[16] = { 0 };
126:
127: static ExceptionHandlerStack Base =
128: {
129: NULL,
130: BaseAltHandlers,
131: sizeof (BaseAltHandlers) / sizeof (BaseAltHandlers[0]),
132: 0,
133: 0,
134: NULL
135: };
136:
137: static LOCK_T Lock = LOCK_INITIALIZER;
138:
139: #ifndef KERNEL
140: static void _NXClearExceptionStack (void);
141: extern void _set_cthread_free_callout (void (*) (void));
142: #endif KERNEL
143:
144: static ExceptionHandlerStack *addme (cthread_t self)
145: {
146: ExceptionHandlerStack *stack;
147:
148: LOCK (Lock); // lookup is thread safe; addition isn't
149:
150: #ifdef KERNEL
151: for (stack = &Base; stack; stack = stack->next)
152: if (stack->thread == 0) {
153: stack->thread = self;
154: UNLOCK (Lock);
155: return stack;
156: }
157: #else KERNEL
158: if (Base.thread == 0) // try statically allocated stack
159: {
160: Base.thread = self;
161: UNLOCK (Lock);
162: return &Base;
163: }
164:
165: /* Pass exception handler cleanup routine to cthread package. */
166: _set_cthread_free_callout (&_NXClearExceptionStack);
167: #endif KERNEL
168:
169: stack = calloc (sizeof (ExceptionHandlerStack), 1);
170: stack->thread = self;
171: stack->next = Base.next;
172: Base.next = stack; // insert atomically
173:
174: UNLOCK (Lock);
175:
176: return stack;
177: }
178:
179:
180: /*
181: Get callers thread & find | allocate a stack. Allocation is only
182: proper for some usages, but we don't check for them...
183: Also, these structs are not recovered upon thread death. XXX
184: */
185: static inline ExceptionHandlerStack *findme (void)
186: {
187: ExceptionHandlerStack *stack;
188: cthread_t self = cthread_self ();
189:
190: for (stack = &Base; stack; stack = stack->next)
191: if (stack->thread == self)
192: return stack;
193:
194: return addme (self);
195: }
196:
197: #ifdef KERNEL
198: /*
199: * Call back from thread_deallocate()
200: * to indicate that a thread is being
201: * destroyed. Kernel thread ids aren't
202: * reused the way that cthread ids are,
203: * so we have to nuke the value in the
204: * structure.
205: *
206: * XXX Should free data structures here.
207: */
208: void
209: _threadFreeExceptionStack(
210: thread_t thread
211: )
212: {
213: ExceptionHandlerStack *stack;
214:
215: // Don't allocate a stack for this thread if it doesn't already have one.
216: for (stack = &Base; stack; stack = stack->next)
217: if (stack->thread == thread)
218: break;
219:
220: if (stack)
221: {
222: stack->handlerStack = 0; // reset the handler chain
223: stack->altHandlersUsed = 0; // reset the alt handler count
224: stack->thread = 0;
225: }
226: }
227: #else KERNEL
228: /* cthreads will reuse a stack and a cthread_id.
229: * We provide this call-in to clean up matters
230: */
231: static void _NXClearExceptionStack (void)
232: {
233: ExceptionHandlerStack *stack;
234: cthread_t self = cthread_self ();
235:
236: // Don't allocate a stack for this thread if it doesn't already have one.
237: for (stack = &Base; stack; stack = stack->next)
238: if (stack->thread == self)
239: break;
240:
241: if (stack)
242: {
243: stack->handlerStack = 0; // reset the handler chain
244: stack->altHandlersUsed = 0; // reset the alt handler count
245: }
246: }
247: #endif KERNEL
248:
249: #define IS_ALT(ptr) ((int)(ptr) % 2)
250: #define NEXT_HANDLER(ptr) \
251: (IS_ALT(ptr) ? ALT_CODE_TO_PTR(ptr)->next : ((NXHandler *)ptr)->next)
252: #define ALT_PTR_TO_CODE(ptr) (((ptr) - me->altHandlers) * 2 + 1)
253: #define ALT_CODE_TO_PTR(code) (me->altHandlers + ((int)(code) - 1) / 2)
254:
255:
256: /* if the node passed in isnt on top of the stack, something's fishy */
257:
258: static void trickyRemoveHandler (NXHandler *handler, int removingAlt)
259: {
260: AltHandler *altNode;
261: NXHandler *node;
262: NXHandler **nodePtr;
263: ExceptionHandlerStack *me = findme ();
264:
265: /* try to find the node anywhere on the stack */
266: node = me->handlerStack;
267: while (node != handler && node)
268: {
269: /* Watch for attempts to remove handlers which are outside the
270: active portion of the stack. This happens when you return
271: from an NX_DURING context without removing the handler.
272: This code assumes the stack grows downward. */
273: #if hppa
274: /* stack grows upward */
275: if (IS_ALT (node) || (void *) node < (void *) &altNode)
276: node = NEXT_HANDLER (node);
277: else
278: #else
279: if (IS_ALT (node) || (void *) node > (void *) &altNode)
280: node = NEXT_HANDLER (node);
281: else
282: #endif
283: {
284: _NXLogError ("Exception handlers were not properly removed.");
285: abort ();
286: }
287: }
288:
289: if (node)
290: {
291: /*
292: * Clean off the stack up to the out of place node. If we are trying
293: * to remove an non-alt handler, we pop eveything off the stack
294: * including that handler, calling any alt procs along the way. If
295: * we are removing an alt handler, we leave all the non-alt handlers
296: * alone as we clean off the stack, but pop off all the alt handlers
297: * we find, including the node we were asked to remove.
298: */
299: if (!removingAlt)
300: _NXLogError ("Exception handlers were not properly removed.");
301:
302: nodePtr = &me->handlerStack;
303: do
304: {
305: node = *nodePtr;
306: if (IS_ALT(node))
307: {
308: altNode = ALT_CODE_TO_PTR(node);
309: if (removingAlt)
310: {
311: if (node == handler)
312: *nodePtr = altNode->next; /* del matching node */
313: else
314: nodePtr = &altNode->next; /* skip node */
315: }
316: else
317: {
318: if (node != handler)
319: (*altNode->proc)(altNode->context, 1, 0, 0);
320: me->altHandlersUsed = altNode - me->altHandlers;
321: *nodePtr = altNode->next; /* del any alt node */
322: }
323: }
324: else
325: {
326: if (removingAlt)
327: nodePtr = &node->next; /* skip node */
328: else
329: *nodePtr = node->next; /* nuke non-alt node */
330: }
331: }
332: while (node != handler);
333: }
334: else
335: {
336: #ifdef KERNEL
337: ;
338: #else /* KERNEL */
339: _NXLogError ("Attempt to remove unrecognized exception handler.");
340: #endif /* KERNEL */
341: }
342: }
343:
344:
345: NXHandler *_NXAddAltHandler (AltProc *proc, void *context)
346: {
347: AltHandler *new;
348: ExceptionHandlerStack *me = findme ();
349:
350: if (me->altHandlersUsed == me->altHandlersAlloced)
351: {
352: if (me->altHandlers == BaseAltHandlers)
353: {
354: me->altHandlers = malloc (++me->altHandlersAlloced *
355: sizeof(AltHandler));
356: bcopy (BaseAltHandlers, me->altHandlers, sizeof (BaseAltHandlers));
357: }
358: else
359: me->altHandlers = realloc (me->altHandlers, ++me->altHandlersAlloced *
360: sizeof(AltHandler));
361: }
362:
363: new = me->altHandlers + me->altHandlersUsed++;
364: new->next = me->handlerStack;
365: me->handlerStack = (NXHandler *) ALT_PTR_TO_CODE (new);
366: new->proc = proc;
367: new->context = context;
368: return me->handlerStack;
369: }
370:
371:
372: void _NXRemoveAltHandler (NXHandler *handler)
373: {
374: ExceptionHandlerStack *me;
375:
376: for (me = &Base; me; me = me->next)
377: if (me->handlerStack == handler)
378: {
379: AltHandler *altNode = ALT_CODE_TO_PTR (handler);
380:
381: me->altHandlersUsed = altNode - me->altHandlers;
382: me->handlerStack = altNode->next;
383: return;
384: }
385:
386: trickyRemoveHandler (handler, TRUE);
387: }
388:
389:
390: void _NXAddHandler (NXHandler *handler)
391: {
392: ExceptionHandlerStack *me = findme ();
393:
394: handler->next = me->handlerStack;
395: me->handlerStack = handler;
396: handler->code = 0;
397: }
398:
399:
400: void _NXRemoveHandler (NXHandler *handler)
401: {
402: ExceptionHandlerStack *me;
403:
404: for (me = &Base; me; me = me->next)
405: if (me->handlerStack == handler)
406: {
407: me->handlerStack = me->handlerStack->next;
408: return;
409: }
410:
411: trickyRemoveHandler (handler, FALSE);
412: }
413:
414:
415: void NXSetExceptionRaiser(NXExceptionRaiser *proc)
416: {
417: ExceptionRaiser = proc;
418: }
419:
420:
421: NXExceptionRaiser *NXGetExceptionRaiser (void)
422: {
423: return ExceptionRaiser;
424: }
425:
426: volatile void _NXRaiseError(int code, const void *data1, const void *data2)
427: {
428: (*ExceptionRaiser)(code, data1, data2);
429: abort (); /* we should never get here! */
430: }
431:
432:
433: /* forwards the error to the next handler */
434: volatile void NXDefaultExceptionRaiser(int code, const void *data1, const void *data2)
435: {
436: NXHandler *destination;
437: AltHandler *altDest;
438: ExceptionHandlerStack *me = findme ();
439:
440: while (1) {
441: destination = me->handlerStack;
442: if (!destination) {
443: if (_NXUncaughtExceptionHandler)
444: (*_NXUncaughtExceptionHandler)(code, data1, data2);
445: else {
446: #ifndef KERNEL
447: _NXLogError("Uncaught exception #%d\n", code);
448: #endif /* not KERNEL */
449: }
450: #ifdef KERNEL
451: panic("Uncaught exception");
452: #else /* KERNEL */
453: exit(-1);
454: #endif /* KERNEL */
455: } else if (IS_ALT(destination)) {
456: altDest = ALT_CODE_TO_PTR(destination);
457: me->handlerStack = altDest->next;
458: me->altHandlersUsed = altDest - me->altHandlers;
459: (*altDest->proc)(altDest->context, code, data1, data2);
460: } else {
461: destination->code = code;
462: destination->data1 = data1;
463: destination->data2 = data2;
464: me->handlerStack = destination->next;
465: _longjmp(destination->jumpState, 1);
466: }
467: }
468: }
469:
470:
471: static char *ErrorBuffer = NULL;
472: static int ErrorBufferOffset = 0;
473:
474: /* stack allocates some space from the error buffer */
475: void NXAllocErrorData(int size, void **data)
476: {
477: int goalSize;
478: static LOCK_T mylock = LOCK_INITIALIZER;
479:
480: LOCK (mylock);
481: goalSize = (ErrorBufferOffset + size + 7) & ~7;
482: if (goalSize > ErrorBufferSize) {
483: ErrorBuffer = realloc(ErrorBuffer, (unsigned)goalSize);
484: ErrorBufferSize = goalSize;
485: }
486: *data = ErrorBuffer + ErrorBufferOffset;
487: ErrorBufferOffset = goalSize;
488: UNLOCK (mylock);
489: }
490:
491:
492: void NXResetErrorData(void)
493: {
494: ErrorBufferOffset = 0; // multiple threads users must cooperate XXX
495: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.