|
|
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.