about 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.c72
1 files changed, 51 insertions, 21 deletions
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index b8b15743cd..66b1ba84c7 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -29,11 +29,7 @@ Cambridge, MA 02139, USA.  */
 
 
 /* Things that want to be locked while forking.  */
-struct
-  {
-    size_t n;
-    struct mutex *locks[0];
-  } _hurd_fork_locks;
+symbol_set_declare (_hurd_fork_locks)
 
 
 /* Things that want to be called before we fork, to prepare the parent for
@@ -62,7 +58,6 @@ __fork (void)
   pid_t pid;
   size_t i;
   error_t err;
-  thread_t thread_self = __mach_thread_self ();
   struct hurd_sigstate *volatile ss;
 
   ss = _hurd_self_sigstate ();
@@ -87,14 +82,32 @@ __fork (void)
       mach_msg_type_number_t nporttypes = 0;
       thread_t *threads = NULL;
       mach_msg_type_number_t nthreads = 0;
-      int ports_locked = 0;
+      int ports_locked = 0, stopped = 0;
+
+      void resume_threads (void)
+	{
+	  if (! stopped)
+	    return;
+
+	  assert (threads);
+
+	  for (i = 0; i < nthreads; ++i)
+	    if (threads[i] != ss->thread)
+	      __thread_resume (threads[i]);
+	  stopped = 0;
+	}
 
       /* Run things that prepare for forking before we create the task.  */
       RUN_HOOK (_hurd_fork_prepare_hook, ());
 
       /* 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]);
+      {
+	void *const *p;
+	for (p = symbol_set_first_element (_hurd_fork_locks);
+	     ! symbol_set_end_p (_hurd_fork_locks, p);
+	     ++p)
+	  __mutex_lock (*p);
+      }
       __mutex_lock (&_hurd_siglock);
       
       newtask = MACH_PORT_NULL;
@@ -108,8 +121,16 @@ __fork (void)
 	__spin_lock (&_hurd_ports[i].lock);
       ports_locked = 1;
 
-      /* Create the child task.  It will inherit a copy of our memory.  */
-      err = __task_create (__mach_task_self (), 1, &newtask);
+      /* Stop all other threads while copying the address space,
+	 so nothing changes.  */
+      err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
+      if (!err)
+        {
+          stopped = 1;
+
+	  /* Create the child task.  It will inherit a copy of our memory.  */
+	  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.  */
@@ -263,7 +284,7 @@ __fork (void)
 		  if (err = __proc_task2proc (portnames[i], newtask, &insert))
 		    LOSE;
 		}
-	      else if (portnames[i] == thread_self)
+	      else if (portnames[i] == ss->thread)
 		{
 		  /* For the name we use for our own thread port, we will
 		     insert the thread port for the child main user thread
@@ -372,6 +393,10 @@ __fork (void)
 	__spin_unlock (&_hurd_ports[i].lock);
       ports_locked = 0;
 
+      /* All state has now been copied from the parent.  It is safe to
+         resume other parent threads.  */
+      resume_threads ();
+
       /* Create the child main user thread and signal thread.  */
       if ((err = __thread_create (newtask, &thread)) ||
 	  (err = __thread_create (newtask, &sigthread)))
@@ -381,8 +406,8 @@ __fork (void)
          dead name rights with the names we want to give the thread ports
          in the child as placeholders.  Now deallocate them so we can use
          the names.  */
-      if ((err = __mach_port_deallocate (newtask, thread_self)) ||
-	  (err = __mach_port_insert_right (newtask, thread_self,
+      if ((err = __mach_port_deallocate (newtask, ss->thread)) ||
+	  (err = __mach_port_insert_right (newtask, ss->thread,
 					   thread, MACH_MSG_TYPE_COPY_SEND)))
 	LOSE;
       /* We have one extra user reference created at the beginning of this
@@ -390,9 +415,9 @@ __fork (void)
 	 accounted for in the child below).  This extra right gets consumed
 	 in the child by the store into _hurd_sigthread in the child fork.  */
       if (thread_refs > 1 &&
-	  (err = __mach_port_mod_refs (newtask, thread_self,
+	  (err = __mach_port_mod_refs (newtask, ss->thread,
 				       MACH_PORT_RIGHT_SEND,
-				       thread_refs - 1)))
+				       thread_refs)))
 	LOSE;
       if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
 	  && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
@@ -474,6 +499,8 @@ __fork (void)
 	for (i = 0; i < _hurd_nports; ++i)
 	  __spin_unlock (&_hurd_ports[i].lock);
 
+      resume_threads ();
+
       if (newtask != MACH_PORT_NULL)
 	{
 	  if (err)
@@ -486,8 +513,6 @@ __fork (void)
 	__mach_port_deallocate (__mach_task_self (), sigthread);
       if (newproc != MACH_PORT_NULL)
 	__mach_port_deallocate (__mach_task_self (), newproc);
-      if (thread_self != MACH_PORT_NULL)
-	__mach_port_deallocate (__mach_task_self (), thread_self);
 
       if (portnames)
 	__vm_deallocate (__mach_task_self (),
@@ -525,7 +550,7 @@ __fork (void)
 
       /* We are the only thread in this new task, so we will
 	 take the task-global signals.  */
-      _hurd_sigthread = thread_self;
+      _hurd_sigthread = ss->thread;
 
       /* Unchain the sigstate structures for threads that existed in the
 	 parent task but don't exist in this task (the child process).
@@ -575,8 +600,13 @@ __fork (void)
 
   /* Unlock things we locked before creating the child task.
      They are locked in both the parent and child tasks.  */
-  for (i = 0; i < _hurd_fork_locks.n; ++i)
-    __mutex_unlock (_hurd_fork_locks.locks[i]);
+  {
+    void *const *p;
+    for (p = symbol_set_first_element (_hurd_fork_locks);
+	 ! symbol_set_end_p (_hurd_fork_locks, p);
+	 ++p)
+      __mutex_unlock (*p);
+  }
 
   _hurd_critical_section_unlock (ss);