From 7d342c378c901f854ffb41084a50eb51bf750ad5 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 8 Sep 2005 03:25:37 +0000 Subject: sync with glibc --- libpthread/linuxthreads/linuxthreads.texi | 365 +++++++++++++++++++++++------- 1 file changed, 282 insertions(+), 83 deletions(-) (limited to 'libpthread/linuxthreads') diff --git a/libpthread/linuxthreads/linuxthreads.texi b/libpthread/linuxthreads/linuxthreads.texi index 7a98103b3..795fb7097 100644 --- a/libpthread/linuxthreads/linuxthreads.texi +++ b/libpthread/linuxthreads/linuxthreads.texi @@ -19,7 +19,7 @@ use @var{errno}. * Thread Attributes:: Tuning thread scheduling. * Cancellation:: Stopping a thread before it's done. * Cleanup Handlers:: Deallocating resources when a thread is - cancelled. + canceled. * Mutexes:: One way to synchronize threads. * Condition Variables:: Another way. * POSIX Semaphores:: And a third way. @@ -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 @@ -98,12 +102,12 @@ returns 0. @xref{Cancellation}, for details. @deftypefun int pthread_join (pthread_t @var{th}, void **thread_@var{return}) @code{pthread_join} suspends the execution of the calling thread until the thread identified by @var{th} terminates, either by calling -@code{pthread_exit} or by being cancelled. +@code{pthread_exit} or by being canceled. If @var{thread_return} is not @code{NULL}, the return value of @var{th} is stored in the location pointed to by @var{thread_return}. The return value of @var{th} is either the argument it gave to @code{pthread_exit}, -or @code{PTHREAD_CANCELED} if @var{th} was cancelled. +or @code{PTHREAD_CANCELED} if @var{th} was canceled. The joined thread @code{th} must be in the joinable state: it must not have been detached using @code{pthread_detach} or the @@ -177,13 +181,18 @@ left in an undefined state, and you must not use it again in a call to any pthreads function until it has been reinitialized. @end deftypefun +@findex pthread_attr_setdetachstate +@findex pthread_attr_setguardsize @findex pthread_attr_setinheritsched @findex pthread_attr_setschedparam @findex pthread_attr_setschedpolicy @findex pthread_attr_setscope +@findex pthread_attr_setstack +@findex pthread_attr_setstackaddr +@findex pthread_attr_setstacksize @comment pthread.h @comment POSIX -@deftypefun int pthread_attr_set@var{attr} (pthread_attr_t *@var{obj}, int @var{value}) +@deftypefun int pthread_attr_setattr (pthread_attr_t *@var{obj}, int @var{value}) Set attribute @var{attr} to @var{value} in the attribute object pointed to by @var{obj}. See below for a list of possible attributes and the values they can take. @@ -194,13 +203,18 @@ for the @var{attr} being modified, they will return the error code below. @end deftypefun +@findex pthread_attr_getdetachstate +@findex pthread_attr_getguardsize @findex pthread_attr_getinheritsched @findex pthread_attr_getschedparam @findex pthread_attr_getschedpolicy @findex pthread_attr_getscope +@findex pthread_attr_getstack +@findex pthread_attr_getstackaddr +@findex pthread_attr_getstacksize @comment pthread.h @comment POSIX -@deftypefun int pthread_attr_get@var{attr} (const pthread_attr_t *@var{obj}, int *@var{value}) +@deftypefun int pthread_attr_getattr (const pthread_attr_t *@var{obj}, int *@var{value}) Store the current setting of @var{attr} in @var{obj} into the variable pointed to by @var{value}. @@ -275,8 +289,45 @@ interpreted relative to the priorities of the other threads of the process, regardless of the priorities of other processes. @code{PTHREAD_SCOPE_PROCESS} is not supported in LinuxThreads. If you -try to set the scope to this value @code{pthread_attr_setscope} will +try to set the scope to this value, @code{pthread_attr_setscope} will fail and return @code{ENOTSUP}. + +@item stackaddr +Provide an address for an application managed stack. The size of the +stack must be at least @code{PTHREAD_STACK_MIN}. + +@item stacksize +Change the size of the stack created for the thread. The value defines +the minimum stack size, in bytes. + +If the value exceeds the system's maximum stack size, or is smaller +than @code{PTHREAD_STACK_MIN}, @code{pthread_attr_setstacksize} will +fail and return @code{EINVAL}. + +@item stack +Provide both the address and size of an application managed stack to +use for the new thread. The base of the memory area is @var{stackaddr} +with the size of the memory area, @var{stacksize}, measured in bytes. + +If the value of @var{stacksize} is less than @code{PTHREAD_STACK_MIN}, +or greater than the system's maximum stack size, or if the value of +@var{stackaddr} lacks the proper alignment, @code{pthread_attr_setstack} +will fail and return @code{EINVAL}. + +@item guardsize +Change the minimum size in bytes of the guard area for the thread's +stack. The default size is a single page. If this value is set, it +will be rounded up to the nearest page size. If the value is set to 0, +a guard area will not be created for this thread. The space allocated +for the guard area is used to catch stack overflow. Therefore, when +allocating large structures on the stack, a larger guard area may be +required to catch a stack overflow. + +If the caller is managing their own stacks (if the @code{stackaddr} +attribute has been set), then the @code{guardsize} attribute is ignored. + +If the value exceeds the @code{stacksize}, @code{pthread_atrr_setguardsize} +will fail and return @code{EINVAL}. @end table @node Cancellation @@ -294,7 +345,7 @@ When a thread eventually honors a cancellation request, it behaves as if @code{pthread_exit(PTHREAD_CANCELED)} was called. All cleanup handlers are executed in reverse order, finalization functions for thread-specific data are called, and finally the thread stops executing. -If the cancelled thread was joinable, the return value +If the canceled thread was joinable, the return value @code{PTHREAD_CANCELED} is provided to whichever thread calls @var{pthread_join} on it. See @code{pthread_exit} for more information. @@ -377,7 +428,7 @@ stack-like discipline. The purpose of cleanup handlers is to free the resources that a thread may hold at the time it terminates. In particular, if a thread exits or -is cancelled while it owns a locked mutex, the mutex will remain locked +is canceled while it owns a locked mutex, the mutex will remain locked forever and prevent other threads from executing normally. The best way to avoid this is, just before locking the mutex, to install a cleanup handler whose effect is to unlock the mutex. Cleanup handlers can be @@ -493,7 +544,7 @@ The sequence @smallexample pthread_cleanup_push_defer_np(routine, arg); ... -pthread_cleanup_pop_defer_np(execute); +pthread_cleanup_pop_restore_np(execute); @end smallexample @noindent @@ -546,15 +597,16 @@ calling thread. If @var{mutexattr} is @code{NULL}, default attributes are used instead. The LinuxThreads implementation supports only one mutex attribute, -the @var{mutex kind}, which is either ``fast'', ``recursive'', or -``error checking''. The kind of a mutex determines whether +the @var{mutex type}, which is either ``fast'', ``recursive'', or +``error checking''. The type of a mutex determines whether it can be locked again by a thread that already owns it. -The default kind is ``fast''. +The default type is ``fast''. Variables of type @code{pthread_mutex_t} can also be initialized statically, using the constants @code{PTHREAD_MUTEX_INITIALIZER} (for -fast mutexes), @code{PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP} (for -recursive mutexes), and @code{PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP} +timed mutexes), @code{PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP} (for +recursive mutexes), @code{PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP} +(for fast mutexes(, and @code{PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP} (for error checking mutexes). @code{pthread_mutex_init} always returns 0. @@ -570,16 +622,17 @@ already locked by another thread, @code{pthread_mutex_lock} suspends the calling thread until the mutex is unlocked. If the mutex is already locked by the calling thread, the behavior of -@code{pthread_mutex_lock} depends on the kind of the mutex. If the mutex -is of the ``fast'' kind, the calling thread is suspended. It will +@code{pthread_mutex_lock} depends on the type of the mutex. If the mutex +is of the ``fast'' type, the calling thread is suspended. It will remain suspended forever, because no other thread can unlock the mutex. -If the mutex is of the ``error checking'' kind, @code{pthread_mutex_lock} +If the mutex is of the ``error checking'' type, @code{pthread_mutex_lock} returns immediately with the error code @code{EDEADLK}. If the mutex is -of the ``recursive'' kind, @code{pthread_mutex_lock} succeeds and +of the ``recursive'' type, @code{pthread_mutex_lock} succeeds and returns immediately, recording the number of times the calling thread has locked the mutex. An equal number of @code{pthread_mutex_unlock} operations must be performed before the mutex returns to the unlocked state. +@c This doesn't discuss PTHREAD_MUTEX_TIMED_NP mutex attributes. FIXME @end deftypefun @comment pthread.h @@ -593,14 +646,33 @@ calling thread in the case of a ``fast'' mutex). Instead, @code{EBUSY}. @end deftypefun +@comment pthread.h +@comment POSIX +@deftypefun int pthread_mutex_timedlock (pthread_mutex_t *@var{mutex}, const struct timespec *@var{abstime}) +The @code{pthread_mutex_timedlock} is similar to the +@code{pthread_mutex_lock} function but instead of blocking for in +indefinite time if the mutex is locked by another thread, it returns +when the time specified in @var{abstime} is reached. + +This function can only be used on standard (``timed'') and ``error +checking'' mutexes. It behaves just like @code{pthread_mutex_lock} for +all other types. + +If the mutex is successfully locked, the function returns zero. If the +time specified in @var{abstime} is reached without the mutex being locked, +@code{ETIMEDOUT} is returned. + +This function was introduced in the POSIX.1d revision of the POSIX standard. +@end deftypefun + @comment pthread.h @comment POSIX @deftypefun int pthread_mutex_unlock (pthread_mutex_t *@var{mutex}) @code{pthread_mutex_unlock} unlocks the given mutex. The mutex is assumed to be locked and owned by the calling thread on entrance to -@code{pthread_mutex_unlock}. If the mutex is of the ``fast'' kind, +@code{pthread_mutex_unlock}. If the mutex is of the ``fast'' type, @code{pthread_mutex_unlock} always returns it to the unlocked state. If -it is of the ``recursive'' kind, it decrements the locking count of the +it is of the ``recursive'' type, it decrements the locking count of the mutex (number of @code{pthread_mutex_lock} operations performed on it by the calling thread), and only when this count reaches zero is the mutex actually unlocked. @@ -673,45 +745,52 @@ LinuxThreads implementation. This function always returns 0. @end deftypefun -LinuxThreads supports only one mutex attribute: the mutex kind, which is -either @code{PTHREAD_MUTEX_FAST_NP} for ``fast'' mutexes, -@code{PTHREAD_MUTEX_RECURSIVE_NP} for ``recursive'' mutexes, or +LinuxThreads supports only one mutex attribute: the mutex type, which is +either @code{PTHREAD_MUTEX_ADAPTIVE_NP} for ``fast'' mutexes, +@code{PTHREAD_MUTEX_RECURSIVE_NP} for ``recursive'' mutexes, +@code{PTHREAD_MUTEX_TIMED_NP} for ``timed'' mutexes, or @code{PTHREAD_MUTEX_ERRORCHECK_NP} for ``error checking'' mutexes. As the @code{NP} suffix indicates, this is a non-portable extension to the POSIX standard and should not be employed in portable programs. -The mutex kind determines what happens if a thread attempts to lock a +The mutex type determines what happens if a thread attempts to lock a mutex it already owns with @code{pthread_mutex_lock}. If the mutex is of -the ``fast'' kind, @code{pthread_mutex_lock} simply suspends the calling -thread forever. If the mutex is of the ``error checking'' kind, +the ``fast'' type, @code{pthread_mutex_lock} simply suspends the calling +thread forever. If the mutex is of the ``error checking'' type, @code{pthread_mutex_lock} returns immediately with the error code -@code{EDEADLK}. If the mutex is of the ``recursive'' kind, the call to +@code{EDEADLK}. If the mutex is of the ``recursive'' type, the call to @code{pthread_mutex_lock} returns immediately with a success return code. The number of times the thread owning the mutex has locked it is recorded in the mutex. The owning thread must call @code{pthread_mutex_unlock} the same number of times before the mutex returns to the unlocked state. -The default mutex kind is ``fast'', that is, @code{PTHREAD_MUTEX_FAST_NP}. +The default mutex type is ``timed'', that is, @code{PTHREAD_MUTEX_TIMED_NP}. +@c This doesn't describe how a ``timed'' mutex behaves. FIXME @comment pthread.h -@comment GNU -@deftypefun int pthread_mutexattr_setkind_np (pthread_mutexattr_t *@var{attr}, int @var{kind}) -@code{pthread_mutexattr_setkind_np} sets the mutex kind attribute in -@var{attr} to the value specified by @var{kind}. +@comment POSIX +@deftypefun int pthread_mutexattr_settype (pthread_mutexattr_t *@var{attr}, int @var{type}) +@code{pthread_mutexattr_settype} sets the mutex type attribute in +@var{attr} to the value specified by @var{type}. -If @var{kind} is not @code{PTHREAD_MUTEX_FAST_NP}, -@code{PTHREAD_MUTEX_RECURSIVE_NP}, or +If @var{type} is not @code{PTHREAD_MUTEX_ADAPTIVE_NP}, +@code{PTHREAD_MUTEX_RECURSIVE_NP}, @code{PTHREAD_MUTEX_TIMED_NP}, or @code{PTHREAD_MUTEX_ERRORCHECK_NP}, this function will return @code{EINVAL} and leave @var{attr} unchanged. + +The standard Unix98 identifiers @code{PTHREAD_MUTEX_DEFAULT}, +@code{PTHREAD_MUTEX_NORMAL}, @code{PTHREAD_MUTEX_RECURSIVE}, +and @code{PTHREAD_MUTEX_ERRORCHECK} are also permitted. + @end deftypefun @comment pthread.h -@comment GNU -@deftypefun int pthread_mutexattr_getkind_np (const pthread_mutexattr_t *@var{attr}, int *@var{kind}) -@code{pthread_mutexattr_getkind_np} retrieves the current value of the -mutex kind attribute in @var{attr} and stores it in the location pointed -to by @var{kind}. +@comment POSIX +@deftypefun int pthread_mutexattr_gettype (const pthread_mutexattr_t *@var{attr}, int *@var{type}) +@code{pthread_mutexattr_gettype} retrieves the current value of the +mutex type attribute in @var{attr} and stores it in the location pointed +to by @var{type}. This function always returns 0. @end deftypefun @@ -820,7 +899,7 @@ nothing. @end deftypefun @code{pthread_cond_wait} and @code{pthread_cond_timedwait} are -cancellation points. If a thread is cancelled while suspended in one of +cancellation points. If a thread is canceled while suspended in one of these functions, the thread immediately resumes execution, relocks the mutex specified by @var{mutex}, and finally executes the cancellation. Consequently, cleanup handlers are assured that @var{mutex} is locked @@ -1237,6 +1316,141 @@ threads must not attach their own signal handlers to these signals, or alternatively they should all block these signals (which is recommended anyway). +@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 +@deftypefun int pthread_atfork (void (*@var{prepare})(void), void (*@var{parent})(void), void (*@var{child})(void)) + +@code{pthread_atfork} registers handler functions to be called just +before and just after a new process is created with @code{fork}. The +@var{prepare} handler will be called from the parent process, just +before the new process is created. The @var{parent} handler will be +called from the parent process, just before @code{fork} returns. The +@var{child} handler will be called from the child process, just before +@code{fork} returns. + +@code{pthread_atfork} returns 0 on success and a non-zero error code on +error. + +One or more of the three handlers @var{prepare}, @var{parent} and +@var{child} can be given as @code{NULL}, meaning that no handler needs +to be called at the corresponding point. + +@code{pthread_atfork} can be called several times to install several +sets of handlers. At @code{fork} time, the @var{prepare} handlers are +called in LIFO order (last added with @code{pthread_atfork}, first +called before @code{fork}), while the @var{parent} and @var{child} +handlers are called in FIFO order (first added, first called). + +If there is insufficient memory available to register the handlers, +@code{pthread_atfork} fails and returns @code{ENOMEM}. Otherwise it +returns 0. + +The functions @code{fork} and @code{pthread_atfork} must not be regarded as +reentrant from the context of the handlers. That is to say, if a +@code{pthread_atfork} handler invoked from within @code{fork} calls +@code{pthread_atfork} or @code{fork}, the behavior is undefined. + +Registering a triplet of handlers is an atomic operation with respect to fork. +If new handlers are registered at about the same time as a fork occurs, either +all three handlers will be called, or none of them will be called. + +The handlers are inherited by the child process, and there is no +way to remove them, short of using @code{exec} to load a new +pocess image. + +@end deftypefun + +To understand the purpose of @code{pthread_atfork}, recall that +@code{fork} duplicates the whole memory space, including mutexes in +their current locking state, but only the calling thread: other threads +are not running in the child process. The mutexes are not usable after +the @code{fork} and must be initialized with @code{pthread_mutex_init} +in the child process. This is a limitation of the current +implementation and might or might not be present in future versions. + +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 @@ -1286,49 +1500,6 @@ The thread @var{th} is already in the detached state @end table @end deftypefun -@comment pthread.h -@comment POSIX -@deftypefun int pthread_atfork (void (*@var{prepare})(void), void (*@var{parent})(void), void (*@var{child})(void)) - -@code{pthread_atfork} registers handler functions to be called just -before and just after a new process is created with @code{fork}. The -@var{prepare} handler will be called from the parent process, just -before the new process is created. The @var{parent} handler will be -called from the parent process, just before @code{fork} returns. The -@var{child} handler will be called from the child process, just before -@code{fork} returns. - -@code{pthread_atfork} returns 0 on success and a non-zero error code on -error. - -One or more of the three handlers @var{prepare}, @var{parent} and -@var{child} can be given as @code{NULL}, meaning that no handler needs -to be called at the corresponding point. - -@code{pthread_atfork} can be called several times to install several -sets of handlers. At @code{fork} time, the @var{prepare} handlers are -called in LIFO order (last added with @code{pthread_atfork}, first -called before @code{fork}), while the @var{parent} and @var{child} -handlers are called in FIFO order (first added, first called). - -If there is insufficient memory available to register the handlers, -@code{pthread_atfork} fails and returns @code{ENOMEM}. Otherwise it -returns 0. -@end deftypefun - -To understand the purpose of @code{pthread_atfork}, recall that -@code{fork} duplicates the whole memory space, including mutexes in -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. - @comment pthread.h @comment GNU @deftypefun void pthread_kill_other_threads_np (@var{void}) @@ -1368,6 +1539,15 @@ record that initialization has been performed. Subsequent calls to @code{pthread_once} with the same @code{once_control} argument do nothing. +If a thread is cancelled while executing @var{init_routine} +the state of the @var{once_control} variable is reset so that +a future call to @code{pthread_once} will call the routine again. + +If the process forks while one or more threads are executing +@code{pthread_once} initialization routines, the states of their respective +@var{once_control} variables will appear to be reset in the child process so +that if the child calls @code{pthread_once}, the routines will be executed. + @code{pthread_once} always returns 0. @end deftypefun @@ -1426,3 +1606,22 @@ The @var{target_thread} is invalid or has already terminated. @end table @end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_setconcurrency (int @var{level}) +@code{pthread_setconcurrency} is unused in LinuxThreads due to the lack +of a mapping of user threads to kernel threads. It exists for source +compatibility. It does store the value @var{level} so that it can be +returned by a subsequent call to @code{pthread_getconcurrency}. It takes +no other action however. +@end deftypefun + +@comment pthread.h +@comment POSIX +@deftypefun int pthread_getconcurrency () +@code{pthread_getconcurrency} is unused in LinuxThreads due to the lack +of a mapping of user threads to kernel threads. It exists for source +compatibility. However, it will return the value that was set by the +last call to @code{pthread_setconcurrency}. +@end deftypefun -- cgit v1.2.3