Ignore:
Timestamp:
Oct 1, 2012, 10:30:32 AM (7 years ago)
Author:
gb
Message:

Try to minimize Mach dependencies on Darwin; in particular, use POSIX
signal handling to handle exceptions (as is done on other *nix platforms.)

Use sigaltstack() on Darwin; it still seems to have problems, but at least
doesn't (usually) try to force all threads to use the same alt stack. (That
just never gets old somehow ...)

Lots of stuff removed; lots of (mostly small, mostly obvious) changes to
support the above.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/source/lisp-kernel/arm-exceptions.c

    r15370 r15470  
    14581458     can return to the kernel/to the Mach exception handler).
    14591459  */
    1460   if (!use_mach_exception_handling) {
    1461     exit_signal_handler(tcr, old_valence, old_last_lisp_frame);
    1462     raise_pending_interrupt(tcr);
    1463   }
     1460  exit_signal_handler(tcr, old_valence, old_last_lisp_frame);
     1461  raise_pending_interrupt(tcr);
    14641462}
    14651463
     
    19041902install_pmcl_exception_handlers()
    19051903{
    1906 #ifdef DARWIN
    1907   extern Boolean use_mach_exception_handling;
    1908 #endif
    1909 
    1910   Boolean install_signal_handlers_for_exceptions =
    1911 #ifdef DARWIN
    1912     !use_mach_exception_handling
    1913 #else
    1914     true
    1915 #endif
    1916     ;
    1917   if (install_signal_handlers_for_exceptions) {
    1918     install_signal_handler(SIGILL, (void *)sigill_handler, RESERVE_FOR_LISP);
    1919     install_signal_handler(SIGSEGV, (void *)ALTSTACK(signal_handler),
     1904  install_signal_handler(SIGILL, (void *)sigill_handler, RESERVE_FOR_LISP);
     1905  install_signal_handler(SIGSEGV, (void *)ALTSTACK(signal_handler),
     1906                         RESERVE_FOR_LISP|ON_ALTSTACK);
     1907  install_signal_handler(SIGBUS, (void *)ALTSTACK(signal_handler),
    19201908                           RESERVE_FOR_LISP|ON_ALTSTACK);
    1921     install_signal_handler(SIGBUS, (void *)ALTSTACK(signal_handler),
    1922                            RESERVE_FOR_LISP|ON_ALTSTACK);
    1923 
    1924   }
    1925  
    19261909  install_signal_handler(SIGNAL_FOR_PROCESS_INTERRUPT,
    19271910                         (void *)interrupt_handler, RESERVE_FOR_LISP);
     
    20632046
    20642047
    2065 #ifdef DARWIN
    2066 
    2067 
    2068 #define TCR_FROM_EXCEPTION_PORT(p) ((TCR *)((natural)p))
    2069 #define TCR_TO_EXCEPTION_PORT(tcr) ((mach_port_t)((natural)(tcr)))
    2070 
    2071 
    2072 
    2073 #define LISP_EXCEPTIONS_HANDLED_MASK \
    2074  (EXC_MASK_SOFTWARE | EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC)
    2075 
    2076 /* (logcount LISP_EXCEPTIONS_HANDLED_MASK) */
    2077 #define NUM_LISP_EXCEPTIONS_HANDLED 4
    2078 
    2079 typedef struct {
    2080   int foreign_exception_port_count;
    2081   exception_mask_t         masks[NUM_LISP_EXCEPTIONS_HANDLED];
    2082   mach_port_t              ports[NUM_LISP_EXCEPTIONS_HANDLED];
    2083   exception_behavior_t behaviors[NUM_LISP_EXCEPTIONS_HANDLED];
    2084   thread_state_flavor_t  flavors[NUM_LISP_EXCEPTIONS_HANDLED];
    2085 } MACH_foreign_exception_state;
    2086 
    2087 
    2088 
    2089 
    2090 /*
    2091   Mach's exception mechanism works a little better than its signal
    2092   mechanism (and, not incidentally, it gets along with GDB a lot
    2093   better.
    2094 
    2095   Initially, we install an exception handler to handle each native
    2096   thread's exceptions.  This process involves creating a distinguished
    2097   thread which listens for kernel exception messages on a set of
    2098   0 or more thread exception ports.  As threads are created, they're
    2099   added to that port set; a thread's exception port is destroyed
    2100   (and therefore removed from the port set) when the thread exits.
    2101 
    2102   A few exceptions can be handled directly in the handler thread;
    2103   others require that we resume the user thread (and that the
    2104   exception thread resumes listening for exceptions.)  The user
    2105   thread might eventually want to return to the original context
    2106   (possibly modified somewhat.)
    2107 
    2108   As it turns out, the simplest way to force the faulting user
    2109   thread to handle its own exceptions is to do pretty much what
    2110   signal() does: the exception handlng thread sets up a sigcontext
    2111   on the user thread's stack and forces the user thread to resume
    2112   execution as if a signal handler had been called with that
    2113   context as an argument.  We can use a distinguished UUO at a
    2114   distinguished address to do something like sigreturn(); that'll
    2115   have the effect of resuming the user thread's execution in
    2116   the (pseudo-) signal context.
    2117 
    2118   Since:
    2119     a) we have miles of code in C and in Lisp that knows how to
    2120     deal with Linux sigcontexts
    2121     b) Linux sigcontexts contain a little more useful information
    2122     (the DAR, DSISR, etc.) than their Darwin counterparts
    2123     c) we have to create a sigcontext ourselves when calling out
    2124     to the user thread: we aren't really generating a signal, just
    2125     leveraging existing signal-handling code.
    2126 
    2127   we create a Linux sigcontext struct.
    2128 
    2129   Simple ?  Hopefully from the outside it is ...
    2130 
    2131   We want the process of passing a thread's own context to it to
    2132   appear to be atomic: in particular, we don't want the GC to suspend
    2133   a thread that's had an exception but has not yet had its user-level
    2134   exception handler called, and we don't want the thread's exception
    2135   context to be modified by a GC while the Mach handler thread is
    2136   copying it around.  On Linux (and on Jaguar), we avoid this issue
    2137   because (a) the kernel sets up the user-level signal handler and
    2138   (b) the signal handler blocks signals (including the signal used
    2139   by the GC to suspend threads) until tcr->xframe is set up.
    2140 
    2141   The GC and the Mach server thread therefore contend for the lock
    2142   "mach_exception_lock".  The Mach server thread holds the lock
    2143   when copying exception information between the kernel and the
    2144   user thread; the GC holds this lock during most of its execution
    2145   (delaying exception processing until it can be done without
    2146   GC interference.)
    2147 
    2148 */
    2149 
    2150 
    2151 #define TRUNC_DOWN(a,b,c)  (((((natural)a)-(b))/(c)) * (c))
    2152 
    2153 void
    2154 fatal_mach_error(char *format, ...);
    2155 
    2156 #define MACH_CHECK_ERROR(context,x) if (x != KERN_SUCCESS) {fatal_mach_error("Mach error while %s : %d", context, x);}
    2157 
    2158 
    2159 void
    2160 restore_mach_thread_state(mach_port_t thread, ExceptionInformation *pseudosigcontext)
    2161 {
    2162   kern_return_t kret;
    2163   _STRUCT_MCONTEXT *mc = UC_MCONTEXT(pseudosigcontext);
    2164 
    2165   /* Set the thread's FP state from the pseudosigcontext */
    2166   kret = thread_set_state(thread,
    2167                           ARM_VFP_STATE,
    2168                           (thread_state_t)&(mc->__fs),
    2169                           ARM_VFP_STATE_COUNT);
    2170 
    2171   MACH_CHECK_ERROR("setting thread FP state", kret);
    2172 
    2173   /* The thread'll be as good as new ... */
    2174   kret = thread_set_state(thread,
    2175                           MACHINE_THREAD_STATE,
    2176                           (thread_state_t)&(mc->__ss),
    2177                           MACHINE_THREAD_STATE_COUNT);
    2178   MACH_CHECK_ERROR("setting thread state", kret);
    2179 
    2180 
    2181 /* This code runs in the exception handling thread, in response
    2182    to an attempt to execute the UU0 at "pseudo_sigreturn" (e.g.,
    2183    in response to a call to pseudo_sigreturn() from the specified
    2184    user thread.
    2185    Find that context (the user thread's R3 points to it), then
    2186    use that context to set the user thread's state.  When this
    2187    function's caller returns, the Mach kernel will resume the
    2188    user thread.
    2189 */
    2190 
    2191 kern_return_t
    2192 do_pseudo_sigreturn(mach_port_t thread, TCR *tcr)
    2193 {
    2194   ExceptionInformation *xp;
    2195 
    2196 #ifdef DEBUG_MACH_EXCEPTIONS
    2197   fprintf(dbgout, "doing pseudo_sigreturn for 0x%x\n",tcr);
    2198 #endif
    2199   tcr->last_lisp_frame = *((natural *)(tcr->last_lisp_frame));
    2200   xp = tcr->pending_exception_context;
    2201   if (xp) {
    2202     tcr->pending_exception_context = NULL;
    2203     tcr->valence = TCR_STATE_LISP;
    2204     restore_mach_thread_state(thread, xp);
    2205     raise_pending_interrupt(tcr);
    2206   } else {
    2207     Bug(NULL, "no xp here!\n");
    2208   }
    2209 #ifdef DEBUG_MACH_EXCEPTIONS
    2210   fprintf(dbgout, "did pseudo_sigreturn for 0x%x\n",tcr);
    2211 #endif
    2212   return KERN_SUCCESS;
    2213 
    2214 
    2215 ExceptionInformation *
    2216 create_thread_context_frame(mach_port_t thread,
    2217                             natural *new_stack_top)
    2218 {
    2219   arm_thread_state_t ts;
    2220   mach_msg_type_number_t thread_state_count;
    2221   kern_return_t result;
    2222   ExceptionInformation *pseudosigcontext;
    2223   _STRUCT_MCONTEXT *mc;
    2224   natural stackp, backlink;
    2225 
    2226   thread_state_count = MACHINE_THREAD_STATE_COUNT;
    2227   result = thread_get_state(thread,
    2228                             ARM_THREAD_STATE,   /* GPRs, some SPRs  */
    2229                             (thread_state_t)&ts,
    2230                             &thread_state_count);
    2231  
    2232   if (result != KERN_SUCCESS) {
    2233     get_tcr(true);
    2234     Bug(NULL, "Exception thread can't obtain thread state, Mach result = %d", result);
    2235   }
    2236   stackp = ts.__sp;
    2237   backlink = stackp;
    2238 
    2239   stackp -= sizeof(*pseudosigcontext);
    2240   pseudosigcontext = (ExceptionInformation *) ptr_from_lispobj(stackp);
    2241 
    2242   stackp -= sizeof(*mc);
    2243   mc = (_STRUCT_MCONTEXT *) ptr_from_lispobj(stackp);
    2244   memmove(&(mc->__ss),&ts,sizeof(ts));
    2245 
    2246   thread_state_count = ARM_VFP_STATE_COUNT;
    2247   thread_get_state(thread,
    2248                    ARM_VFP_STATE,
    2249                    (thread_state_t)&(mc->__fs),
    2250                    &thread_state_count);
    2251 
    2252 
    2253   thread_state_count = ARM_EXCEPTION_STATE_COUNT;
    2254   thread_get_state(thread,
    2255                    ARM_EXCEPTION_STATE,
    2256                    (thread_state_t)&(mc->__es),
    2257                    &thread_state_count);
    2258 
    2259 
    2260   UC_MCONTEXT(pseudosigcontext) = mc;
    2261   if (new_stack_top) {
    2262     *new_stack_top = stackp;
    2263   }
    2264   return pseudosigcontext;
    2265 }
    2266 
    2267 /*
    2268   This code sets up the user thread so that it executes a "pseudo-signal
    2269   handler" function when it resumes.  Create a linux sigcontext struct
    2270   on the thread's stack and pass it as an argument to the pseudo-signal
    2271   handler.
    2272 
    2273   Things are set up so that the handler "returns to" pseudo_sigreturn(),
    2274   which will restore the thread's context.
    2275 
    2276   If the handler invokes code that throws (or otherwise never sigreturn()'s
    2277   to the context), that's fine.
    2278 
    2279   Actually, check that: throw (and variants) may need to be careful and
    2280   pop the tcr's xframe list until it's younger than any frame being
    2281   entered.
    2282 */
    2283 
    2284 int
    2285 setup_signal_frame(mach_port_t thread,
    2286                    void *handler_address,
    2287                    int signum,
    2288                    int code,
    2289                    TCR *tcr)
    2290 {
    2291   arm_thread_state_t ts;
    2292   ExceptionInformation *pseudosigcontext;
    2293   int old_valence = tcr->valence;
    2294   natural stackp, *pstackp;
    2295 
    2296 #ifdef DEBUG_MACH_EXCEPTIONS
    2297   fprintf(dbgout,"Setting up exception handling for 0x%x\n", tcr);
    2298 #endif
    2299   pseudosigcontext = create_thread_context_frame(thread, &stackp);
    2300   pstackp = (natural *)stackp;
    2301   *--pstackp = tcr->last_lisp_frame;
    2302   stackp = (natural)pstackp;
    2303   tcr->last_lisp_frame = stackp;
    2304   pseudosigcontext->uc_onstack = 0;
    2305   pseudosigcontext->uc_sigmask = (sigset_t) 0;
    2306   pseudosigcontext->uc_mcsize = ARM_MCONTEXT_SIZE;
    2307   tcr->pending_exception_context = pseudosigcontext;
    2308   tcr->valence = TCR_STATE_EXCEPTION_WAIT;
    2309  
    2310 
    2311   /*
    2312      It seems like we've created a  sigcontext on the thread's
    2313      stack.  Set things up so that we call the handler (with appropriate
    2314      args) when the thread's resumed.
    2315   */
    2316 
    2317   ts.__pc = (natural) handler_address;
    2318   ts.__sp = stackp;
    2319   ts.__r[0] = signum;
    2320   ts.__r[1] = (natural)pseudosigcontext;
    2321   ts.__r[2] = (natural)tcr;
    2322   ts.__r[3] = (natural)old_valence;
    2323   ts.__lr = (natural)pseudo_sigreturn;
    2324   ts.__cpsr = xpPSR(pseudosigcontext);
    2325 
    2326 
    2327   thread_set_state(thread,
    2328                    MACHINE_THREAD_STATE,
    2329                    (thread_state_t)&ts,
    2330                    MACHINE_THREAD_STATE_COUNT);
    2331 #ifdef DEBUG_MACH_EXCEPTIONS
    2332   fprintf(dbgout,"Set up exception context for 0x%x at 0x%x\n", tcr, tcr->pending_exception_context);
    2333 #endif
    2334   return 0;
    2335 }
    2336 
    2337 
    2338 void
    2339 pseudo_signal_handler(int signum, ExceptionInformation *context, TCR *tcr, int old_valence)
    2340 {
    2341   signal_handler(signum, NULL, context, tcr, old_valence, 0);
    2342 }
    2343 
    2344 
    2345 int
    2346 thread_set_fp_exceptions_enabled(mach_port_t thread, Boolean enabled)
    2347 {
    2348   /* Likely hopeless. */
    2349   return 0;
    2350 }
    2351 
    2352 /*
    2353   This function runs in the exception handling thread.  It's
    2354   called (by this precise name) from the library function "exc_server()"
    2355   when the thread's exception ports are set up.  (exc_server() is called
    2356   via mach_msg_server(), which is a function that waits for and dispatches
    2357   on exception messages from the Mach kernel.)
    2358 
    2359   This checks to see if the exception was caused by a pseudo_sigreturn()
    2360   UUO; if so, it arranges for the thread to have its state restored
    2361   from the specified context.
    2362 
    2363   Otherwise, it tries to map the exception to a signal number and
    2364   arranges that the thread run a "pseudo signal handler" to handle
    2365   the exception.
    2366 
    2367   Some exceptions could and should be handled here directly.
    2368 */
    2369 
    2370 kern_return_t
    2371 catch_exception_raise(mach_port_t exception_port,
    2372                       mach_port_t thread,
    2373                       mach_port_t task,
    2374                       exception_type_t exception,
    2375                       exception_data_t code_vector,
    2376                       mach_msg_type_number_t code_count)
    2377 {
    2378   int signum = 0, code = *code_vector;
    2379   TCR *tcr = TCR_FROM_EXCEPTION_PORT(exception_port);
    2380   kern_return_t kret;
    2381 
    2382 #ifdef DEBUG_MACH_EXCEPTIONS
    2383   fprintf(dbgout, "obtaining Mach exception lock in exception thread\n");
    2384 #endif
    2385 
    2386   if (tcr->flags & (1<<TCR_FLAG_BIT_PENDING_EXCEPTION)) {
    2387     CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PENDING_EXCEPTION);
    2388   }
    2389   /* On the ARM, code_vector[1] contains the undefined instruction
    2390      in this case, not its address.  */
    2391   if ((exception == EXC_BAD_INSTRUCTION) &&
    2392       (code_vector[0] == EXC_ARM_UNDEFINED) &&
    2393       (code_vector[1] == PSEUDO_SIGRETURN_UUO)) {
    2394     kret = do_pseudo_sigreturn(thread, tcr);
    2395   } else if (tcr->flags & (1<<TCR_FLAG_BIT_PROPAGATE_EXCEPTION)) {
    2396     CLR_TCR_FLAG(tcr,TCR_FLAG_BIT_PROPAGATE_EXCEPTION);
    2397     kret = 17;
    2398   } else {
    2399     switch (exception) {
    2400     case EXC_BAD_ACCESS:
    2401       signum = SIGSEGV;
    2402       break;
    2403        
    2404     case EXC_BAD_INSTRUCTION:
    2405       signum = SIGILL;
    2406       break;
    2407      
    2408       break;
    2409      
    2410     case EXC_ARITHMETIC:
    2411       signum = SIGFPE;
    2412       break;
    2413 
    2414     default:
    2415       break;
    2416     }
    2417     if (signum) {
    2418       kret = setup_signal_frame(thread,
    2419                                 (void *)pseudo_signal_handler,
    2420                                 signum,
    2421                                 code,
    2422                                 tcr);
    2423 #if 0
    2424       fprintf(dbgout, "Setup pseudosignal handling in 0x%x\n",tcr);
    2425 #endif
    2426 
    2427     } else {
    2428       kret = 17;
    2429     }
    2430   }
    2431 
    2432   return kret;
    2433 }
    2434 
    2435 
    2436 
    2437 typedef struct {
    2438   mach_msg_header_t Head;
    2439   /* start of the kernel processed data */
    2440   mach_msg_body_t msgh_body;
    2441   mach_msg_port_descriptor_t thread;
    2442   mach_msg_port_descriptor_t task;
    2443   /* end of the kernel processed data */
    2444   NDR_record_t NDR;
    2445   exception_type_t exception;
    2446   mach_msg_type_number_t codeCnt;
    2447   integer_t code[2];
    2448   mach_msg_trailer_t trailer;
    2449 } exceptionRequest;
    2450 
    2451 
    2452 boolean_t
    2453 openmcl_exc_server(mach_msg_header_t *in, mach_msg_header_t *out)
    2454 {
    2455   static NDR_record_t _NDR = {0};
    2456   kern_return_t handled;
    2457   mig_reply_error_t *reply = (mig_reply_error_t *) out;
    2458   exceptionRequest *req = (exceptionRequest *) in;
    2459 
    2460   reply->NDR = _NDR;
    2461 
    2462   out->msgh_bits = in->msgh_bits & MACH_MSGH_BITS_REMOTE_MASK;
    2463   out->msgh_remote_port = in->msgh_remote_port;
    2464   out->msgh_size = sizeof(mach_msg_header_t)+(3 * sizeof(unsigned));
    2465   out->msgh_local_port = MACH_PORT_NULL;
    2466   out->msgh_id = in->msgh_id+100;
    2467 
    2468   /* Could handle other exception flavors in the range 2401-2403 */
    2469 
    2470 
    2471   if (in->msgh_id != 2401) {
    2472     reply->RetCode = MIG_BAD_ID;
    2473     return FALSE;
    2474   }
    2475   handled = catch_exception_raise(req->Head.msgh_local_port,
    2476                                   req->thread.name,
    2477                                   req->task.name,
    2478                                   req->exception,
    2479                                   req->code,
    2480                                   req->codeCnt);
    2481   reply->RetCode = handled;
    2482   return TRUE;
    2483 }
    2484 
    2485 /*
    2486   The initial function for an exception-handling thread.
    2487 */
    2488 
    2489 void *
    2490 exception_handler_proc(void *arg)
    2491 {
    2492   extern boolean_t exc_server();
    2493   mach_port_t p = TCR_TO_EXCEPTION_PORT(arg);
    2494 
    2495   mach_msg_server(openmcl_exc_server, 2048, p, 0);
    2496   /* Should never return. */
    2497   abort();
    2498 }
    2499 
    2500 
    2501 
    2502 mach_port_t
    2503 mach_exception_port_set()
    2504 {
    2505   static mach_port_t __exception_port_set = MACH_PORT_NULL;
    2506   kern_return_t kret; 
    2507   if (__exception_port_set == MACH_PORT_NULL) {
    2508     kret = mach_port_allocate(mach_task_self(),
    2509                               MACH_PORT_RIGHT_PORT_SET,
    2510                               &__exception_port_set);
    2511     MACH_CHECK_ERROR("allocating thread exception_ports",kret);
    2512     create_system_thread(0,
    2513                          NULL,
    2514                          exception_handler_proc,
    2515                          (void *)((natural)__exception_port_set));
    2516   }
    2517   return __exception_port_set;
    2518 }
    2519 
    2520 /*
    2521   Setup a new thread to handle those exceptions specified by
    2522   the mask "which".  This involves creating a special Mach
    2523   message port, telling the Mach kernel to send exception
    2524   messages for the calling thread to that port, and setting
    2525   up a handler thread which listens for and responds to
    2526   those messages.
    2527 
    2528 */
    2529 
    2530 /*
    2531   Establish the lisp thread's TCR as its exception port, and determine
    2532   whether any other ports have been established by foreign code for
    2533   exceptions that lisp cares about.
    2534 
    2535   If this happens at all, it should happen on return from foreign
    2536   code and on entry to lisp code via a callback.
    2537 
    2538   This is a lot of trouble (and overhead) to support Java, or other
    2539   embeddable systems that clobber their caller's thread exception ports.
    2540  
    2541 */
    2542 kern_return_t
    2543 tcr_establish_exception_port(TCR *tcr, mach_port_t thread)
    2544 {
    2545   kern_return_t kret;
    2546   MACH_foreign_exception_state *fxs = (MACH_foreign_exception_state *)tcr->native_thread_info;
    2547   int i;
    2548   unsigned n = NUM_LISP_EXCEPTIONS_HANDLED;
    2549   mach_port_t lisp_port = TCR_TO_EXCEPTION_PORT(tcr), foreign_port;
    2550   exception_mask_t mask = 0;
    2551 
    2552   kret = thread_swap_exception_ports(thread,
    2553                                      LISP_EXCEPTIONS_HANDLED_MASK,
    2554                                      lisp_port,
    2555                                      EXCEPTION_DEFAULT,
    2556                                      THREAD_STATE_NONE,
    2557                                      fxs->masks,
    2558                                      &n,
    2559                                      fxs->ports,
    2560                                      fxs->behaviors,
    2561                                      fxs->flavors);
    2562   if (kret == KERN_SUCCESS) {
    2563     fxs->foreign_exception_port_count = n;
    2564     for (i = 0; i < n; i ++) {
    2565       foreign_port = fxs->ports[i];
    2566 
    2567       if ((foreign_port != lisp_port) &&
    2568           (foreign_port != MACH_PORT_NULL)) {
    2569         mask |= fxs->masks[i];
    2570       }
    2571     }
    2572     tcr->foreign_exception_status = (int) mask;
    2573   }
    2574   return kret;
    2575 }
    2576 
    2577 kern_return_t
    2578 tcr_establish_lisp_exception_port(TCR *tcr)
    2579 {
    2580   return tcr_establish_exception_port(tcr, (mach_port_t)((natural)tcr->native_thread_id));
    2581 }
    2582 
    2583 /*
    2584   Do this when calling out to or returning from foreign code, if
    2585   any conflicting foreign exception ports were established when we
    2586   last entered lisp code.
    2587 */
    2588 kern_return_t
    2589 restore_foreign_exception_ports(TCR *tcr)
    2590 {
    2591   exception_mask_t m = (exception_mask_t) tcr->foreign_exception_status;
    2592  
    2593   if (m) {
    2594     MACH_foreign_exception_state *fxs  =
    2595       (MACH_foreign_exception_state *) tcr->native_thread_info;
    2596     int i, n = fxs->foreign_exception_port_count;
    2597     exception_mask_t tm;
    2598 
    2599     for (i = 0; i < n; i++) {
    2600       if ((tm = fxs->masks[i]) & m) {
    2601         thread_set_exception_ports((mach_port_t)((natural)tcr->native_thread_id),
    2602                                    tm,
    2603                                    fxs->ports[i],
    2604                                    fxs->behaviors[i],
    2605                                    fxs->flavors[i]);
    2606       }
    2607     }
    2608   }
    2609 }
    2610                                    
    2611 
    2612 /*
    2613   This assumes that a Mach port (to be used as the thread's exception port) whose
    2614   "name" matches the TCR's 32-bit address has already been allocated.
    2615 */
    2616 
    2617 kern_return_t
    2618 setup_mach_exception_handling(TCR *tcr)
    2619 {
    2620   mach_port_t
    2621     thread_exception_port = TCR_TO_EXCEPTION_PORT(tcr),
    2622     task_self = mach_task_self();
    2623   kern_return_t kret;
    2624 
    2625   kret = mach_port_insert_right(task_self,
    2626                                 thread_exception_port,
    2627                                 thread_exception_port,
    2628                                 MACH_MSG_TYPE_MAKE_SEND);
    2629   MACH_CHECK_ERROR("adding send right to exception_port",kret);
    2630 
    2631   kret = tcr_establish_exception_port(tcr, (mach_port_t)((natural) tcr->native_thread_id));
    2632   if (kret == KERN_SUCCESS) {
    2633     mach_port_t exception_port_set = mach_exception_port_set();
    2634 
    2635     kret = mach_port_move_member(task_self,
    2636                                  thread_exception_port,
    2637                                  exception_port_set);
    2638   }
    2639   return kret;
    2640 }
    2641 
    2642 void
    2643 darwin_exception_init(TCR *tcr)
    2644 {
    2645   void tcr_monitor_exception_handling(TCR*, Boolean);
    2646   kern_return_t kret;
    2647   MACH_foreign_exception_state *fxs =
    2648     calloc(1, sizeof(MACH_foreign_exception_state));
    2649  
    2650   tcr->native_thread_info = (void *) fxs;
    2651 
    2652   if ((kret = setup_mach_exception_handling(tcr))
    2653       != KERN_SUCCESS) {
    2654     fprintf(dbgout, "Couldn't setup exception handler - error = %d\n", kret);
    2655     terminate_lisp();
    2656   }
    2657 }
    2658 
    2659 /*
    2660   The tcr is the "name" of the corresponding thread's exception port.
    2661   Destroying the port should remove it from all port sets of which it's
    2662   a member (notably, the exception port set.)
    2663 */
    2664 void
    2665 darwin_exception_cleanup(TCR *tcr)
    2666 {
    2667   void *fxs = tcr->native_thread_info;
    2668   extern Boolean use_mach_exception_handling;
    2669 
    2670   if (fxs) {
    2671     tcr->native_thread_info = NULL;
    2672     free(fxs);
    2673   }
    2674   if (use_mach_exception_handling) {
    2675     mach_port_deallocate(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
    2676     mach_port_destroy(mach_task_self(),TCR_TO_EXCEPTION_PORT(tcr));
    2677   }
    2678 }
    2679 
    2680 
    2681 Boolean
    2682 suspend_mach_thread(mach_port_t mach_thread)
    2683 {
    2684   kern_return_t status;
    2685   Boolean aborted = false;
    2686  
    2687   do {
    2688     aborted = false;
    2689     status = thread_suspend(mach_thread);
    2690     if (status == KERN_SUCCESS) {
    2691       status = thread_abort_safely(mach_thread);
    2692       if (status == KERN_SUCCESS) {
    2693         aborted = true;
    2694       } else {
    2695         fprintf(dbgout, "abort failed on thread = 0x%x\n",mach_thread);
    2696         thread_resume(mach_thread);
    2697       }
    2698     } else {
    2699       return false;
    2700     }
    2701   } while (! aborted);
    2702   return true;
    2703 }
    2704 
    2705 /*
    2706   Only do this if pthread_kill indicated that the pthread isn't
    2707   listening to signals anymore, as can happen as soon as pthread_exit()
    2708   is called on Darwin.  The thread could still call out to lisp as it
    2709   is exiting, so we need another way to suspend it in this case.
    2710 */
    2711 Boolean
    2712 mach_suspend_tcr(TCR *tcr)
    2713 {
    2714   mach_port_t mach_thread = (mach_port_t)((natural)( tcr->native_thread_id));
    2715   ExceptionInformation *pseudosigcontext;
    2716   Boolean result = false;
    2717  
    2718   result = suspend_mach_thread(mach_thread);
    2719   if (result) {
    2720     pseudosigcontext = create_thread_context_frame(mach_thread, NULL);
    2721     pseudosigcontext->uc_onstack = 0;
    2722     pseudosigcontext->uc_sigmask = (sigset_t) 0;
    2723     tcr->suspend_context = pseudosigcontext;
    2724   }
    2725   return result;
    2726 }
    2727 
    2728 void
    2729 mach_resume_tcr(TCR *tcr)
    2730 {
    2731   ExceptionInformation *xp;
    2732   mach_port_t mach_thread = (mach_port_t)((natural)(tcr->native_thread_id));
    2733  
    2734   xp = tcr->suspend_context;
    2735 #ifdef DEBUG_MACH_EXCEPTIONS
    2736   fprintf(dbgout, "resuming TCR 0x%x, pending_exception_context = 0x%x\n",
    2737           tcr, tcr->pending_exception_context);
    2738 #endif
    2739   tcr->suspend_context = NULL;
    2740   restore_mach_thread_state(mach_thread, xp);
    2741 #ifdef DEBUG_MACH_EXCEPTIONS
    2742   fprintf(dbgout, "restored state in TCR 0x%x, pending_exception_context = 0x%x\n",
    2743           tcr, tcr->pending_exception_context);
    2744 #endif
    2745   thread_resume(mach_thread);
    2746 }
    2747 
    2748 void
    2749 fatal_mach_error(char *format, ...)
    2750 {
    2751   va_list args;
    2752   char s[512];
    2753  
    2754 
    2755   va_start(args, format);
    2756   vsnprintf(s, sizeof(s),format, args);
    2757   va_end(args);
    2758 
    2759   Fatal("Mach error", s);
    2760 }
    2761 
    2762 void
    2763 pseudo_interrupt_handler(int signum, ExceptionInformation *context)
    2764 {
    2765   interrupt_handler(signum, NULL, context);
    2766 }
    2767 
    2768 int
    2769 mach_raise_thread_interrupt(TCR *target)
    2770 {
    2771   mach_port_t mach_thread = (mach_port_t)((natural)(target->native_thread_id));
    2772   kern_return_t kret;
    2773   Boolean result = false;
    2774   TCR *current = get_tcr(false);
    2775   thread_basic_info_data_t info;
    2776   mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;
    2777 
    2778   LOCK(lisp_global(TCR_AREA_LOCK), current);
    2779 
    2780   if (suspend_mach_thread(mach_thread)) {
    2781     if (thread_info(mach_thread,
    2782                     THREAD_BASIC_INFO,
    2783                     (thread_info_t)&info,
    2784                     &info_count) == KERN_SUCCESS) {
    2785       if (info.suspend_count == 1) {
    2786         if ((target->valence == TCR_STATE_LISP) &&
    2787             (!target->unwinding) &&
    2788             (TCR_INTERRUPT_LEVEL(target) >= 0)) {
    2789           kret = setup_signal_frame(mach_thread,
    2790                                     (void *)pseudo_interrupt_handler,
    2791                                     SIGNAL_FOR_PROCESS_INTERRUPT,
    2792                                     0,
    2793                                     target);
    2794           if (kret == KERN_SUCCESS) {
    2795             result = true;
    2796           }
    2797         }
    2798       }
    2799     }
    2800     if (! result) {
    2801       target->interrupt_pending = 1 << fixnumshift;
    2802     }
    2803     thread_resume(mach_thread);
    2804    
    2805   }
    2806   UNLOCK(lisp_global(TCR_AREA_LOCK), current);
    2807   return 0;
    2808 }
    2809 
    2810 #endif
Note: See TracChangeset for help on using the changeset viewer.