📄 except.c
字号:
caddr_t(*exc_bound(pattern, env))() register int pattern; /* pattern bound to handler */ caddr_t *env; /* environment to be returned to caller */{ register __queue_t *q; register caddr_t (*func)(); CLR_ERROR(); LOCK(); q = FIRSTQ(&__Curproc->lwp_exchandles); while ((q != QNULL) && (((exccontext_t *)q)->exc_pattern != pattern)) { q = q->q_next; } if (q == QNULL) { UNLOCK(); return (CFUNCNULL); } else { *env = (((exccontext_t *)q)->exc_env); func = ((exccontext_t *)q)->exc_func; UNLOCK(); return (func); }}/* * exc_notify() -- PRIMITIVE. * Utility for using excbound: if there is a non-null function * bound to the handler bound to "pattern", invoke it with the * bound argument. Otherwise, raise an exception with the pattern. * Returns two kinds of results: int for exc_raise (the pattern) or * anything (whatever the handler function returns). * The (int) cast is therefore only to make lint happy, not what really is * returned. */intexc_notify(pattern) int pattern;{ register caddr_t (*func)(); caddr_t env; CLR_ERROR(); func = exc_bound(pattern, &env); if (func != CFUNCNULL) return ((int)func(env)); else return (exc_raise(pattern));}/* Do the real work of exc_raise with room above on the stack to work with */STATIC intexcraise(pattern) int pattern; /* pattern to match the one set by exc_handle() */{ register int *proc; register exccontext_t *cntxt; register int *c; register int i; int popcount = 0; int *sp; caddr_t (*func)(); caddr_t env; int patt; extern int __excreturn(); CLR_ERROR(); LOCK(); /* look for a match, but don't skip backout handlers */ for (cntxt = (exccontext_t *)FIRSTQ(&__Curproc->lwp_exchandles); cntxt != EXCNULL; cntxt = cntxt->exc_next) { if ((cntxt->exc_pattern != pattern) && (cntxt->exc_pattern != CATCHALL)) { popcount++; /* don't zap now in case no match */ } else { break; } } ERROR((cntxt == EXCNULL), LE_NONEXIST); /* * if last handler in a procedure and not an * exit handler, remove __exccleanup */ if ((cntxt->exc_refcnt == 0) && (cntxt->exc_pattern != EXITPATTERN)) { *cntxt->exc_retaddr = cntxt->exc_return; } /* * alter the actual context so CSWTCH() will work. */ sp = (int *)cntxt->exc_regs[SP]; c = cntxt->exc_regs; proc = __Curproc->lwp_context; for (i = 0; i < MC_GENERALREGS; i++) proc[i] = c[i]; /* * we will push 3 things for __excreturn (see low.s) to use * so subtract 3 items: * __excreturn, pattern, return address. * __excreturn is the address to go to upon CSWTCH; * excpattern will be popped by __excreturn into d0, * and __excreturn will rts to exc_pc. * Important that pattern be on stack * since it could be lost in a global if * an asynchrouous event caused an exception to occur. */ __Curproc->lwp_context[SP] = (int)sp - (3 * sizeof (int)); *(--sp) = cntxt->exc_pc; *(--sp) = pattern; *(--sp) = (int) __excreturn; /* destroy intervening contexts incl. this one */ for (i = 0; i <= popcount; i++) { REM_QUEUE((exccontext_t *), &__Curproc->lwp_exchandles, cntxt); func = cntxt->exc_func; env = cntxt->exc_env; patt = cntxt->exc_pattern; FREECHUNK(ExcType, cntxt); if (patt == EXITPATTERN) { /* * Do exit handling en route to a handler above. * Note that we're on the client stack. * If excraise were implemented as a system call, * (i.e., on a sep. stack here), * should context switch to this context and * have the cleanup code reraise the exception * (int lwp_needreraise to hold pattern added to cntxt.) * UNLOCK so can call nugget cleanly (e.g., mon_exit). */ UNLOCK(); (void) (*func)(env); LOCK(); } } /* now a normal context switch will put us in the handler context */ CSWTCH(); /* NOTREACHED */}/* * exc_raise() -- PRIMITIVE. * Raise an exception by searching for a matching handler, installing * its context in the process's context, and doing a context switch. * exc_raise will munge the stack starting with the point where * the exc_handle call was made. This could overwrite the stack * where exc_raise is executing, so we make a dummy procedure * call to guarantee room on the stack for exc_raise to execute safely. */intexc_raise(pattern) int pattern;{ int dummy[EXCSTKSZ];#ifdef lint dummy[0] = dummy[0];#endif lint if ((pattern == EXITPATTERN) || (pattern == -1)) { SET_ERROR(__Curproc, LE_INVALIDARG); return (-1); } return (excraise(pattern));}/* * __exccleanup is an assembly language entry point * that is called when a procedure that established * one or more exception handlers returns. (it is called only once, * regardless of how many handlers the procedure set up). * It's function is to call exchelpclean(). * It must provide a way so it (__exccleanup) can return * a different place (the original procedure return point). * Note that exchelpclean() is called as a byproduct of the * original procedure returning normally so the stack and * registers are all as they should be. Thus, from the * point of view of the original procedure, it looks like * it called __exccleanup immediately upon return. * Finally, note that it's important that contexts are * NOT allocated on the stack since we would be clobbering * them with the locals, etc. here. We could get around * this by writing the whole thing in assembler, not calling * any procedures (including this helper procedure), and carefully * avoid messing with the stack. * However, in environments (e.g., sys V) where interrupts can't * be saved on an alternate stack, we could have interrupts * clobbering the stack (and thus any cntxt info) on the sly. */void__exchelpclean(){ register exccontext_t *cntxt = EXCNULL; register int retloc; register qheader_t *q = &__Curproc->lwp_exchandles; register int *retaddr; int patt; caddr_t env; int refcnt; caddr_t (*func)(); CLR_ERROR(); LOCK(); /* * Discard all unused handlers (including CATCHALL). * Must be at least one, since we're here cleaning up. */ while (!ISEMPTYQ(q)) { REM_QUEUE((exccontext_t *), q, cntxt); func = cntxt->exc_func; env = cntxt->exc_env; patt = cntxt->exc_pattern; refcnt = cntxt->exc_refcnt; retaddr = cntxt->exc_retaddr; retloc = cntxt->exc_return; FREECHUNK(ExcType, cntxt); if (patt == 0) { UNLOCK(); (void) (*func)(env); LOCK(); } if (refcnt == 0) break; } /* * return address must be on stack so we don't * lose it (a global can be overwritten if not locked) * in case of an interrupt. exchelpclean() left room where * the original return address was for it to be restored. * We restore the original return address there, return to * __exccleanup to restore any registers used by exchelpclean(), * and then execute the original return. * At this point, we have the initial context (refcnt == 0) * to get the right return adddress from. */ *retaddr = retloc; UNLOCK();}/* clean up any exception handlers held by a dying process */void__exceptcleanup(corpse) lwp_t *corpse;{ register exccontext_t *cntxt; while (!ISEMPTYQ(&corpse->lwp_exchandles)) { REM_QUEUE((exccontext_t *), &corpse->lwp_exchandles, cntxt); LWPTRACE(tr_EXCEPT, 1, ("freeing exception %x\n", cntxt)); FREECHUNK(ExcType, cntxt); }}/* allocate a cache of exception contexts */void__init_exc(){ ExcType = __allocinit(sizeof (exccontext_t), NUMHANDLERS, IFUNCNULL, FALSE);}/* * What to do when a trap occurs. Called from low.s * If any sort of handler was established, raise the exception. * Otherwise, destroy the offending thread. */int__exc_trap(trapid) int trapid; /* UNIX signal causing trap */{ register caddr_t (*func)(); caddr_t env; int result; func = exc_bound(trapid, &env); if (func != CFUNCNULL) return ((int)func(env)); result = exc_raise(trapid); if (result != -1) { return (result); } else { (void)lwp_destroy(SELF); /* NOTREACHED */ }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -