about summary refs log tree commit diff
path: root/linuxthreads
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads')
-rw-r--r--linuxthreads/ChangeLog23
-rw-r--r--linuxthreads/internals.h2
-rw-r--r--linuxthreads/linuxthreads.texi192
-rw-r--r--linuxthreads/lockfile.c23
-rw-r--r--linuxthreads/ptfork.c2
5 files changed, 186 insertions, 56 deletions
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index b0647f2477..90c1631880 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,26 @@
+2000-06-04  Kaz Kylheku  <kaz@ashi.footprints.net>
+
+	Added missing fork time handling of global libio lock.
+
+	* lockfile.c (__fresetlockfiles): Now also resets the list lock,
+	not just the individual stream locks. Rewritten to use new
+	iterator interface provided by libio rather than accessing
+	global variable.
+
+	* lockfile.c (__flockfilelist, _funlockfilelist): New functions
+	which lock and unlock the stream list using the new interface
+	provied by libio.
+	* internals.h: Likewise.
+
+	* ptfork.c (__fork): Now calls __flockfilelist before fork,
+	and __funlockfilelist in the parent after the fork.
+	Child still calls __fresetlockfiles as before.
+
+	* linuxthreads.texi: Now explains what happens to streams at
+	fork time. Also whole new section on forking and thread added.
+	Definition of pthread_atfork moved out of Miscellaneous Functions
+	to this new section.
+
 2000-06-04  Jakub Jelinek  <jakub@redhat.com>
 
 	* sysdeps/sparc/sparc32/sparcv9/pspinlock.c (__pthread_spin_lock):
diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h
index 3790efed3f..c523d6f23b 100644
--- a/linuxthreads/internals.h
+++ b/linuxthreads/internals.h
@@ -418,6 +418,8 @@ void __pthread_reset_main_thread(void);
 void __pthread_once_fork_prepare(void);
 void __pthread_once_fork_parent(void);
 void __pthread_once_fork_child(void);
+void __flockfilelist(void);
+void __funlockfilelist(void);
 void __fresetlockfiles(void);
 void __pthread_manager_adjust_prio(int thread_prio);
 void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif);
diff --git a/linuxthreads/linuxthreads.texi b/linuxthreads/linuxthreads.texi
index f50199e9fc..a4ad820110 100644
--- a/linuxthreads/linuxthreads.texi
+++ b/linuxthreads/linuxthreads.texi
@@ -27,6 +27,10 @@ use @var{errno}.
                                   different threads.
 * Threads and Signal Handling:: Why you should avoid mixing the two, and
                                   how to do it if you must.
+* Threads and Fork::            Interactions between threads and the 
+                                  @code{fork} function.
+* Streams and Fork::            Interactions between stdio streams and 
+                                  @code{fork}.
 * Miscellaneous Thread Functions:: A grab bag of utility routines.
 @end menu
 
@@ -1237,54 +1241,37 @@ threads must not attach their own signal handlers to these signals, or
 alternatively they should all block these signals (which is recommended
 anyway).
 
-@node Miscellaneous Thread Functions
-@section Miscellaneous Thread Functions
-
-@comment pthread.h
-@comment POSIX
-@deftypefun {pthread_t} pthread_self (@var{void})
-@code{pthread_self} returns the thread identifier for the calling thread.
-@end deftypefun
-
-@comment pthread.h
-@comment POSIX
-@deftypefun int pthread_equal (pthread_t thread1, pthread_t thread2)
-@code{pthread_equal} determines if two thread identifiers refer to the same
-thread.
-
-A non-zero value is returned if @var{thread1} and @var{thread2} refer to
-the same thread. Otherwise, 0 is returned.
-@end deftypefun
-
-@comment pthread.h
-@comment POSIX
-@deftypefun int pthread_detach (pthread_t @var{th})
-@code{pthread_detach} puts the thread @var{th} in the detached
-state. This guarantees that the memory resources consumed by @var{th}
-will be freed immediately when @var{th} terminates. However, this
-prevents other threads from synchronizing on the termination of @var{th}
-using @code{pthread_join}.
-
-A thread can be created initially in the detached state, using the
-@code{detachstate} attribute to @code{pthread_create}. In contrast,
-@code{pthread_detach} applies to threads created in the joinable state,
-and which need to be put in the detached state later.
-
-After @code{pthread_detach} completes, subsequent attempts to perform
-@code{pthread_join} on @var{th} will fail. If another thread is already
-joining the thread @var{th} at the time @code{pthread_detach} is called,
-@code{pthread_detach} does nothing and leaves @var{th} in the joinable
-state.
-
-On success, 0 is returned. On error, one of the following codes is
-returned:
-@table @code
-@item ESRCH
-No thread could be found corresponding to that specified by @var{th}
-@item EINVAL
-The thread @var{th} is already in the detached state
-@end table
-@end deftypefun
+@node Threads and Fork
+@section Threads and Fork
+
+It's not intuitively obvious what should happen when a multi-threaded POSIX
+process calls @code{fork}. Not only are the semantics tricky, but you may
+need to write code that does the right thing at fork time even if that code
+doesn't use the @code{fork} function. Moreover, you need to be aware of
+interaction between @code{fork} and some library features like
+@code{pthread_once} and stdio streams.
+
+When @code{fork} is called by one of the threads of a process, it creates a new
+process which is copy of the  calling process. Effectively, in addition to
+copying certain system objects, the function takes a snapshot of the memory
+areas of the parent process, and creates identical areas in the child. 
+To make matters more complicated, with threads it's possible for two or more
+threads to concurrently call fork to create two or more child processes.
+
+The child process has a copy of the address space of the parent, but it does
+not inherit any of its threads. Execution of the child process is carried out
+by a new thread which returns from @code{fork} function with a return value of
+zero; it is the only thread in the child process.  Because threads are not
+inherited across fork, issues arise. At the time of the call to @code{fork},
+threads in the parent process other than the one calling @code{fork} may have
+been executing critical regions of code.  As a result, the child process may
+get a copy of objects that are not in a well-defined state.  This potential
+problem affects all components of the program.
+
+Any program component which will continue being used in a child process must
+correctly handle its state during @code{fork}. For this purpose, the POSIX
+interface provides the special function @code{pthread_atfork} for installing
+pointers to handler functions which are called from within @code{fork}.
 
 @comment pthread.h
 @comment POSIX
@@ -1336,12 +1323,109 @@ their current locking state, but only the calling thread: other threads
 are not running in the child process. Thus, if a mutex is locked by a
 thread other than the thread calling @code{fork}, that mutex will remain
 locked forever in the child process, possibly blocking the execution of
-the child process. To avoid this, install handlers with
-@code{pthread_atfork} as follows: the @var{prepare} handler locks the
-global mutexes (in locking order), and the @var{parent} and @var{child}
-handlers unlock them (in reverse order). Alternatively, @var{prepare}
-and @var{parent} can be set to @code{NULL} and @var{child} to a function
-that calls @code{pthread_mutex_init} on the global mutexes.
+the child process. Or if some shared data, such as a linked list, was in the
+middle of being updated by a thread in the parent process, the child 
+will get a copy of the incompletely updated data which it cannot use.
+
+To avoid this, install handlers with @code{pthread_atfork} as follows: have the
+@var{prepare} handler lock the mutexes (in locking order), and the
+@var{parent} handler unlock the mutexes. The @var{child} handler should reset
+the mutexes using @code{pthread_mutex_init}, as well as any other
+synchronization objects such as condition variables.
+
+Locking the global mutexes before the fork ensures that all other threads are
+locked out of the critical regions of code protected by those mutexes.  Thus
+when @code{fork} takes a snapshot of the parent's address space, that snapshot
+will copy valid, stable data.  Resetting the synchronization objects in the
+child process will ensure they are properly cleansed of any artifacts from the
+threading subsystem of the parent process. For example, a mutex may inherit
+a wait queue of threads waiting for the lock; this wait queue makes no sense
+in the child process. Initializing the mutex takes care of this.
+
+@node Streams and Fork
+@section Streams and Fork
+
+The GNU standard I/O library has an internal mutex which guards the internal
+linked list of all standard C FILE objects. This mutex is properly taken care
+of during @code{fork} so that the child receives an intact copy of the list.
+This allows the @code{fopen} function, and related stream-creating functions,
+to work correctly in the child process, since these functions need to insert
+into the list.
+
+However, the individual stream locks are not completely taken care of.  Thus
+unless the multithreaded application takes special precautions in its use of
+@code{fork}, the child process might not be able to safely use the streams that
+it inherited from the parent.   In general, for any given open stream in the
+parent that is to be used by the child process, the application must ensure
+that that stream is not in use by another thread when @code{fork} is called.
+Otherwise an inconsistent copy of the stream object be produced. An easy way to
+ensure this is to use @code{flockfile} to lock the stream prior to calling
+@code{fork} and then unlock it with @code{funlockfile} inside the parent
+process, provided that the parent's threads properly honor these locks.
+Nothing special needs to be done in the child process, since the library
+internally resets all stream locks. 
+
+Note that the stream locks are not shared between the parent and child.
+For example, even if you ensure that, say, the stream @code{stdout} is properly
+treated and can be safely used in the child, the stream locks do not provide
+an exclusion mechanism between the parent and child. If both processes write
+to @code{stdout}, strangely interleaved output may result regardless of
+the explicit use of @code{flockfile} or implicit locks.
+
+Also note that these provisions are a GNU extension; other systems might not
+provide any way for streams to be used in the child of a multithreaded process.
+POSIX requires that such a child process confines itself to calling only
+asynchronous safe functions, which excludes much of the library, including
+standard I/O.
+
+@node Miscellaneous Thread Functions
+@section Miscellaneous Thread Functions
+
+@comment pthread.h
+@comment POSIX
+@deftypefun {pthread_t} pthread_self (@var{void})
+@code{pthread_self} returns the thread identifier for the calling thread.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_equal (pthread_t thread1, pthread_t thread2)
+@code{pthread_equal} determines if two thread identifiers refer to the same
+thread.
+
+A non-zero value is returned if @var{thread1} and @var{thread2} refer to
+the same thread. Otherwise, 0 is returned.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_detach (pthread_t @var{th})
+@code{pthread_detach} puts the thread @var{th} in the detached
+state. This guarantees that the memory resources consumed by @var{th}
+will be freed immediately when @var{th} terminates. However, this
+prevents other threads from synchronizing on the termination of @var{th}
+using @code{pthread_join}.
+
+A thread can be created initially in the detached state, using the
+@code{detachstate} attribute to @code{pthread_create}. In contrast,
+@code{pthread_detach} applies to threads created in the joinable state,
+and which need to be put in the detached state later.
+
+After @code{pthread_detach} completes, subsequent attempts to perform
+@code{pthread_join} on @var{th} will fail. If another thread is already
+joining the thread @var{th} at the time @code{pthread_detach} is called,
+@code{pthread_detach} does nothing and leaves @var{th} in the joinable
+state.
+
+On success, 0 is returned. On error, one of the following codes is
+returned:
+@table @code
+@item ESRCH
+No thread could be found corresponding to that specified by @var{th}
+@item EINVAL
+The thread @var{th} is already in the detached state
+@end table
+@end deftypefun
 
 @comment pthread.h
 @comment GNU
diff --git a/linuxthreads/lockfile.c b/linuxthreads/lockfile.c
index 0c9cf27591..b6f98a44be 100644
--- a/linuxthreads/lockfile.c
+++ b/linuxthreads/lockfile.c
@@ -68,20 +68,39 @@ strong_alias (__ftrylockfile, _IO_ftrylockfile)
 #endif
 weak_alias (__ftrylockfile, ftrylockfile);
 
+void
+__flockfilelist(void)
+{
+#ifdef USE_IN_LIBIO
+  _IO_list_lock();
+#endif
+}
+
+void
+__funlockfilelist(void)
+{
+#ifdef USE_IN_LIBIO
+  _IO_list_unlock();
+#endif
+}
 
 void
 __fresetlockfiles (void)
 {
 #ifdef USE_IN_LIBIO
+  _IO_ITER i;
+
   _IO_FILE *fp;
   pthread_mutexattr_t attr;
 
   __pthread_mutexattr_init (&attr);
   __pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
 
-  for (fp = _IO_list_all; fp != NULL; fp = fp->_chain)
-    __pthread_mutex_init (fp->_lock, &attr);
+  for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
+    __pthread_mutex_init (_IO_iter_file(i)->_lock, &attr);
 
   __pthread_mutexattr_destroy (&attr);
+
+  _IO_list_resetlock();
 #endif
 }
diff --git a/linuxthreads/ptfork.c b/linuxthreads/ptfork.c
index 440d66c4a3..deb347fe29 100644
--- a/linuxthreads/ptfork.c
+++ b/linuxthreads/ptfork.c
@@ -83,6 +83,7 @@ pid_t __fork(void)
 
   pthread_call_handlers(pthread_atfork_prepare);
   __pthread_once_fork_prepare();
+  __flockfilelist();
 
   pid = __libc_fork();
 
@@ -95,6 +96,7 @@ pid_t __fork(void)
 
     pthread_mutex_init(&pthread_atfork_lock, NULL);
   } else {
+    __funlockfilelist();
     __pthread_once_fork_parent();
     pthread_call_handlers(pthread_atfork_parent);