diff options
Diffstat (limited to 'linuxthreads')
-rw-r--r-- | linuxthreads/ChangeLog | 23 | ||||
-rw-r--r-- | linuxthreads/internals.h | 2 | ||||
-rw-r--r-- | linuxthreads/linuxthreads.texi | 192 | ||||
-rw-r--r-- | linuxthreads/lockfile.c | 23 | ||||
-rw-r--r-- | linuxthreads/ptfork.c | 2 |
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); |