diff options
Diffstat (limited to 'hurd/hurdsig.c')
-rw-r--r-- | hurd/hurdsig.c | 181 |
1 files changed, 96 insertions, 85 deletions
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c index 9414c05..7dd2b8c 100644 --- a/hurd/hurdsig.c +++ b/hurd/hurdsig.c @@ -41,6 +41,9 @@ thread_t _hurd_sigthread; /* Linked-list of per-thread signal state. */ struct hurd_sigstate *_hurd_sigstates; + +/* Timeout for RPC's after interrupt_operation. */ +mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 3000; static void default_sigaction (struct sigaction actions[NSIG]) @@ -76,9 +79,6 @@ _hurd_thread_sigstate (thread_t thread) __sigemptyset (&ss->pending); memset (&ss->sigaltstack, 0, sizeof (ss->sigaltstack)); ss->suspended = 0; -#ifdef noteven - __condition_init (&ss->arrived); -#endif ss->intr_port = MACH_PORT_NULL; ss->context = NULL; @@ -225,16 +225,17 @@ abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state, } /* Find the location of the MiG reply port cell in use by the thread whose - state is described by THREAD_STATE. Make sure that this location can be - set without faulting, or else return NULL. */ + state is described by THREAD_STATE. If SIGTHREAD is nonzero, make sure + that this location can be set without faulting, or else return NULL. */ static mach_port_t * -interrupted_reply_port_location (struct machine_thread_all_state *thread_state) +interrupted_reply_port_location (struct machine_thread_all_state *thread_state, + int sigthread) { mach_port_t *portloc = (mach_port_t *) __hurd_threadvar_location_from_sp (_HURD_THREADVAR_MIG_REPLY, (void *) thread_state->basic.SP); - if (_hurdsig_catch_fault (SIGSEGV)) + if (sigthread && _hurdsig_catch_fault (SIGSEGV)) { assert (_hurdsig_fault_sigcode == (long int) portloc); /* Faulted trying to read the stack. */ @@ -244,7 +245,8 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state) /* Fault now if this pointer is bogus. */ *(volatile mach_port_t *) portloc = *portloc; - _hurdsig_end_catch_fault (); + if (sigthread) + _hurdsig_end_catch_fault (); return portloc; } @@ -266,12 +268,14 @@ interrupted_reply_port_location (struct machine_thread_all_state *thread_state) be applied back to the thread if it might ever run again, else zero. */ static mach_port_t -abort_rpcs (struct hurd_sigstate *ss, int signo, - struct machine_thread_all_state *state, int *state_change, - mach_port_t *reply_port, mach_msg_type_name_t reply_port_type, - int untraced) +_hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, int sigthread, + struct machine_thread_all_state *state, int *state_change, + mach_port_t *reply_port, + mach_msg_type_name_t reply_port_type, + int untraced) { - mach_port_t msging_port; + extern const void _hurd_intr_rpc_msg_do_trap, _hurd_intr_rpc_msg_in_trap; + mach_port_t rcv_port = MACH_PORT_NULL; mach_port_t intr_port; *state_change = 0; @@ -285,71 +289,69 @@ abort_rpcs (struct hurd_sigstate *ss, int signo, receive completes immediately or aborts. */ abort_thread (ss, state, reply_port, reply_port_type, untraced); - if (_hurdsig_rcv_interrupted_p (state, &msging_port)) + if (state->basic.PC < (natural_t) &_hurd_intr_rpc_msg_in_trap) { - error_t err; - - /* The RPC request message was sent and the thread was waiting for - the reply message; now the message receive has been aborted, so - the mach_msg_call will return MACH_RCV_INTERRUPTED. We must tell - the server to interrupt the pending operation. The thread must - wait for the reply message before running the signal handler (to - guarantee that the operation has finished being interrupted), so - our nonzero return tells the trampoline code to finish the message - receive operation before running the handler. */ - - err = __interrupt_operation (intr_port); - - if (err) - { - mach_port_t *reply; - - /* The interrupt didn't work. - Destroy the receive right the thread is blocked on. */ - __mach_port_destroy (__mach_task_self (), msging_port); - - /* The system call return value register now contains - MACH_RCV_INTERRUPTED; when mach_msg resumes, it will retry the - call. Since we have just destroyed the receive right, the - retry will fail with MACH_RCV_INVALID_NAME. Instead, just - change the return value here to EINTR so mach_msg will not - retry and the EINTR error code will propagate up. */ - state->basic.SYSRETURN = EINTR; - *state_change = 1; - - /* If that was the thread's MiG reply port (which I think should - always be the case), clear the reply port cell so it won't be - reused. */ - reply = interrupted_reply_port_location (state); - if (reply != NULL && *reply == msging_port) - *reply = MACH_PORT_NULL; - } - - /* All threads whose RPCs were interrupted by the interrupt_operation - call above will retry their RPCs unless we clear SS->intr_port. - So we clear it for the thread taking a signal when SA_RESTART is - clear, so that its call returns EINTR. */ - if (!(ss->actions[signo].sa_flags & SA_RESTART)) - ss->intr_port = MACH_PORT_NULL; - - return err ? MACH_PORT_NULL : msging_port; + /* The thread is about to do the RPC, but hasn't yet entered + mach_msg. Mutate the thread's state so it knows not to try + the RPC. */ + MACHINE_THREAD_STATE_SET_PC (&state->basic, + &_hurd_intr_rpc_msg_in_trap); + state->basic.SYSRETURN = MACH_SEND_INTERRUPTED; + *state_change = 1; } + else if (state->basic.PC == (natural_t) &_hurd_intr_rpc_msg_in_trap && + /* The thread was blocked in the system call. After thread_abort, + the return value register indicates what state the RPC was in + when interrupted. */ + state->basic.SYSRETURN == MACH_RCV_INTERRUPTED) + { + /* The RPC request message was sent and the thread was waiting for + the reply message; now the message receive has been aborted, so + the mach_msg call will return MACH_RCV_INTERRUPTED. We must tell + the server to interrupt the pending operation. The thread must + wait for the reply message before running the signal handler (to + guarantee that the operation has finished being interrupted), so + our nonzero return tells the trampoline code to finish the message + receive operation before running the handler. */ + + mach_port_t *reply = interrupted_reply_port_location (state, + sigthread); + error_t err = __interrupt_operation (intr_port); + + if (err) + { + if (reply) + { + /* The interrupt didn't work. + Destroy the receive right the thread is blocked on. */ + __mach_port_destroy (__mach_task_self (), *reply); + *reply = MACH_PORT_NULL; + } - /* One of the following is true: - - 1. The RPC has not yet been sent. The thread will start its operation - after the signal has been handled. - - 2. The RPC has finished, but not yet cleared SS->intr_port. - The thread will clear SS->intr_port after running the handler. - - 3. The RPC request message was being sent was aborted. The mach_msg - system call will return MACH_SEND_INTERRUPTED, and HURD_EINTR_RPC will - notice the interruption (either retrying the RPC or returning EINTR). */ + /* The system call return value register now contains + MACH_RCV_INTERRUPTED; when mach_msg resumes, it will retry the + call. Since we have just destroyed the receive right, the + retry will fail with MACH_RCV_INVALID_NAME. Instead, just + change the return value here to EINTR so mach_msg will not + retry and the EINTR error code will propagate up. */ + state->basic.SYSRETURN = EINTR; + *state_change = 1; + } + else if (reply) + rcv_port = *reply; + + /* All threads whose RPCs were interrupted by the interrupt_operation + call above will retry their RPCs unless we clear SS->intr_port. + So we clear it for the thread taking a signal when SA_RESTART is + clear, so that its call returns EINTR. */ + if (! signo || !(ss->actions[signo].sa_flags & SA_RESTART)) + ss->intr_port = MACH_PORT_NULL; + } - return MACH_PORT_NULL; + return rcv_port; } + /* Abort the RPCs being run by all threads but this one; all other threads should be suspended. If LIVE is nonzero, those threads may run again, so they should be adjusted as necessary to be @@ -387,8 +389,9 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live) /* Abort any operation in progress with interrupt_operation. Record the reply port the thread is waiting on. We will wait for all the replies below. */ - reply_ports[nthreads++] = abort_rpcs (ss, signo, state, &state_changed, - NULL, 0, 0); + reply_ports[nthreads++] = _hurdsig_abort_rpcs (ss, signo, 1, + state, &state_changed, + NULL, 0, 0); if (state_changed && live) /* Aborting the RPC needed to change this thread's state, and it might ever run again. So write back its state. */ @@ -403,11 +406,18 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live) { error_t err; mach_msg_header_t head; - err = __mach_msg (&head, MACH_RCV_MSG, 0, sizeof head, + err = __mach_msg (&head, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof head, reply_ports[nthreads], - MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - if (err != MACH_RCV_TOO_LARGE) - assert_perror (err); + _hurd_interrupted_rpc_timeout, MACH_PORT_NULL); + switch (err) + { + case MACH_RCV_TIMED_OUT: + case MACH_RCV_TOO_LARGE: + break; + + default: + assert_perror (err); + } } } @@ -745,7 +755,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, if (! machine_get_basic_state (ss->thread, &thread_state)) goto sigbomb; - loc = interrupted_reply_port_location (&thread_state); + loc = interrupted_reply_port_location (&thread_state, 1); if (loc && *loc != MACH_PORT_NULL) /* This is the reply port for the context which called sigreturn. Since we are abandoning that context entirely @@ -759,11 +769,11 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, } else { - wait_for_reply = (abort_rpcs (ss, signo, - &thread_state, &state_changed, - &reply_port, reply_port_type, - untraced) - != MACH_PORT_NULL); + wait_for_reply + = (_hurdsig_abort_rpcs (ss, signo, 1, + &thread_state, &state_changed, + &reply_port, reply_port_type, untraced) + != MACH_PORT_NULL); if (ss->critical_section) { @@ -790,7 +800,8 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss, { /* Fetch the thread variable for the MiG reply port, and set it to MACH_PORT_NULL. */ - mach_port_t *loc = interrupted_reply_port_location (&thread_state); + mach_port_t *loc = interrupted_reply_port_location (&thread_state, + 1); if (loc) { scp->sc_reply_port = *loc; |