error_t hurd_thread_cancel (thread_t thread) { struct hurd_sigstate *ss = _hurd_thread_sigstate (thread); struct machine_thread_all_state state; int state_change; error_t err; if (! ss) return EINVAL; if (ss == _hurd_self_sigstate ()) { /* We are cancelling ourselves, so it is easy to succeed quickly. Since this function is not a cancellation point, we just leave the flag set pending the next cancellation point (hurd_check_cancel or RPC) and return success. */ ss->cancel = 1; return 0; } assert (! __spin_lock_locked (&ss->critical_section_lock)); __spin_lock (&ss->critical_section_lock); __spin_lock (&ss->lock); err = __thread_suspend (thread); __spin_unlock (&ss->lock); if (! err) { /* Set the flag telling the thread its operation is being cancelled. */ ss->cancel = 1; /* Interrupt any interruptible RPC now in progress. */ state.set = 0; _hurdsig_abort_rpcs (ss, 0, 0, &state, &state_change, NULL, 0, 0); if (state_change) err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR, (natural_t *) &state.basic, MACHINE_THREAD_STATE_COUNT); if (ss->cancel_hook) /* The code being cancelled has a special wakeup function. Calling this should make the thread wake up and check the cancellation flag. */ (*ss->cancel_hook) (); __thread_resume (thread); } _hurd_critical_section_unlock (ss); return err; }
static void abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live) { /* We can just loop over the sigstates. Any thread doing something interruptible must have one. We needn't bother locking because all other threads are stopped. */ struct hurd_sigstate *ss; size_t nthreads; mach_port_t *reply_ports; /* First loop over the sigstates to count them. We need to know how big a vector we will need for REPLY_PORTS. */ nthreads = 0; for (ss = _hurd_sigstates; ss != NULL; ss = ss->next) ++nthreads; reply_ports = alloca (nthreads * sizeof *reply_ports); nthreads = 0; for (ss = _hurd_sigstates; ss != NULL; ss = ss->next, ++nthreads) if (ss->thread == _hurd_msgport_thread) reply_ports[nthreads] = MACH_PORT_NULL; else { int state_changed; state->set = 0; /* Reset scratch area. */ /* 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] = _hurdsig_abort_rpcs (ss, signo, 1, state, &state_changed, NULL); if (live) { if (reply_ports[nthreads] != MACH_PORT_NULL) { /* We will wait for the reply to this RPC below, so the thread must issue a new RPC rather than waiting for the reply to the one it sent. */ state->basic.SYSRETURN = EINTR; state_changed = 1; } if (state_changed) /* Aborting the RPC needed to change this thread's state, and it might ever run again. So write back its state. */ __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR, (natural_t *) &state->basic, MACHINE_THREAD_STATE_COUNT); } } /* Wait for replies from all the successfully interrupted RPCs. */ while (nthreads-- > 0) if (reply_ports[nthreads] != MACH_PORT_NULL) { error_t err; mach_msg_header_t head; err = __mach_msg (&head, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof head, reply_ports[nthreads], _hurd_interrupted_rpc_timeout, MACH_PORT_NULL); switch (err) { case MACH_RCV_TIMED_OUT: case MACH_RCV_TOO_LARGE: break; default: assert_perror (err); } } }