about summary refs log tree commit diff
path: root/hurd/hurdsig.c
diff options
context:
space:
mode:
Diffstat (limited to 'hurd/hurdsig.c')
-rw-r--r--hurd/hurdsig.c181
1 files changed, 96 insertions, 85 deletions
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index 9414c059e3..7dd2b8c2fc 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;