summary refs log tree commit diff
path: root/sysdeps/mach/hurd/fork.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/mach/hurd/fork.c')
-rw-r--r--sysdeps/mach/hurd/fork.c171
1 files changed, 93 insertions, 78 deletions
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index b9170f155e..8e625b8e5e 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -67,24 +67,14 @@ __fork (void)
   error_t err;
   thread_t thread_self = __mach_thread_self ();
   struct hurd_sigstate *volatile ss;
-  sigset_t pending;
-
-  void unlockss (void)
-    {
-      __spin_lock (&ss->lock);
-      ss->critical_section = 0;
-      pending = ss->pending & ~ss->blocked;
-      __spin_unlock (&ss->lock);
-      /* XXX Copying mutex into child and calling mutex_unlock lossy.  */
-      __mutex_unlock (&_hurd_siglock);
-      ss = NULL;		/* Make sure we crash if we use it again.  */
-    }
 
   ss = _hurd_self_sigstate ();
   __spin_lock (&ss->lock);
   ss->critical_section = 1;
   __spin_unlock (&ss->lock);
-  __mutex_lock (&_hurd_siglock);
+
+#undef	LOSE
+#define LOSE assert_perror (err) /* XXX */
 
   if (! setjmp (env))
     {
@@ -108,6 +98,7 @@ __fork (void)
       /* Lock things that want to be locked before we fork.  */
       for (i = 0; i < _hurd_fork_locks.n; ++i)
 	__mutex_lock (_hurd_fork_locks.locks[i]);
+      __mutex_lock (&_hurd_siglock);
       
       newtask = MACH_PORT_NULL;
       thread = sigthread = MACH_PORT_NULL;
@@ -121,31 +112,37 @@ __fork (void)
       ports_locked = 1;
 
       /* Create the child task.  It will inherit a copy of our memory.  */
-      if (err = __task_create (__mach_task_self (), 1, &newtask))
-	goto lose;
+      err = __task_create (__mach_task_self (), 1, &newtask);
+
+      /* Unlock the global signal state lock, so we do not
+	 block the signal thread any longer than necessary.  */
+      __mutex_unlock (&_hurd_siglock);
+
+      if (err)
+	LOSE;
 
       /* Fetch the names of all ports used in this task.  */
       if (err = __mach_port_names (__mach_task_self (),
 				   &portnames, &nportnames,
 				   &porttypes, &nporttypes))
-	goto lose;
+	LOSE;
       if (nportnames != nporttypes)
 	{
 	  err = EGRATUITOUS;
-	  goto lose;
+	  LOSE;
 	}
 
       /* Get send rights for all the threads in this task.
 	 We want to avoid giving these rights to the child.  */
       if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
-	goto lose;
+	LOSE;
 
       /* Get the child process's proc server port.  We will insert it into
 	 the child with the same name as we use for our own proc server
 	 port; and we will need it to set the child's message port.  */
       if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
 				  newtask, &newproc))
-	goto lose;
+	LOSE;
 
       /* Insert all our port rights into the child task.  */
       thread_refs = sigthread_refs = 0;
@@ -187,7 +184,7 @@ __fork (void)
 			   }));
 		}
 	      else if (err)
-		goto lose;
+		LOSE;
 	      if (porttypes[i] & MACH_PORT_TYPE_SEND)
 		{
 		  /* Give the child as many send rights for its receive
@@ -199,12 +196,12 @@ __fork (void)
 						  portnames[i],
 						  MACH_PORT_RIGHT_SEND,
 						  &refs))
-		    goto lose;
+		    LOSE;
 		  if (err = __mach_port_extract_right (newtask,
 						       portnames[i],
 						       MACH_MSG_TYPE_MAKE_SEND,
 						       &port, &poly))
-		    goto lose;
+		    LOSE;
 		  if (portnames[i] == _hurd_msgport)
 		    {
 		      /* We just created a receive right for the child's
@@ -213,7 +210,7 @@ __fork (void)
 			 for it, give it to the proc server.  */
 		      mach_port_t old;
 		      if (err = __proc_setmsgport (newproc, port, &old))
-			goto lose;
+			LOSE;
 		      if (old != MACH_PORT_NULL)
 			/* XXX what to do here? */
 			__mach_port_deallocate (__mach_task_self (), old);
@@ -222,13 +219,13 @@ __fork (void)
 						      portnames[i],
 						      port,
 						      MACH_MSG_TYPE_MOVE_SEND))
-		    goto lose;
+		    LOSE;
 		  if (refs > 1 &&
 		      (err = __mach_port_mod_refs (newtask,
 						   portnames[i],
 						   MACH_PORT_RIGHT_SEND,
 						   refs - 1)))
-		    goto lose;
+		    LOSE;
 		}
 	      if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
 		{
@@ -241,15 +238,16 @@ __fork (void)
 		       portnames[i],
 		       MACH_MSG_TYPE_MAKE_SEND_ONCE,
 		       &port, &poly))
-		    goto lose;
+		    LOSE;
 		  if (err = __mach_port_insert_right
 		      (newtask,
 		       portnames[i], port,
 		       MACH_MSG_TYPE_MOVE_SEND_ONCE))
-		    goto lose;
+		    LOSE;
 		}
 	    }
-	  else if (porttypes[i] & MACH_PORT_TYPE_SEND)
+	  else if (porttypes[i] &
+		   (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
 	    {
 	      /* This is a send right or a dead name.
 		 Give the child as many references for it as we have.  */
@@ -266,7 +264,7 @@ __fork (void)
 		{
 		  /* Get the proc server port for the new task.  */
 		  if (err = __proc_task2proc (portnames[i], newtask, &insert))
-		    goto lose;
+		    LOSE;
 		}
 	      else if (portnames[i] == thread_self)
 		{
@@ -281,7 +279,7 @@ __fork (void)
                      rights created when a thread is created).  */
 		  if (err = __mach_port_allocate_name
 		      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
-		    goto lose;
+		    LOSE;
 		}
 	      else if (portnames[i] == _hurd_msgport_thread)
 		/* For the name we use for our signal thread's thread port,
@@ -293,7 +291,7 @@ __fork (void)
 		  /* Allocate a dead name right as a placeholder.  */
 		  if (err = __mach_port_allocate_name
 		      (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
-		    goto lose;
+		    LOSE;
 		}
 	      else
 		{
@@ -313,38 +311,61 @@ __fork (void)
 					      portnames[i],
 					      MACH_PORT_RIGHT_SEND,
 					      record_refs ?: &refs))
-		goto lose;
+		LOSE;
 	      if (insert == MACH_PORT_NULL)
 		continue;
-	      /* Insert the chosen send right into the child.  */
-	      err = __mach_port_insert_right (newtask,
-					      portnames[i],
-					      insert,
-					      MACH_MSG_TYPE_COPY_SEND);
-	      if (err == KERN_NAME_EXISTS)
+	      if (insert == portnames[i] &&
+		  (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
+		/* This is a dead name; allocate another dead name
+		   with the same name in the child.  */
+	      allocate_dead_name:
+		err = __mach_port_allocate_name (newtask,
+						 MACH_PORT_RIGHT_DEAD_NAME,
+						 portnames[i]);
+	      else
+		/* Insert the chosen send right into the child.  */
+		err = __mach_port_insert_right (newtask,
+						portnames[i],
+						insert,
+						MACH_MSG_TYPE_COPY_SEND);
+	      switch (err)
 		{
-		  /* It already has a send right under this name (?!).
-		     Well, it starts out with a send right for its task
-		     port, and inherits the bootstrap and exception ports
-		     from us.  */
-		  mach_port_t childport;
-		  mach_msg_type_name_t poly;
-		  assert (__mach_port_extract_right (newtask, portnames[i],
-						     MACH_MSG_TYPE_COPY_SEND,
-						     &childport, &poly) == 0 &&
-			  childport == insert &&
-			  __mach_port_deallocate (__mach_task_self (),
-						  childport) == 0);
+		case KERN_NAME_EXISTS:
+		  {
+		    /* It already has a send right under this name (?!).
+		       Well, it starts out with a send right for its task
+		       port, and inherits the bootstrap and exception ports
+		       from us.  */
+		    mach_port_t childport;
+		    mach_msg_type_name_t poly;
+		    assert (__mach_port_extract_right (newtask, portnames[i],
+						       MACH_MSG_TYPE_COPY_SEND,
+						       &childport,
+						       &poly) == 0 &&
+			    childport == insert &&
+			    __mach_port_deallocate (__mach_task_self (),
+						    childport) == 0);
+		    break;
+		  }
+
+		case KERN_INVALID_CAPABILITY:
+		  /* The port just died.  It was a send right,
+		     and now it's a dead name.  */
+		  goto allocate_dead_name;
+
+		default:
+		  LOSE;
+		  break;
+
+		case KERN_SUCCESS:
+		  /* Give the child as many user references as we have.  */
+		  if (refs > 1 &&
+		      (err = __mach_port_mod_refs (newtask,
+						   portnames[i],
+						   MACH_PORT_RIGHT_SEND,
+						   refs - 1)))
+		    LOSE;
 		}
-	      else if (err)
-		goto lose;
-	      /* Give the child as many user references as we have.  */
-	      if (refs > 1 &&
-		  (err = __mach_port_mod_refs (newtask,
-					       portnames[i],
-					       MACH_PORT_RIGHT_SEND,
-					       refs - 1)))
-		goto lose;
 	    }
 	}
 
@@ -354,13 +375,10 @@ __fork (void)
 	__spin_unlock (&_hurd_ports[i].lock);
       ports_locked = 0;
 
-      /* Unlock the signal state.  The child must unlock its own copy too.  */
-      unlockss ();
-
       /* Create the child main user thread and signal thread.  */
       if ((err = __thread_create (newtask, &thread)) ||
 	  (err = __thread_create (newtask, &sigthread)))
-	goto lose;
+	LOSE;
 
       /* Insert send rights for those threads.  We previously allocated
          dead name rights with the names we want to give the thread ports
@@ -369,7 +387,7 @@ __fork (void)
       if ((err = __mach_port_deallocate (newtask, thread_self)) ||
 	  (err = __mach_port_insert_right (newtask, thread_self,
 					   thread, MACH_MSG_TYPE_COPY_SEND)))
-	goto lose;
+	LOSE;
       /* We have one extra user reference created at the beginning of this
 	 function, accounted for by mach_port_names (and which will thus be
 	 accounted for in the child below).  This extra right gets consumed
@@ -378,18 +396,18 @@ __fork (void)
 	  (err = __mach_port_mod_refs (newtask, thread_self,
 				       MACH_PORT_RIGHT_SEND,
 				       thread_refs - 1)))
-	goto lose;
+	LOSE;
       if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
 	  && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
 	      (err = __mach_port_insert_right (newtask, _hurd_msgport_thread,
 					       sigthread,
 					       MACH_MSG_TYPE_COPY_SEND))))
-	goto lose;
+	LOSE;
       if (sigthread_refs > 1 &&
 	  (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
 				       MACH_PORT_RIGHT_SEND,
 				       sigthread_refs - 1)))
-	goto lose;
+	LOSE;
 
       /* This seems like a convenient juncture to copy the proc server's
 	 idea of what addresses our argv and envp are found at from the
@@ -400,7 +418,7 @@ __fork (void)
 	err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
 	       ?: __proc_set_arg_locations (newproc, argv, envp));
 	if (err)
-	  goto lose;
+	  LOSE;
       }
 	    
       /* Set the child signal thread up to run the msgport server function
@@ -412,7 +430,7 @@ __fork (void)
       if (err = __thread_get_state (_hurd_msgport_thread,
 				    MACHINE_THREAD_STATE_FLAVOR,
 				    (natural_t *) &state, &statecount))
-	goto lose;
+	LOSE;
 #if STACK_GROWTH_UP
       state.SP = __hurd_sigthread_stack_base;
 #else
@@ -422,7 +440,7 @@ __fork (void)
 				   (unsigned long int) _hurd_msgport_receive);
       if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
 				    (natural_t *) &state, statecount))
-	goto lose;
+	LOSE;
       /* We do not thread_resume SIGTHREAD here because the child
 	 fork needs to do more setup before it can take signals.  */
 
@@ -430,13 +448,13 @@ __fork (void)
       _hurd_longjmp_thread_state (&state, env, 1);
       if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
 				    (natural_t *) &state, statecount))
-	goto lose;
+	LOSE;
 
       /* Get the PID of the child from the proc server.  We must do this
 	 before calling proc_child below, because at that point any
 	 authorized POSIX.1 process may kill the child task with SIGKILL.  */
       if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
-	goto lose;
+	LOSE;
 
       /* Register the child with the proc server.  It is important that
 	 this be that last thing we do before starting the child thread
@@ -445,7 +463,7 @@ __fork (void)
 	 this point, and the child must have a message port so it responds
 	 to POSIX.1 signals.  */
       if (err = __USEPORT (PROC, __proc_child (port, newtask)))
-	goto lose;
+	LOSE;
 
       /* This must be the absolutely last thing we do; we can't assume that
 	 the child will remain alive for even a moment once we do this.  We
@@ -527,9 +545,7 @@ __fork (void)
 	}
       ss->next = NULL;
       _hurd_sigstates = ss;
-
-      /* Unlock our copies of the signal state locks.  */
-      unlockss ();
+      __mutex_unlock (&_hurd_siglock);
 
       /* Fetch our new process IDs from the proc server.  No need to
 	 refetch our pgrp; it is always inherited from the parent (so
@@ -565,8 +581,7 @@ __fork (void)
   for (i = 0; i < _hurd_fork_locks.n; ++i)
     __mutex_unlock (_hurd_fork_locks.locks[i]);
 
-  if (pending)
-    __msg_sig_post (_hurd_msgport, 0, __mach_task_self ());
+  _hurd_critical_section_unlock (ss);
 
   return err ? __hurd_fail (err) : pid;
 }