about summary refs log tree commit diff
path: root/sysdeps/nptl/fork.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/nptl/fork.c')
-rw-r--r--sysdeps/nptl/fork.c53
1 files changed, 38 insertions, 15 deletions
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index 1a68cbd647..616d897a36 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -54,6 +54,12 @@ __libc_fork (void)
     struct used_handler *next;
   } *allp = NULL;
 
+  /* Determine if we are running multiple threads.  We skip some fork
+     handlers in the single-thread case, to make fork safer to use in
+     signal handlers.  POSIX requires that fork is async-signal-safe,
+     but our current fork implementation is not.  */
+  bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
+
   /* Run all the registered preparation handlers.  In reverse order.
      While doing this we build up a list of all the entries.  */
   struct fork_handler *runp;
@@ -109,12 +115,21 @@ __libc_fork (void)
       break;
     }
 
-  _IO_list_lock ();
+  /* If we are not running multiple threads, we do not have to
+     preserve lock state.  If fork runs from a signal handler, only
+     async-signal-safe functions can be used in the child.  These data
+     structures are only used by unsafe functions, so their state does
+     not matter if fork was called from a signal handler.  */
+  if (multiple_threads)
+    {
+      _IO_list_lock ();
 
-  /* Acquire malloc locks.  This needs to come last because fork
-     handlers may use malloc, and the libio list lock has an indirect
-     malloc dependency as well (via the getdelim function).  */
-  __malloc_fork_lock_parent ();
+      /* Acquire malloc locks.  This needs to come last because fork
+	 handlers may use malloc, and the libio list lock has an
+	 indirect malloc dependency as well (via the getdelim
+	 function).  */
+      __malloc_fork_lock_parent ();
+    }
 
 #ifndef NDEBUG
   pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid);
@@ -173,14 +188,18 @@ __libc_fork (void)
 # endif
 #endif
 
-      /* Release malloc locks.  */
-      __malloc_fork_unlock_child ();
+      /* Reset the lock state in the multi-threaded case.  */
+      if (multiple_threads)
+	{
+	  /* Release malloc locks.  */
+	  __malloc_fork_unlock_child ();
 
-      /* Reset the file list.  These are recursive mutexes.  */
-      fresetlockfiles ();
+	  /* Reset the file list.  These are recursive mutexes.  */
+	  fresetlockfiles ();
 
-      /* Reset locks in the I/O code.  */
-      _IO_list_resetlock ();
+	  /* Reset locks in the I/O code.  */
+	  _IO_list_resetlock ();
+	}
 
       /* Reset the lock the dynamic loader uses to protect its data.  */
       __rtld_lock_initialize (GL(dl_load_lock));
@@ -217,11 +236,15 @@ __libc_fork (void)
       /* Restore the PID value.  */
       THREAD_SETMEM (THREAD_SELF, pid, parentpid);
 
-      /* Release malloc locks, parent process variant.  */
-      __malloc_fork_unlock_parent ();
+      /* Release acquired locks in the multi-threaded case.  */
+      if (multiple_threads)
+	{
+	  /* Release malloc locks, parent process variant.  */
+	  __malloc_fork_unlock_parent ();
 
-      /* We execute this even if the 'fork' call failed.  */
-      _IO_list_unlock ();
+	  /* We execute this even if the 'fork' call failed.  */
+	  _IO_list_unlock ();
+	}
 
       /* Run the handlers registered for the parent.  */
       while (allp != NULL)