mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
LinuxThreads library.
1998-03-11 00:42 Wolfram Gloger <wmglo@dent.med.uni-muenchen.de> * linuxthreads/manager.c: Enable resetting of the thread scheduling policy to SCHED_OTHER when the parent thread has a different one. 1998-02-01 13:51 Ulrich Drepper <drepper@cygnus.com> * sysdeps/unix/sysv/linux/bits/posix_opt.h: Define _POSIX_ASYNCHRONOUS_IO. * sysdeps/pthread/pthread.h: Define bits for Unix98 variants of mutexes. * mutex.c: Implement new mutex types. * internals.h: Include <signal.h>. * libpthread.map: Add __erno_location and __h_errno_location. * errno.c: Return pointer to variable actually in use. This might not be the one in the thread structure. * internals.h (struct _pthread_descr_struct): Add new fields p_errnop and p_h_errnop. * manager.c (__pthread_manager): Set p_errnop and p_h_errnop member of manager thread structure. (pthread_handle_create): Set p_errnop and p_h_errnop members for new thread. * pthread.c: Adapt initializer for thread structures. (__pthread_initial_thread): Set p_errnop and p_h_errnop member. (__pthread_reset_main_thread): Reset p_errnop and p_h_errnop of current thread to global variables. 1998-01-31 17:27 Ulrich Drepper <drepper@cygnus.com> * rwlock.c: New file. * Makefile (libpthread-routines): Add rwlock. * sysdeps/pthread/pthread.h: Define data structures and declare functions. * libpthread.map: Add new functions. 1997-12-18 13:50 Philip Blundell <pb@nexus.co.uk> * sysdeps/arm/pt-machine.h: New file; add ARM support. * sysdeps/arm/Implies: likewise. * README: Document it. 1997-12-13 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> * signals.c: Remove unneeded initializer for sigwaited, saving a 1997-04-11 01:18 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> * semaphore.c (sem_init): Set sem_spinlock only if available. 1997-12-04 01:48 Ulrich Drepper <drepper@cygnus.com> * mutex.c: Implement PTHREAD_MUTEX_CHECKERROR. * sysdeps/pthread/pthread.h: Define PTHREAD_MUTEX_CHECKERROR. * Makefile: Update from LinuxThreads 0.7. * internals.h. Likewise. * manager.c: Likewise. * mutex.c: Likewise. * pthread.c: Likewise. * signals.c: Likewise. * specific.c: Likewise. * Examples/ex3.c: Likewise. 1997-11-20 18:13 Ulrich Drepper <drepper@cygnus.com> * pthread.c (__pthread_reset_main_thread): Close pipe only if still open. 1997-10-29 05:38 Ulrich Drepper <drepper@cygnus.com> * wrapsyscall.c: Add socket functions which are also cancelation points. 1997-10-19 21:40 Wolfram Gloger <wg@wolfram.dent.med.uni-muenchen.de> * specific.c (__libc_internal_tsd_set, __libc_internal_tsd_get): New functions for fast thread specific data within libc. * internals.h: Add new array p_libc_specific to struct _pthread_descr_struct. * sysdeps/pthread/bits/libc-lock.h: Declare new functions. 1997-10-13 05:39 Ulrich Drepper <drepper@cygnus.com> * semaphore.h: Add __BEGIN_DECLS/__END_DECLS. Reported by Ralf Corsepius <corsepiu@faw.uni-ulm.de>. 1997-08-29 03:05 Ulrich Drepper <drepper@cygnus.com> * internals.h (struct _pthread_descr_struct): Add definitions for two-level specific key handling. * manager.c (pthread_handle_create): Initialize specific memory array. * specific.c: Implement two-level key handling. * weaks.c: Don't provide dummy key handling. * sysdeps/pthread/bits/libc-lock.h: Typedef __libc_lock_t (no #define). Add definition of __libc_key_t. * sysdeps/unix/sysv/linux/bits/local_lim.h: Define PTHREAD_KEYS_MAX as 1024. Add definition of _POSIX_THREAD_DESTRUCTOR_ITERATIONS and PTHREAD_DESTRUCTOR_ITERATIONS. * manager.c (pthread_handle_create): Compare mmap result with MAP_FAILED. * ptfork.c: Rename to __pthread_atfork and make old name a weak alias. * sysdeps/pthread/bits/pthread.h: Add prototype for __pthread_atfork. 1997-08-22 19:04 Richard Henderson <rth@cygnus.com> sysdeps/sparc -> sysdeps/sparc/sparc32 sysdeps/sparc64 -> sysdeps/sparc/sparc64 * internals.h: Change definition of THREAD_SELF to be an expression, not a statement that did a return. * sysdeps/alpha/pt-machine.h (THREAD_SELF): Update accordingly. * sysdeps/sparc/sparc32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF): Follow Solaris and use a "system reserved" register (%g6) to hold the thread descriptor. * sysdeps/sparc/sparc64/pt-machine.h: Likewise. 1997-08-03 00:09 Ulrich Drepper <drepper@cygnus.com> * mutex.c: Correct pthread_once. Patch by Xavier Leroy. * sysdeps/pthread/pthread.h: Add prototype for __pthread_once. * sysdeps/pthread/bits/pthread.h: Add macros for __libc_once. * semaphore.c: Include spinlock.h only when needed. * specific.c (__pthread_setsepcific, __pthread_getspecific): Reject keys for entries not in use. * weaks.c: Implement key handling functions for real. 1997-06-29 01:04 Richard Henderson <richard@gnu.ai.mit.edu> Initial sparc64-linux support: * linuxthreads/sysdeps/sparc64/Implies: New file. * linuxthreads/sysdeps/sparc64/pt-machine.h: Likewise. 1997-06-29 00:48 Ulrich Drepper <drepper@cygnus.com> * semaphore.c: Include spinlock.h at correct place. Patch by HJ Lu. 1997-06-13 10:06 Richard Henderson <rth@tamu.edu> The Great Bit File Move: * sysdeps/alpha/semaphorebits.h: -> .../bits/semaphore.h. * sysdeps/powerpc/semaphorebits.h: Likewise. * sysdeps/pthread/cmpxchg/semaphorebits.h: Likewise. * sysdeps/pthread/no-cmpxchg/semaphorebits.h: Likewise. * sysdeps/pthread/libc-lock.h: -> bits/ * sysdeps/pthread/stdio-lock.h: Likewise. * sysdeps/unix/sysv/linux/local_lim.h: Likewise. * sysdeps/unix/sysv/linux/posix_opt.h: Likewise. * semaphore.h: Likewise. * sysdeps/pthread/pthread.h: Likewise. * lockfile.c: <foo.h> -> <bits/foo.h>. * semaphore.h: Likewise. * Makefile: (headers): foo.h -> bits/foo.h. * sysdeps/pthread/Makefile: Likewise. 1997-04-11 01:18 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> * semaphore.c (sem_init): Set sem_spinlock only if available. * sysdeps/m68k/pt-machine.h (testandset, __compare_and_swap): Fix asm constraints. 1997-04-09 03:00 Ulrich Drepper <drepper@cygnus.com> Update from LinuxThreads 0.6. * attr.c (pthread_attr_getdetachstate): Use __sched_get_priority_max and __sched_get_priority_min instead of names without `__'. * manager.c: Rewrite large parts to implement opaque pthread_t. * cancel.c: Adapt for opaque pthread_t type. * condvar.c: Likewise. * errno.c: Likewise. * join.c: Likewise. * mutex.c: Likewise. * pthread.c: Likewise. * signals.c: Likewise. * specific.c: Likewise. * restart.h: Likewise. * queue.h: Likewise. * Examples/ex3.c: Likewise. * Examples/ex4.c: Likewise. * sysdeps/pthread/pthread.h: Likewise. * pthread.c: Accumulate time for all threads in thread manager. * semaphore.c: Implement fallback implementation for architectures sometimes missing compare-exchange operations. * cancel.c (pthread_cancel): Validate handle argument. * join.c (pthread_join): Likewise. (pthread_detach): Likewise. * signals.c (pthread_kill): Likewise. * spinlock.h (acquire): Use __sched_yield not sched_yield. * queue.h (enqueue): Enqueue thread according to priority. * internals.c (struct pthread_start_args): New struct for passing args to cloning function. (struct _pthread): Rename to _pthread_descr_struct and adapt for opaque pthread_t. * Examples/Makefile (clean): Pass -f option to rm. * sysdeps/i386/pt-machine.h: Add check for compare-exchange instruction and define TEST_FOR_COMPARE_AND_SWAP. * sysdeps/i386/i486/pt-machine.h: Removed. * sysdeps/unix/sysv/linux/local_lim.h (PTHREAD_THREADS_MAX): Increase to 1024. 1997-04-04 16:38 Ulrich Drepper <drepper@cygnus.com> * restart.h (suspend): Clear p_signal before suspending. (suspend_with_cancellation): Likewise. Patch by Xavier Leroy <Xavier.Leroy@inria.fr>. * weaks.c: Make __pthread_key_create return 1. * sysdeps/pthread/libc-lock.h: Define __libc_key_create, __libc_getspecific, __libc_setspecific, and __libc_key_t. * sysdeps/pthread/stdio-lock.h: Don't care for implementation not using libio. 1997-03-19 15:13 Miguel de Icaza <miguel@nuclecu.unam.mx> * sysdeps/sparc/pt-machine (RELEASE): Fix. 1997-03-01 07:55 Geoff Keating <geoffk@ozemail.com.au> * sysdeps/powerpc/Implies: Added. * sysdeps/powerpc/pt-machine.h: Added. * sysdeps/powerpc/semaphorebits.h: Added. 1997-01-22 01:22 Ulrich Drepper <drepper@cygnus.com> * linuxtheads/pthread.c (__pthread_initial_thread): Correct initializer. (__pthread_manager_thread): Likewise. Reported by Andreas Jaeger. 1997-01-18 22:15 Richard Henderson <rth@tamu.edu> Since sigset_t no longer fits in a register, we can't pass in the thread's initial mask so easily. Take this opportunity to simplify the clone implementation by only accepting a single void* argument. * linuxthreads/manager.c (__pthread_manager): Put thread vitals in the thread struct instead of as arguments through clone. (pthread_start_thread): Look for them there. * linuxthreads/internals.h (struct _pthread): Add p_initial_fn, p_initial_fn_arg, p_initial_mask. Fix __pthread_manager proto. * linuxthreads/pthread.c (pthread_initialize_manager): Revise clone invocation.
This commit is contained in:
1
linuxthreads/Banner
Normal file
1
linuxthreads/Banner
Normal file
@ -0,0 +1 @@
|
|||||||
|
linuxthreads-0.7 by Xavier Leroy
|
271
linuxthreads/ChangeLog
Normal file
271
linuxthreads/ChangeLog
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
1998-03-11 00:42 Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>
|
||||||
|
|
||||||
|
* linuxthreads/manager.c: Enable resetting of the thread
|
||||||
|
scheduling policy to SCHED_OTHER when the parent thread
|
||||||
|
has a different one.
|
||||||
|
|
||||||
|
1998-02-01 13:51 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* sysdeps/unix/sysv/linux/bits/posix_opt.h: Define
|
||||||
|
_POSIX_ASYNCHRONOUS_IO.
|
||||||
|
|
||||||
|
* sysdeps/pthread/pthread.h: Define bits for Unix98 variants of
|
||||||
|
mutexes.
|
||||||
|
* mutex.c: Implement new mutex types.
|
||||||
|
|
||||||
|
* internals.h: Include <signal.h>.
|
||||||
|
|
||||||
|
* libpthread.map: Add __erno_location and __h_errno_location.
|
||||||
|
|
||||||
|
* errno.c: Return pointer to variable actually in use. This might
|
||||||
|
not be the one in the thread structure.
|
||||||
|
* internals.h (struct _pthread_descr_struct): Add new fields p_errnop
|
||||||
|
and p_h_errnop.
|
||||||
|
* manager.c (__pthread_manager): Set p_errnop and p_h_errnop member
|
||||||
|
of manager thread structure.
|
||||||
|
(pthread_handle_create): Set p_errnop and p_h_errnop members for new
|
||||||
|
thread.
|
||||||
|
* pthread.c: Adapt initializer for thread structures.
|
||||||
|
(__pthread_initial_thread): Set p_errnop and p_h_errnop member.
|
||||||
|
(__pthread_reset_main_thread): Reset p_errnop and p_h_errnop of
|
||||||
|
current thread to global variables.
|
||||||
|
|
||||||
|
1998-01-31 17:27 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* rwlock.c: New file.
|
||||||
|
* Makefile (libpthread-routines): Add rwlock.
|
||||||
|
* sysdeps/pthread/pthread.h: Define data structures and declare
|
||||||
|
functions.
|
||||||
|
* libpthread.map: Add new functions.
|
||||||
|
|
||||||
|
1997-12-18 13:50 Philip Blundell <pb@nexus.co.uk>
|
||||||
|
|
||||||
|
* sysdeps/arm/pt-machine.h: New file; add ARM support.
|
||||||
|
* sysdeps/arm/Implies: likewise.
|
||||||
|
* README: Document it.
|
||||||
|
|
||||||
|
1997-12-13 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
|
||||||
|
|
||||||
|
* signals.c: Remove unneeded initializer for sigwaited, saving a
|
||||||
|
warning.
|
||||||
|
|
||||||
|
1997-04-11 01:18 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
|
||||||
|
|
||||||
|
* semaphore.c (sem_init): Set sem_spinlock only if available.
|
||||||
|
|
||||||
|
1997-12-04 01:48 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* mutex.c: Implement PTHREAD_MUTEX_CHECKERROR.
|
||||||
|
* sysdeps/pthread/pthread.h: Define PTHREAD_MUTEX_CHECKERROR.
|
||||||
|
|
||||||
|
* Makefile: Update from LinuxThreads 0.7.
|
||||||
|
* internals.h. Likewise.
|
||||||
|
* manager.c: Likewise.
|
||||||
|
* mutex.c: Likewise.
|
||||||
|
* pthread.c: Likewise.
|
||||||
|
* signals.c: Likewise.
|
||||||
|
* specific.c: Likewise.
|
||||||
|
* Examples/ex3.c: Likewise.
|
||||||
|
|
||||||
|
1997-11-20 18:13 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* pthread.c (__pthread_reset_main_thread): Close pipe only if still
|
||||||
|
open.
|
||||||
|
|
||||||
|
1997-10-29 05:38 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* wrapsyscall.c: Add socket functions which are also cancelation
|
||||||
|
points.
|
||||||
|
|
||||||
|
1997-10-19 21:40 Wolfram Gloger <wg@wolfram.dent.med.uni-muenchen.de>
|
||||||
|
|
||||||
|
* specific.c (__libc_internal_tsd_set, __libc_internal_tsd_get):
|
||||||
|
New functions for fast thread specific data within libc.
|
||||||
|
|
||||||
|
* internals.h: Add new array p_libc_specific to struct
|
||||||
|
_pthread_descr_struct.
|
||||||
|
|
||||||
|
* sysdeps/pthread/bits/libc-lock.h: Declare new functions.
|
||||||
|
|
||||||
|
1997-10-13 05:39 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* semaphore.h: Add __BEGIN_DECLS/__END_DECLS.
|
||||||
|
Reported by Ralf Corsepius <corsepiu@faw.uni-ulm.de>.
|
||||||
|
|
||||||
|
1997-08-29 03:05 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* internals.h (struct _pthread_descr_struct): Add definitions for
|
||||||
|
two-level specific key handling.
|
||||||
|
* manager.c (pthread_handle_create): Initialize specific memory array.
|
||||||
|
* specific.c: Implement two-level key handling.
|
||||||
|
* weaks.c: Don't provide dummy key handling.
|
||||||
|
* sysdeps/pthread/bits/libc-lock.h: Typedef __libc_lock_t (no #define).
|
||||||
|
Add definition of __libc_key_t.
|
||||||
|
* sysdeps/unix/sysv/linux/bits/local_lim.h: Define PTHREAD_KEYS_MAX
|
||||||
|
as 1024.
|
||||||
|
Add definition of _POSIX_THREAD_DESTRUCTOR_ITERATIONS and
|
||||||
|
PTHREAD_DESTRUCTOR_ITERATIONS.
|
||||||
|
|
||||||
|
* manager.c (pthread_handle_create): Compare mmap result with
|
||||||
|
MAP_FAILED.
|
||||||
|
|
||||||
|
* ptfork.c: Rename to __pthread_atfork and make old name a weak alias.
|
||||||
|
* sysdeps/pthread/bits/pthread.h: Add prototype for __pthread_atfork.
|
||||||
|
|
||||||
|
1997-08-22 19:04 Richard Henderson <rth@cygnus.com>
|
||||||
|
|
||||||
|
sysdeps/sparc -> sysdeps/sparc/sparc32
|
||||||
|
sysdeps/sparc64 -> sysdeps/sparc/sparc64
|
||||||
|
|
||||||
|
* internals.h: Change definition of THREAD_SELF to be an expression,
|
||||||
|
not a statement that did a return.
|
||||||
|
* sysdeps/alpha/pt-machine.h (THREAD_SELF): Update accordingly.
|
||||||
|
* sysdeps/sparc/sparc32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF):
|
||||||
|
Follow Solaris and use a "system reserved" register (%g6) to hold
|
||||||
|
the thread descriptor.
|
||||||
|
* sysdeps/sparc/sparc64/pt-machine.h: Likewise.
|
||||||
|
|
||||||
|
1997-08-03 00:09 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* mutex.c: Correct pthread_once. Patch by Xavier Leroy.
|
||||||
|
* sysdeps/pthread/pthread.h: Add prototype for __pthread_once.
|
||||||
|
* sysdeps/pthread/bits/pthread.h: Add macros for __libc_once.
|
||||||
|
|
||||||
|
* semaphore.c: Include spinlock.h only when needed.
|
||||||
|
|
||||||
|
* specific.c (__pthread_setsepcific, __pthread_getspecific): Reject
|
||||||
|
keys for entries not in use.
|
||||||
|
|
||||||
|
* weaks.c: Implement key handling functions for real.
|
||||||
|
|
||||||
|
1997-06-29 01:04 Richard Henderson <richard@gnu.ai.mit.edu>
|
||||||
|
|
||||||
|
Initial sparc64-linux support:
|
||||||
|
* linuxthreads/sysdeps/sparc64/Implies: New file.
|
||||||
|
* linuxthreads/sysdeps/sparc64/pt-machine.h: Likewise.
|
||||||
|
|
||||||
|
1997-06-29 00:48 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* semaphore.c: Include spinlock.h at correct place.
|
||||||
|
Patch by HJ Lu.
|
||||||
|
|
||||||
|
1997-06-13 10:06 Richard Henderson <rth@tamu.edu>
|
||||||
|
|
||||||
|
The Great Bit File Move:
|
||||||
|
* sysdeps/alpha/semaphorebits.h: -> .../bits/semaphore.h.
|
||||||
|
* sysdeps/powerpc/semaphorebits.h: Likewise.
|
||||||
|
* sysdeps/pthread/cmpxchg/semaphorebits.h: Likewise.
|
||||||
|
* sysdeps/pthread/no-cmpxchg/semaphorebits.h: Likewise.
|
||||||
|
* sysdeps/pthread/libc-lock.h: -> bits/
|
||||||
|
* sysdeps/pthread/stdio-lock.h: Likewise.
|
||||||
|
* sysdeps/unix/sysv/linux/local_lim.h: Likewise.
|
||||||
|
* sysdeps/unix/sysv/linux/posix_opt.h: Likewise.
|
||||||
|
* semaphore.h: Likewise.
|
||||||
|
* sysdeps/pthread/pthread.h: Likewise.
|
||||||
|
|
||||||
|
* lockfile.c: <foo.h> -> <bits/foo.h>.
|
||||||
|
* semaphore.h: Likewise.
|
||||||
|
|
||||||
|
* Makefile: (headers): foo.h -> bits/foo.h.
|
||||||
|
* sysdeps/pthread/Makefile: Likewise.
|
||||||
|
|
||||||
|
1997-04-11 01:18 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
|
||||||
|
|
||||||
|
* semaphore.c (sem_init): Set sem_spinlock only if available.
|
||||||
|
|
||||||
|
* sysdeps/m68k/pt-machine.h (testandset, __compare_and_swap): Fix
|
||||||
|
asm constraints.
|
||||||
|
|
||||||
|
1997-04-09 03:00 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
Update from LinuxThreads 0.6.
|
||||||
|
|
||||||
|
* attr.c (pthread_attr_getdetachstate): Use __sched_get_priority_max
|
||||||
|
and __sched_get_priority_min instead of names without `__'.
|
||||||
|
|
||||||
|
* manager.c: Rewrite large parts to implement opaque pthread_t.
|
||||||
|
|
||||||
|
* cancel.c: Adapt for opaque pthread_t type.
|
||||||
|
* condvar.c: Likewise.
|
||||||
|
* errno.c: Likewise.
|
||||||
|
* join.c: Likewise.
|
||||||
|
* mutex.c: Likewise.
|
||||||
|
* pthread.c: Likewise.
|
||||||
|
* signals.c: Likewise.
|
||||||
|
* specific.c: Likewise.
|
||||||
|
* restart.h: Likewise.
|
||||||
|
* queue.h: Likewise.
|
||||||
|
* Examples/ex3.c: Likewise.
|
||||||
|
* Examples/ex4.c: Likewise.
|
||||||
|
* sysdeps/pthread/pthread.h: Likewise.
|
||||||
|
|
||||||
|
* pthread.c: Accumulate time for all threads in thread manager.
|
||||||
|
|
||||||
|
* semaphore.c: Implement fallback implementation for architectures
|
||||||
|
sometimes missing compare-exchange operations.
|
||||||
|
|
||||||
|
* cancel.c (pthread_cancel): Validate handle argument.
|
||||||
|
* join.c (pthread_join): Likewise.
|
||||||
|
(pthread_detach): Likewise.
|
||||||
|
* signals.c (pthread_kill): Likewise.
|
||||||
|
|
||||||
|
* spinlock.h (acquire): Use __sched_yield not sched_yield.
|
||||||
|
|
||||||
|
* queue.h (enqueue): Enqueue thread according to priority.
|
||||||
|
|
||||||
|
* internals.c (struct pthread_start_args): New struct for passing
|
||||||
|
args to cloning function.
|
||||||
|
(struct _pthread): Rename to _pthread_descr_struct and adapt for
|
||||||
|
opaque pthread_t.
|
||||||
|
|
||||||
|
* Examples/Makefile (clean): Pass -f option to rm.
|
||||||
|
|
||||||
|
* sysdeps/i386/pt-machine.h: Add check for compare-exchange instruction
|
||||||
|
and define TEST_FOR_COMPARE_AND_SWAP.
|
||||||
|
* sysdeps/i386/i486/pt-machine.h: Removed.
|
||||||
|
|
||||||
|
* sysdeps/unix/sysv/linux/local_lim.h (PTHREAD_THREADS_MAX): Increase
|
||||||
|
to 1024.
|
||||||
|
|
||||||
|
1997-04-04 16:38 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* restart.h (suspend): Clear p_signal before suspending.
|
||||||
|
(suspend_with_cancellation): Likewise.
|
||||||
|
Patch by Xavier Leroy <Xavier.Leroy@inria.fr>.
|
||||||
|
|
||||||
|
* weaks.c: Make __pthread_key_create return 1.
|
||||||
|
* sysdeps/pthread/libc-lock.h: Define __libc_key_create,
|
||||||
|
__libc_getspecific, __libc_setspecific, and __libc_key_t.
|
||||||
|
* sysdeps/pthread/stdio-lock.h: Don't care for implementation not
|
||||||
|
using libio.
|
||||||
|
|
||||||
|
1997-03-19 15:13 Miguel de Icaza <miguel@nuclecu.unam.mx>
|
||||||
|
|
||||||
|
* sysdeps/sparc/pt-machine (RELEASE): Fix.
|
||||||
|
|
||||||
|
1997-03-01 07:55 Geoff Keating <geoffk@ozemail.com.au>
|
||||||
|
|
||||||
|
* sysdeps/powerpc/Implies: Added.
|
||||||
|
* sysdeps/powerpc/pt-machine.h: Added.
|
||||||
|
* sysdeps/powerpc/semaphorebits.h: Added.
|
||||||
|
|
||||||
|
1997-01-22 01:22 Ulrich Drepper <drepper@cygnus.com>
|
||||||
|
|
||||||
|
* linuxtheads/pthread.c (__pthread_initial_thread): Correct
|
||||||
|
initializer.
|
||||||
|
(__pthread_manager_thread): Likewise.
|
||||||
|
Reported by Andreas Jaeger.
|
||||||
|
|
||||||
|
1997-01-18 22:15 Richard Henderson <rth@tamu.edu>
|
||||||
|
|
||||||
|
Since sigset_t no longer fits in a register, we can't pass in the
|
||||||
|
thread's initial mask so easily. Take this opportunity to simplify
|
||||||
|
the clone implementation by only accepting a single void* argument.
|
||||||
|
|
||||||
|
* linuxthreads/manager.c (__pthread_manager): Put thread vitals
|
||||||
|
in the thread struct instead of as arguments through clone.
|
||||||
|
(pthread_start_thread): Look for them there.
|
||||||
|
* linuxthreads/internals.h (struct _pthread): Add p_initial_fn,
|
||||||
|
p_initial_fn_arg, p_initial_mask. Fix __pthread_manager proto.
|
||||||
|
* linuxthreads/pthread.c (pthread_initialize_manager): Revise
|
||||||
|
clone invocation.
|
73
linuxthreads/Changes
Normal file
73
linuxthreads/Changes
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
Release 0.7:
|
||||||
|
- Destructors for thread-specific data now conform to the POSIX semantics
|
||||||
|
(call destructors again if non-NULL TSD remains after a round of
|
||||||
|
destruction).
|
||||||
|
- Implemented thread-specific data as a sparse array, allows more TSD keys
|
||||||
|
and smaller thread descriptors (Ulrich Drepper).
|
||||||
|
- Added "error checking" mutexes.
|
||||||
|
- Protect against multiple sigwait() on the same signals.
|
||||||
|
- Simplified implementation of semaphores when compare_and_swap is
|
||||||
|
not available.
|
||||||
|
- Fixed bug in fork() where stdin was closed if fork() was called before
|
||||||
|
the first pthread_create().
|
||||||
|
- Fixed bug in the gethostby*_r functions (bad result if null bytes
|
||||||
|
in addresses).
|
||||||
|
- Typos in manual pages corrected.
|
||||||
|
- First cut at a PowerPC port (not working yet, runs into problems
|
||||||
|
with gcc and with the C library).
|
||||||
|
|
||||||
|
Release 0.6:
|
||||||
|
- Validation of thread identifiers: no more crashes when operating on
|
||||||
|
a thread that has exited (based on Pavel Krauz's ideas).
|
||||||
|
- Added fallback implementation of semaphores for the 386 and the
|
||||||
|
Sparc.
|
||||||
|
- Fixed a bug in signal handling causing false restarts of suspended
|
||||||
|
threads.
|
||||||
|
- Fixed a bug in realtime scheduling causing all threads to have
|
||||||
|
default scheduling on Ix86 with libc5.
|
||||||
|
- With realtime scheduling, unlocking a mutex now restarts the
|
||||||
|
highest priority thread waiting on the mutex, not the
|
||||||
|
first-suspended thread (Richard Neitzel).
|
||||||
|
- Timing a process now returns cumulative times for all threads, not
|
||||||
|
just times for the initial thread (suggested by Wolfram Gloger).
|
||||||
|
- Cleaned up name space (internal defs prefixed by __, weak aliases
|
||||||
|
for non-portable extensions).
|
||||||
|
- MIPS port (contributed by Ralf Baechle).
|
||||||
|
|
||||||
|
Release 0.5:
|
||||||
|
- Signal-safe semaphores a la POSIX 1003.1b added.
|
||||||
|
- Locking bug in pthread_mutex_trylock over recursive mutexes fixed.
|
||||||
|
- Race conditions in thread cancellation fixed.
|
||||||
|
- Sparc port (contributed by Miguel de Icaza).
|
||||||
|
- Support for getpwnam_r and getpwuid_r.
|
||||||
|
- Added pthread_kill_other_threads_np to be used in conjunction with
|
||||||
|
exec*().
|
||||||
|
|
||||||
|
Release 0.4:
|
||||||
|
- Manual pages for all functions.
|
||||||
|
- Synchronization bug causing accumulation of zombie processes fixed.
|
||||||
|
- Race condition in pthread_cond_timedwait fixed.
|
||||||
|
- Recursive mutexes are back by popular demand.
|
||||||
|
- Partial support for realtime scheduling (initiated by Richard Neitzel).
|
||||||
|
- pthread.h cleaned up a lot: now C++ compatible, added missing "const"
|
||||||
|
qualifiers, added short documentation, put to GNU libc standards
|
||||||
|
for name space pollution (Ulrich Drepper).
|
||||||
|
- Motorola 68k port (contributed by Andreas Schwab).
|
||||||
|
- Interaction with fork(2) cleaned up a lot.
|
||||||
|
|
||||||
|
Release 0.3:
|
||||||
|
- Thread creation and reclaimation now performed by a centralized
|
||||||
|
"thread manager" thread.
|
||||||
|
- Removed recursive mutexes to make regular mutexes more efficient.
|
||||||
|
- Now available as a shared library (contributed by Richard Henderson).
|
||||||
|
- Alpha port (contributed by Richard Henderson).
|
||||||
|
- Fixed many small discrepancies with Posix 1003.1c.
|
||||||
|
- Put under the LGPL instead of the GPL.
|
||||||
|
|
||||||
|
Release 0.2:
|
||||||
|
- Reentrant libc functions (adapted from libc 5.3.9 by Peeter Joot)
|
||||||
|
- pthread_cond_wait did not reacquire the mutex correctly on return
|
||||||
|
- More efficient pthread_cond_broadcast
|
||||||
|
|
||||||
|
Release 0.1:
|
||||||
|
- First public release
|
15
linuxthreads/Examples/Makefile
Normal file
15
linuxthreads/Examples/Makefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-g -O -Wall -I.. -D_REENTRANT
|
||||||
|
LIBPTHREAD=../libpthread.a
|
||||||
|
|
||||||
|
PROGS=ex1 ex2 ex3 ex4 ex5 proxy
|
||||||
|
|
||||||
|
all: $(PROGS)
|
||||||
|
|
||||||
|
.c:
|
||||||
|
$(CC) $(CFLAGS) -o $* $*.c $(LIBPTHREAD)
|
||||||
|
|
||||||
|
$(PROGS):
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PROGS)
|
36
linuxthreads/Examples/ex1.c
Normal file
36
linuxthreads/Examples/ex1.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* Creates two threads, one printing 10000 "a"s, the other printing
|
||||||
|
10000 "b"s.
|
||||||
|
Illustrates: thread creation, thread joining. */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
|
||||||
|
void * process(void * arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
fprintf(stderr, "Starting process %s\n", (char *) arg);
|
||||||
|
for (i = 0; i < 10000; i++) {
|
||||||
|
write(1, (char *) arg, 1);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int retcode;
|
||||||
|
pthread_t th_a, th_b;
|
||||||
|
void * retval;
|
||||||
|
|
||||||
|
retcode = pthread_create(&th_a, NULL, process, "a");
|
||||||
|
if (retcode != 0) fprintf(stderr, "create a failed %d\n", retcode);
|
||||||
|
retcode = pthread_create(&th_b, NULL, process, "b");
|
||||||
|
if (retcode != 0) fprintf(stderr, "create b failed %d\n", retcode);
|
||||||
|
retcode = pthread_join(th_a, &retval);
|
||||||
|
if (retcode != 0) fprintf(stderr, "join a failed %d\n", retcode);
|
||||||
|
retcode = pthread_join(th_b, &retval);
|
||||||
|
if (retcode != 0) fprintf(stderr, "join b failed %d\n", retcode);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
116
linuxthreads/Examples/ex2.c
Normal file
116
linuxthreads/Examples/ex2.c
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/* The classic producer-consumer example.
|
||||||
|
Illustrates mutexes and conditions.
|
||||||
|
All integers between 0 and 9999 should be printed exactly twice,
|
||||||
|
once to the right of the arrow and once to the left. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 16
|
||||||
|
|
||||||
|
/* Circular buffer of integers. */
|
||||||
|
|
||||||
|
struct prodcons {
|
||||||
|
int buffer[BUFFER_SIZE]; /* the actual data */
|
||||||
|
pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */
|
||||||
|
int readpos, writepos; /* positions for reading and writing */
|
||||||
|
pthread_cond_t notempty; /* signaled when buffer is not empty */
|
||||||
|
pthread_cond_t notfull; /* signaled when buffer is not full */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize a buffer */
|
||||||
|
|
||||||
|
void init(struct prodcons * b)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&b->lock, NULL);
|
||||||
|
pthread_cond_init(&b->notempty, NULL);
|
||||||
|
pthread_cond_init(&b->notfull, NULL);
|
||||||
|
b->readpos = 0;
|
||||||
|
b->writepos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store an integer in the buffer */
|
||||||
|
|
||||||
|
void put(struct prodcons * b, int data)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&b->lock);
|
||||||
|
/* Wait until buffer is not full */
|
||||||
|
while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
|
||||||
|
pthread_cond_wait(&b->notfull, &b->lock);
|
||||||
|
/* pthread_cond_wait reacquired b->lock before returning */
|
||||||
|
}
|
||||||
|
/* Write the data and advance write pointer */
|
||||||
|
b->buffer[b->writepos] = data;
|
||||||
|
b->writepos++;
|
||||||
|
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
|
||||||
|
/* Signal that the buffer is now not empty */
|
||||||
|
pthread_cond_signal(&b->notempty);
|
||||||
|
pthread_mutex_unlock(&b->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and remove an integer from the buffer */
|
||||||
|
|
||||||
|
int get(struct prodcons * b)
|
||||||
|
{
|
||||||
|
int data;
|
||||||
|
pthread_mutex_lock(&b->lock);
|
||||||
|
/* Wait until buffer is not empty */
|
||||||
|
while (b->writepos == b->readpos) {
|
||||||
|
pthread_cond_wait(&b->notempty, &b->lock);
|
||||||
|
}
|
||||||
|
/* Read the data and advance read pointer */
|
||||||
|
data = b->buffer[b->readpos];
|
||||||
|
b->readpos++;
|
||||||
|
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
|
||||||
|
/* Signal that the buffer is now not full */
|
||||||
|
pthread_cond_signal(&b->notfull);
|
||||||
|
pthread_mutex_unlock(&b->lock);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A test program: one thread inserts integers from 1 to 10000,
|
||||||
|
the other reads them and prints them. */
|
||||||
|
|
||||||
|
#define OVER (-1)
|
||||||
|
|
||||||
|
struct prodcons buffer;
|
||||||
|
|
||||||
|
void * producer(void * data)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for (n = 0; n < 10000; n++) {
|
||||||
|
printf("%d --->\n", n);
|
||||||
|
put(&buffer, n);
|
||||||
|
}
|
||||||
|
put(&buffer, OVER);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * consumer(void * data)
|
||||||
|
{
|
||||||
|
int d;
|
||||||
|
while (1) {
|
||||||
|
d = get(&buffer);
|
||||||
|
if (d == OVER) break;
|
||||||
|
printf("---> %d\n", d);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
pthread_t th_a, th_b;
|
||||||
|
void * retval;
|
||||||
|
|
||||||
|
init(&buffer);
|
||||||
|
/* Create the threads */
|
||||||
|
pthread_create(&th_a, NULL, producer, 0);
|
||||||
|
pthread_create(&th_b, NULL, consumer, 0);
|
||||||
|
/* Wait until producer and consumer finish. */
|
||||||
|
pthread_join(th_a, &retval);
|
||||||
|
pthread_join(th_b, &retval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
144
linuxthreads/Examples/ex3.c
Normal file
144
linuxthreads/Examples/ex3.c
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/* Multi-thread searching.
|
||||||
|
Illustrates: thread cancellation, cleanup handlers. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* Defines the number of searching threads */
|
||||||
|
#define NUM_THREADS 5
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
void *search(void *);
|
||||||
|
void print_it(void *);
|
||||||
|
|
||||||
|
/* Global variables */
|
||||||
|
pthread_t threads[NUM_THREADS];
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
int tries;
|
||||||
|
|
||||||
|
int main(argc, argv)
|
||||||
|
int argc;
|
||||||
|
char ** argv;
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
/* create a number to search for */
|
||||||
|
pid = getpid();
|
||||||
|
printf("Searching for the number = %d...\n", pid);
|
||||||
|
|
||||||
|
/* Initialize the mutex lock */
|
||||||
|
pthread_mutex_init(&lock, NULL);
|
||||||
|
|
||||||
|
/* Create the searching threads */
|
||||||
|
for (i=0; i<NUM_THREADS; i++)
|
||||||
|
pthread_create(&threads[i], NULL, search, (void *)pid);
|
||||||
|
|
||||||
|
/* Wait for (join) all the searching threads */
|
||||||
|
for (i=0; i<NUM_THREADS; i++)
|
||||||
|
pthread_join(threads[i], NULL);
|
||||||
|
|
||||||
|
printf("It took %d tries to find the number.\n", tries);
|
||||||
|
|
||||||
|
/* Exit the program */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the cleanup function that is called
|
||||||
|
when the threads are cancelled */
|
||||||
|
|
||||||
|
void print_it(void *arg)
|
||||||
|
{
|
||||||
|
int *try = (int *) arg;
|
||||||
|
pthread_t tid;
|
||||||
|
|
||||||
|
/* Get the calling thread's ID */
|
||||||
|
tid = pthread_self();
|
||||||
|
|
||||||
|
/* Print where the thread was in its search when it was cancelled */
|
||||||
|
printf("Thread %lx was canceled on its %d try.\n", tid, *try);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the search routine that is executed in each thread */
|
||||||
|
|
||||||
|
void *search(void *arg)
|
||||||
|
{
|
||||||
|
int num = (int) arg;
|
||||||
|
int i, j, ntries;
|
||||||
|
pthread_t tid;
|
||||||
|
|
||||||
|
/* get the calling thread ID */
|
||||||
|
tid = pthread_self();
|
||||||
|
|
||||||
|
/* use the thread ID to set the seed for the random number generator */
|
||||||
|
/* Since srand and rand are not thread-safe, serialize with lock */
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
srand((int)tid);
|
||||||
|
i = rand() & 0xFFFFFF;
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
ntries = 0;
|
||||||
|
|
||||||
|
/* Set the cancellation parameters --
|
||||||
|
- Enable thread cancellation
|
||||||
|
- Defer the action of the cancellation */
|
||||||
|
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||||
|
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
|
||||||
|
|
||||||
|
/* Push the cleanup routine (print_it) onto the thread
|
||||||
|
cleanup stack. This routine will be called when the
|
||||||
|
thread is cancelled. Also note that the pthread_cleanup_push
|
||||||
|
call must have a matching pthread_cleanup_pop call. The
|
||||||
|
push and pop calls MUST be at the same lexical level
|
||||||
|
within the code */
|
||||||
|
|
||||||
|
/* Pass address of `ntries' since the current value of `ntries' is not
|
||||||
|
the one we want to use in the cleanup function */
|
||||||
|
|
||||||
|
pthread_cleanup_push(print_it, (void *)&ntries);
|
||||||
|
|
||||||
|
/* Loop forever */
|
||||||
|
while (1) {
|
||||||
|
i = (i + 1) & 0xFFFFFF;
|
||||||
|
ntries++;
|
||||||
|
|
||||||
|
/* Does the random number match the target number? */
|
||||||
|
if (num == i) {
|
||||||
|
/* Try to lock the mutex lock --
|
||||||
|
if locked, check to see if the thread has been cancelled
|
||||||
|
if not locked then continue */
|
||||||
|
while (pthread_mutex_trylock(&lock) == EBUSY)
|
||||||
|
pthread_testcancel();
|
||||||
|
|
||||||
|
/* Set the global variable for the number of tries */
|
||||||
|
tries = ntries;
|
||||||
|
printf("Thread %lx found the number!\n", tid);
|
||||||
|
|
||||||
|
/* Cancel all the other threads */
|
||||||
|
for (j=0; j<NUM_THREADS; j++)
|
||||||
|
if (threads[j] != tid) pthread_cancel(threads[j]);
|
||||||
|
|
||||||
|
/* Break out of the while loop */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Every 100 tries check to see if the thread has been cancelled. */
|
||||||
|
if (ntries % 100 == 0) {
|
||||||
|
pthread_testcancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The only way we can get here is when the thread breaks out
|
||||||
|
of the while loop. In this case the thread that makes it here
|
||||||
|
has found the number we are looking for and does not need to run
|
||||||
|
the thread cleanup function. This is why the pthread_cleanup_pop
|
||||||
|
function is called with a 0 argument; this will pop the cleanup
|
||||||
|
function off the stack without executing it */
|
||||||
|
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
|
return((void *)0);
|
||||||
|
}
|
||||||
|
|
107
linuxthreads/Examples/ex4.c
Normal file
107
linuxthreads/Examples/ex4.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/* Making a library function that uses static variables thread-safe.
|
||||||
|
Illustrates: thread-specific data, pthread_once(). */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* This is a typical example of a library function that uses
|
||||||
|
static variables to accumulate results between calls.
|
||||||
|
Here, it just returns the concatenation of all string arguments
|
||||||
|
that were given to it. */
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
char * str_accumulate(char * s)
|
||||||
|
{
|
||||||
|
static char accu[1024] = { 0 };
|
||||||
|
strcat(accu, s);
|
||||||
|
return accu;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Of course, this cannot be used in a multi-threaded program
|
||||||
|
because all threads store "accu" at the same location.
|
||||||
|
So, we'll use thread-specific data to have a different "accu"
|
||||||
|
for each thread. */
|
||||||
|
|
||||||
|
/* Key identifying the thread-specific data */
|
||||||
|
static pthread_key_t str_key;
|
||||||
|
/* "Once" variable ensuring that the key for str_alloc will be allocated
|
||||||
|
exactly once. */
|
||||||
|
static pthread_once_t str_alloc_key_once = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
/* Forward functions */
|
||||||
|
static void str_alloc_key(void);
|
||||||
|
static void str_alloc_destroy_accu(void * accu);
|
||||||
|
|
||||||
|
/* Thread-safe version of str_accumulate */
|
||||||
|
|
||||||
|
char * str_accumulate(char * s)
|
||||||
|
{
|
||||||
|
char * accu;
|
||||||
|
|
||||||
|
/* Make sure the key is allocated */
|
||||||
|
pthread_once(&str_alloc_key_once, str_alloc_key);
|
||||||
|
/* Get the thread-specific data associated with the key */
|
||||||
|
accu = (char *) pthread_getspecific(str_key);
|
||||||
|
/* It's initially NULL, meaning that we must allocate the buffer first. */
|
||||||
|
if (accu == NULL) {
|
||||||
|
accu = malloc(1024);
|
||||||
|
if (accu == NULL) return NULL;
|
||||||
|
accu[0] = 0;
|
||||||
|
/* Store the buffer pointer in the thread-specific data. */
|
||||||
|
pthread_setspecific(str_key, (void *) accu);
|
||||||
|
printf("Thread %lx: allocating buffer at %p\n", pthread_self(), accu);
|
||||||
|
}
|
||||||
|
/* Now we can use accu just as in the non thread-safe code. */
|
||||||
|
strcat(accu, s);
|
||||||
|
return accu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function to allocate the key for str_alloc thread-specific data. */
|
||||||
|
|
||||||
|
static void str_alloc_key(void)
|
||||||
|
{
|
||||||
|
pthread_key_create(&str_key, str_alloc_destroy_accu);
|
||||||
|
printf("Thread %lx: allocated key %d\n", pthread_self(), str_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function to free the buffer when the thread exits. */
|
||||||
|
/* Called only when the thread-specific data is not NULL. */
|
||||||
|
|
||||||
|
static void str_alloc_destroy_accu(void * accu)
|
||||||
|
{
|
||||||
|
printf("Thread %lx: freeing buffer at %p\n", pthread_self(), accu);
|
||||||
|
free(accu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test program */
|
||||||
|
|
||||||
|
void * process(void * arg)
|
||||||
|
{
|
||||||
|
char * res;
|
||||||
|
res = str_accumulate("Result of ");
|
||||||
|
res = str_accumulate((char *) arg);
|
||||||
|
res = str_accumulate(" thread");
|
||||||
|
printf("Thread %lx: \"%s\"\n", pthread_self(), res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char ** argv)
|
||||||
|
{
|
||||||
|
char * res;
|
||||||
|
pthread_t th1, th2;
|
||||||
|
|
||||||
|
res = str_accumulate("Result of ");
|
||||||
|
pthread_create(&th1, NULL, process, "first");
|
||||||
|
pthread_create(&th2, NULL, process, "second");
|
||||||
|
res = str_accumulate("initial thread");
|
||||||
|
printf("Thread %lx: \"%s\"\n", pthread_self(), res);
|
||||||
|
pthread_join(th1, NULL);
|
||||||
|
pthread_join(th2, NULL);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
102
linuxthreads/Examples/ex5.c
Normal file
102
linuxthreads/Examples/ex5.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/* The classic producer-consumer example, implemented with semaphores.
|
||||||
|
All integers between 0 and 9999 should be printed exactly twice,
|
||||||
|
once to the right of the arrow and once to the left. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "semaphore.h"
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 16
|
||||||
|
|
||||||
|
/* Circular buffer of integers. */
|
||||||
|
|
||||||
|
struct prodcons {
|
||||||
|
int buffer[BUFFER_SIZE]; /* the actual data */
|
||||||
|
int readpos, writepos; /* positions for reading and writing */
|
||||||
|
sem_t sem_read; /* number of elements available for reading */
|
||||||
|
sem_t sem_write; /* number of locations available for writing */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize a buffer */
|
||||||
|
|
||||||
|
void init(struct prodcons * b)
|
||||||
|
{
|
||||||
|
sem_init(&b->sem_write, 0, BUFFER_SIZE - 1);
|
||||||
|
sem_init(&b->sem_read, 0, 0);
|
||||||
|
b->readpos = 0;
|
||||||
|
b->writepos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store an integer in the buffer */
|
||||||
|
|
||||||
|
void put(struct prodcons * b, int data)
|
||||||
|
{
|
||||||
|
/* Wait until buffer is not full */
|
||||||
|
sem_wait(&b->sem_write);
|
||||||
|
/* Write the data and advance write pointer */
|
||||||
|
b->buffer[b->writepos] = data;
|
||||||
|
b->writepos++;
|
||||||
|
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
|
||||||
|
/* Signal that the buffer contains one more element for reading */
|
||||||
|
sem_post(&b->sem_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read and remove an integer from the buffer */
|
||||||
|
|
||||||
|
int get(struct prodcons * b)
|
||||||
|
{
|
||||||
|
int data;
|
||||||
|
/* Wait until buffer is not empty */
|
||||||
|
sem_wait(&b->sem_read);
|
||||||
|
/* Read the data and advance read pointer */
|
||||||
|
data = b->buffer[b->readpos];
|
||||||
|
b->readpos++;
|
||||||
|
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
|
||||||
|
/* Signal that the buffer has now one more location for writing */
|
||||||
|
sem_post(&b->sem_write);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A test program: one thread inserts integers from 1 to 10000,
|
||||||
|
the other reads them and prints them. */
|
||||||
|
|
||||||
|
#define OVER (-1)
|
||||||
|
|
||||||
|
struct prodcons buffer;
|
||||||
|
|
||||||
|
void * producer(void * data)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for (n = 0; n < 10000; n++) {
|
||||||
|
printf("%d --->\n", n);
|
||||||
|
put(&buffer, n);
|
||||||
|
}
|
||||||
|
put(&buffer, OVER);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * consumer(void * data)
|
||||||
|
{
|
||||||
|
int d;
|
||||||
|
while (1) {
|
||||||
|
d = get(&buffer);
|
||||||
|
if (d == OVER) break;
|
||||||
|
printf("---> %d\n", d);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
pthread_t th_a, th_b;
|
||||||
|
void * retval;
|
||||||
|
|
||||||
|
init(&buffer);
|
||||||
|
/* Create the threads */
|
||||||
|
pthread_create(&th_a, NULL, producer, 0);
|
||||||
|
pthread_create(&th_b, NULL, consumer, 0);
|
||||||
|
/* Wait until producer and consumer finish. */
|
||||||
|
pthread_join(th_a, &retval);
|
||||||
|
pthread_join(th_b, &retval);
|
||||||
|
return 0;
|
||||||
|
}
|
986
linuxthreads/FAQ.html
Normal file
986
linuxthreads/FAQ.html
Normal file
@ -0,0 +1,986 @@
|
|||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<TITLE>LinuxThreads Frequently Asked Questions</TITLE>
|
||||||
|
</HEAD>
|
||||||
|
<BODY>
|
||||||
|
<H1 ALIGN=center>LinuxThreads Frequently Asked Questions <BR>
|
||||||
|
(with answers)</H1>
|
||||||
|
|
||||||
|
<HR><P>
|
||||||
|
|
||||||
|
<A HREF="#A">A. The big picture</A><BR>
|
||||||
|
<A HREF="#B">B. Getting more information</A><BR>
|
||||||
|
<A HREF="#C">C. Issues related to the C library</A><BR>
|
||||||
|
<A HREF="#D">D. Problems, weird behaviors, potential bugs</A><BR>
|
||||||
|
<A HREF="#E">E. Missing functions, wrong types, etc</A><BR>
|
||||||
|
<A HREF="#F">F. C++ issues</A><BR>
|
||||||
|
<A HREF="#G">G. Debugging LinuxThreads programs</A><BR>
|
||||||
|
<A HREF="#H">H. Compiling multithreaded code; errno madness</A><BR>
|
||||||
|
<A HREF="#I">I. X-Windows and other libraries</A><BR>
|
||||||
|
<A HREF="#J">J. Signals and threads</A><BR>
|
||||||
|
<A HREF="#K">K. Internals of LinuxThreads</A><P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="A">A. The big picture</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="A.1">A.1: What is LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
LinuxThreads is a Linux library for multi-threaded programming.
|
||||||
|
It implements the Posix 1003.1c API (Application Programming
|
||||||
|
Interface) for threads. It runs on any Linux system with kernel 2.0.0
|
||||||
|
or more recent, and a suitable C library (see section <A HREF="B">B</A>).
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H4><A NAME="A.2">A.2: What are threads?</A></H4>
|
||||||
|
|
||||||
|
A thread is a sequential flow of control through a program.
|
||||||
|
Multi-threaded programming is, thus, a form of parallel programming
|
||||||
|
where several threads of control are executing concurrently in the
|
||||||
|
program. All threads execute in the same memory space, and can
|
||||||
|
therefore work concurrently on shared data.<P>
|
||||||
|
|
||||||
|
Multi-threaded programming differs from Unix-style multi-processing in
|
||||||
|
that all threads share the same memory space (and a few other system
|
||||||
|
resources, such as file descriptors), instead of running in their own
|
||||||
|
memory space as is the case with Unix processes.<P>
|
||||||
|
|
||||||
|
Threads are useful for two reasons. First, they allow a program to
|
||||||
|
exploit multi-processor machines: the threads can run in parallel on
|
||||||
|
several processors, allowing a single program to divide its work
|
||||||
|
between several processors, thus running faster than a single-threaded
|
||||||
|
program, which runs on only one processor at a time. Second, some
|
||||||
|
programs are best expressed as several threads of control that
|
||||||
|
communicate together, rather than as one big monolithic sequential
|
||||||
|
program. Examples include server programs, overlapping asynchronous
|
||||||
|
I/O, and graphical user interfaces.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="A.3">A.3: What is POSIX 1003.1c?</A></H4>
|
||||||
|
|
||||||
|
It's an API for multi-threaded programming standardized by IEEE as
|
||||||
|
part of the POSIX standards. Most Unix vendors have endorsed the
|
||||||
|
POSIX 1003.1c standard. Implementations of the 1003.1c API are
|
||||||
|
already available under Sun Solaris 2.5, Digital Unix 4.0,
|
||||||
|
Silicon Graphics IRIX 6, and should soon be available from other
|
||||||
|
vendors such as IBM and HP. More generally, the 1003.1c API is
|
||||||
|
replacing relatively quickly the proprietary threads library that were
|
||||||
|
developed previously under Unix, such as Mach cthreads, Solaris
|
||||||
|
threads, and IRIX sprocs. Thus, multithreaded programs using the
|
||||||
|
1003.1c API are likely to run unchanged on a wide variety of Unix
|
||||||
|
platforms.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="A.4">A.4: What is the status of LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
In short, it's not completely finished (hence the version numbers in
|
||||||
|
0.<I>x</I>), but what is done is pretty mature.
|
||||||
|
LinuxThreads implements almost all of Posix 1003.1c, as well as a few
|
||||||
|
extensions. The only part of LinuxThreads that does not conform yet
|
||||||
|
to Posix is signal handling (see section <A HREF="#J">J</A>). Apart
|
||||||
|
from the signal stuff, all the Posix 1003.1c base functionality is
|
||||||
|
provided and conforms to the standard (to the best of my knowledge).
|
||||||
|
The signal stuff is hard to get right, at least without special kernel
|
||||||
|
support, and while I'm definitely looking at ways to implement the
|
||||||
|
Posix behavior for signals, this might take a long time before it's
|
||||||
|
completed.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="A.5">A.5: How stable is LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
The basic functionality (thread creation and termination, mutexes,
|
||||||
|
conditions, semaphores) is very stable. Several industrial-strength
|
||||||
|
programs, such as the AOL multithreaded Web server, use LinuxThreads
|
||||||
|
and seem quite happy about it. There are some rough edges in
|
||||||
|
the LinuxThreads / C library interface, at least with libc 5, but most
|
||||||
|
of these rough edges are fixed in glibc 2, which should soon become
|
||||||
|
the standard C library for Linux distributions (see section <A
|
||||||
|
HREF="#C">C</A>). <P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="B">B. Getting more information</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="B.1">B.1: What are good books and other sources of
|
||||||
|
information on POSIX threads?</A></H4>
|
||||||
|
|
||||||
|
The FAQ for comp.programming.threads lists several books:
|
||||||
|
<A HREF="http://www.serpentine.com/~bos/threads-faq/">http://www.serpentine.com/~bos/threads-faq/</A>.<P>
|
||||||
|
|
||||||
|
There are also some online tutorials. Follow the links from the
|
||||||
|
LinuxThreads web page:
|
||||||
|
<A HREF="http://pauillac.inria.fr/~xleroy/linuxthreads">http://pauillac.inria.fr/~xleroy/linuxthreads</A>.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="B.2">B.2: I'd like to be informed of future developments on
|
||||||
|
LinuxThreads. Is there a mailing list for this purpose?</A></H4>
|
||||||
|
|
||||||
|
I post LinuxThreads-related announcements on the newsgroup
|
||||||
|
<A HREF="news:comp.os.linux.announce">comp.os.linux.announce</A>,
|
||||||
|
and also on the mailing list
|
||||||
|
<code>linux-threads@magenet.com</code>.
|
||||||
|
You can subscribe to the latter by writing
|
||||||
|
<A HREF="mailto:majordomo@magenet.com">majordomo@magenet.com</A>.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="B.3">B.3: What are good places for discussing
|
||||||
|
LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
For questions about programming with POSIX threads in general, use
|
||||||
|
the newsgroup
|
||||||
|
<A HREF="news:comp.programming.threads">comp.programming.threads</A>.
|
||||||
|
Be sure you read the
|
||||||
|
<A HREF="http://www.serpentine.com/~bos/threads-faq/">FAQ</A>
|
||||||
|
for this group before you post.<P>
|
||||||
|
|
||||||
|
For Linux-specific questions, use
|
||||||
|
<A
|
||||||
|
HREF="news:comp.os.linux.development.apps">comp.os.linux.development.apps</A>
|
||||||
|
and <A
|
||||||
|
HREF="news:comp.os.linux.development.kernel">comp.os.linux.development.kernel</A>.
|
||||||
|
The latter is especially appropriate for questions relative to the
|
||||||
|
interface between the kernel and LinuxThreads.<P>
|
||||||
|
|
||||||
|
Very specific LinuxThreads questions, and in particular everything
|
||||||
|
that looks like a potential bug in LinuxThreads, should be mailed
|
||||||
|
directly to me (<code>Xavier.Leroy@inria.fr</code>). Before mailing
|
||||||
|
me, make sure that your question is not answered in this FAQ.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="B.4">B.4: I'd like to read the POSIX 1003.1c standard. Is
|
||||||
|
it available online?</A></H4>
|
||||||
|
|
||||||
|
Unfortunately, no. POSIX standards are copyrighted by IEEE, and
|
||||||
|
IEEE does not distribute them freely. You can buy paper copies from
|
||||||
|
IEEE, but the price is fairly high ($120 or so). If you disagree with
|
||||||
|
this policy and you're an IEEE member, be sure to let them know.<P>
|
||||||
|
|
||||||
|
On the other hand, you probably don't want to read the standard. It's
|
||||||
|
very hard to read, written in standard-ese, and targeted to
|
||||||
|
implementors who already know threads inside-out. A good book on
|
||||||
|
POSIX threads provides the same information in a much more readable form.
|
||||||
|
I can personally recommend Dave Butenhof's book, <CITE>Programming
|
||||||
|
with POSIX threads</CITE> (Addison-Wesley). Butenhof was part of the
|
||||||
|
POSIX committee and also designed the Digital Unix implementations of
|
||||||
|
POSIX threads, and it shows.<P>
|
||||||
|
|
||||||
|
Another good source of information is the X/Open Group Single Unix
|
||||||
|
specification which is available both
|
||||||
|
<A HREF="http://www.rdg.opengroup.org/onlinepubs/7908799/index.html">on-line</A>
|
||||||
|
and as a
|
||||||
|
<A HREF="http://www.UNIX-systems.org/gosolo2/">book and CD/ROM</A>.
|
||||||
|
That specification includes pretty much all the POSIX standards,
|
||||||
|
including 1003.1c, with some extensions and clarifications.<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="C">C. Issues related to the C library</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="C.1">C.1: Which version of the C library should I use
|
||||||
|
with LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
Most current Linux distributions come with libc version 5, maintained
|
||||||
|
by H.J.Lu. For LinuxThreads to work properly, you must use either
|
||||||
|
libc 5.2.18 or libc 5.4.12 or later. Avoid 5.3.12 and 5.4.7: these
|
||||||
|
have problems with the per-thread errno variable.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
Unfortunately, many popular Linux distributions (e.g. RedHat 4.2) come
|
||||||
|
with libc 5.3.12 preinstalled -- the one that does not work with
|
||||||
|
LinuxThreads. Fortunately, you can often find pre-packaged binaries
|
||||||
|
of more recent versions of libc for these distributions. In the case
|
||||||
|
of RedHat 4, there is a RPM package for libc-5.4 in the "contrib"
|
||||||
|
area of RedHat FTP sites.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H4><A NAME="C.2">C.2: What about glibc 2, a.k.a. libc 6?</A></H4>
|
||||||
|
|
||||||
|
It's the next generation libc for Linux, developed by Ulrich
|
||||||
|
Drepper and other FSF collaborators. glibc 2 offers much better
|
||||||
|
support for threads than libc 5. Indeed, thread support was
|
||||||
|
planned from the very early stages of glibc 2, while it's a
|
||||||
|
last-minute addition to libc 5. glibc 2 actually comes with a
|
||||||
|
specially adapted version of LinuxThreads, which you can drop in the
|
||||||
|
glibc 2 sources as an add-on package.
|
||||||
|
|
||||||
|
<H4><A NAME="C.3">C.3: So, should I switch to glibc 2, or stay with a
|
||||||
|
recent libc 5?</A></H4>
|
||||||
|
|
||||||
|
Depends how you plan to do it. Switching an already installed
|
||||||
|
system from libc 5 to glibc 2 is not completely straightforward.
|
||||||
|
See the <A HREF="http://sunsite.unc.edu/LDP/HOWTO/Glibc2-HOWTO.html">Glibc2
|
||||||
|
HOWTO</A> for more information.
|
||||||
|
But (re-)installing a Linux distribution based on glibc 2 is easy.
|
||||||
|
One such distribution available now is RedHat 5.0. Debian and other
|
||||||
|
Linux distributors will also provide glibc 2-based distributions in the
|
||||||
|
near future.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H4><A NAME="C.4">C.4: Where can I find glibc 2 and the version of
|
||||||
|
LinuxThreads that goes with it?</A></H4>
|
||||||
|
|
||||||
|
On <code>prep.ai.mit.edu</code> and its many, many mirrors around the world.
|
||||||
|
See <A
|
||||||
|
HREF="http://www.gnu.org/order/ftp.html">http://www.gnu.org/order/ftp.html</A>
|
||||||
|
for a list of mirrors.<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="D">D. Problems, weird behaviors, potential bugs</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="D.1">D.1: When I compile LinuxThreads, I run into problems in
|
||||||
|
file <code>libc_r/dirent.c</code></A></H4>
|
||||||
|
|
||||||
|
You probably mean:
|
||||||
|
<PRE>
|
||||||
|
libc_r/dirent.c:94: structure has no member named `dd_lock'
|
||||||
|
</PRE>
|
||||||
|
I haven't actually seen this problem, but several users reported it.
|
||||||
|
My understanding is that something is wrong in the include files of
|
||||||
|
your Linux installation (<code>/usr/include/*</code>). Make sure
|
||||||
|
you're using a supported version of the C library. (See section <A
|
||||||
|
HREF="#B">B</A>).<P>
|
||||||
|
|
||||||
|
<H4><A NAME="D.2">D.2: When I compile LinuxThreads, I run into problems with
|
||||||
|
<CODE>/usr/include/sched.h</CODE>: there are several occurrences of
|
||||||
|
<CODE>_p</CODE> that the C compiler does not understand</A></H4>
|
||||||
|
|
||||||
|
Yes, <CODE>/usr/include/sched.h</CODE> that comes with libc 5.3.12 is broken.
|
||||||
|
Replace it with the <code>sched.h</code> file contained in the
|
||||||
|
LinuxThreads distribution. But really you should not be using libc
|
||||||
|
5.3.12 with LinuxThreads! (See question <A HREF="#C.1">C.1</A>.)<P>
|
||||||
|
|
||||||
|
<H4><A NAME="D.3">D.3: My program does <CODE>fdopen()</CODE> on a file
|
||||||
|
descriptor opened on a pipe. When I link it with LinuxThreads,
|
||||||
|
<CODE>fdopen()</CODE> always returns NULL!</A></H4>
|
||||||
|
|
||||||
|
You're using one of the buggy versions of libc (5.3.12, 5.4.7., etc).
|
||||||
|
See question <A HREF="#C.1">C.1</A> above.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="D.4">D.4: My program crashes the first time it calls
|
||||||
|
<CODE>pthread_create()</CODE> !</A></H4>
|
||||||
|
|
||||||
|
You wouldn't be using glibc 2.0, by any chance? That's a known bug
|
||||||
|
with glibc 2.0. Please upgrade to 2.0.1 or later.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="D.5">D.5: When I'm running a program that creates N
|
||||||
|
threads, <code>top</code> or <code>ps</code>
|
||||||
|
display N+2 processes that are running my program. What do all these
|
||||||
|
processes correspond to?</A></H4>
|
||||||
|
|
||||||
|
Due to the general "one process per thread" model, there's one process
|
||||||
|
for the initial thread and N processes for the threads it created
|
||||||
|
using <CODE>pthread_create</CODE>. That leaves one process
|
||||||
|
unaccounted for. That extra process corresponds to the "thread
|
||||||
|
manager" thread, a thread created internally by LinuxThreads to handle
|
||||||
|
thread creation and thread termination. This extra thread is asleep
|
||||||
|
most of the time.
|
||||||
|
|
||||||
|
<H4><A NAME="D.6">D.6: Scheduling seems to be very unfair when there
|
||||||
|
is strong contention on a mutex: instead of giving the mutex to each
|
||||||
|
thread in turn, it seems that it's almost always the same thread that
|
||||||
|
gets the mutex. Isn't this completely broken behavior?</A></H4>
|
||||||
|
|
||||||
|
What happens is the following: when a thread unlocks a mutex, all
|
||||||
|
other threads that were waiting on the mutex are sent a signal which
|
||||||
|
makes them runnable. However, the kernel scheduler may or may not
|
||||||
|
restart them immediately. If the thread that unlocked the mutex
|
||||||
|
tries to lock it again immediately afterwards, it is likely that it
|
||||||
|
will succeed, because the threads haven't yet restarted. This results
|
||||||
|
in an apparently very unfair behavior, when the same thread repeatedly
|
||||||
|
locks and unlocks the mutex, while other threads can't lock the mutex.<P>
|
||||||
|
|
||||||
|
This is perfectly acceptable behavior with respect to the POSIX
|
||||||
|
standard: for the default scheduling policy, POSIX makes no guarantees
|
||||||
|
of fairness, such as "the thread waiting for the mutex for the longest
|
||||||
|
time always acquires it first". This allows implementations of
|
||||||
|
mutexes to remain simple and efficient. Properly written
|
||||||
|
multithreaded code avoids that kind of heavy contention on mutexes,
|
||||||
|
and does not run into fairness problems. If you need scheduling
|
||||||
|
guarantees, you should consider using the real-time scheduling
|
||||||
|
policies <code>SCHED_RR</code> and <code>SCHED_FIFO</code>, which have
|
||||||
|
precisely defined scheduling behaviors. <P>
|
||||||
|
|
||||||
|
<H4><A NAME="D.7">D.7: I have a simple test program with two threads
|
||||||
|
that do nothing but <CODE>printf()</CODE> in tight loops, and from the
|
||||||
|
printout it seems that only one thread is running, the other doesn't
|
||||||
|
print anything!</A></H4>
|
||||||
|
|
||||||
|
If you wait long enough, you should see the second thread kick in.
|
||||||
|
But still, you're right, one thread prevents the other one from
|
||||||
|
running for long periods of time. The reason is explained in
|
||||||
|
question <A HREF="#D.6">D.6</A> above: <CODE>printf()</CODE> performs
|
||||||
|
locking on <CODE>stdout</CODE>, and thus your two threads contend very
|
||||||
|
heavily for the mutex associated with <CODE>stdout</CODE>. But if you
|
||||||
|
do some real work between two calls to <CODE>printf()</CODE>, you'll
|
||||||
|
see that scheduling becomes much smoother. <P>
|
||||||
|
|
||||||
|
<H4><A NAME="D.8">D.8: I've looked at <code><pthread.h></code>
|
||||||
|
and there seems to be a gross error in the <code>pthread_cleanup_push</code>
|
||||||
|
macro: it opens a block with <code>{</code> but does not close it!
|
||||||
|
Surely you forgot a <code>}</code> at the end of the macro, right?
|
||||||
|
</A></H4>
|
||||||
|
|
||||||
|
Nope. That's the way it should be. The closing brace is provided by
|
||||||
|
the <code>pthread_cleanup_pop</code> macro. The POSIX standard
|
||||||
|
requires <code>pthread_cleanup_push</code> and
|
||||||
|
<code>pthread_cleanup_pop</code> to be used in matching pairs, at the
|
||||||
|
same level of brace nesting. This allows
|
||||||
|
<code>pthread_cleanup_push</code> to open a block in order to
|
||||||
|
stack-allocate some data structure, and
|
||||||
|
<code>pthread_cleanup_pop</code> to close that block. It's ugly, but
|
||||||
|
it's the standard way of implementing cleanup handlers.<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="E">E. Missing functions, wrong types, etc</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="E.1">E.1: Where is <CODE>pthread_yield()</CODE> ? How
|
||||||
|
comes LinuxThreads does not implement it?</A></H4>
|
||||||
|
|
||||||
|
Because it's not part of the (final) POSIX 1003.1c standard.
|
||||||
|
Several drafts of the standard contained <CODE>pthread_yield()</CODE>,
|
||||||
|
but then the POSIX guys discovered it was redundant with
|
||||||
|
<CODE>sched_yield()</CODE> and dropped it. So, just use
|
||||||
|
<CODE>sched_yield()</CODE> instead.
|
||||||
|
|
||||||
|
<H4><A NAME="E.2">E.2: I've found some type errors in
|
||||||
|
<code><pthread.h></code>.
|
||||||
|
For instance, the second argument to <CODE>pthread_create()</CODE>
|
||||||
|
should be a <CODE>pthread_attr_t</CODE>, not a
|
||||||
|
<CODE>pthread_attr_t *</CODE>. Also, didn't you forget to declare
|
||||||
|
<CODE>pthread_attr_default</CODE>?</A></H4>
|
||||||
|
|
||||||
|
No, I didn't. What you're describing is draft 4 of the POSIX
|
||||||
|
standard, which is used in OSF DCE threads. LinuxThreads conforms to the
|
||||||
|
final standard. Even though the functions have the same names as in
|
||||||
|
draft 4 and DCE, their calling conventions are slightly different. In
|
||||||
|
particular, attributes are passed by reference, not by value, and
|
||||||
|
default attributes are denoted by the NULL pointer. Since draft 4/DCE
|
||||||
|
will eventually disappear, you'd better port your program to use the
|
||||||
|
standard interface.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="E.3">E.3: I'm porting an application from Solaris and I
|
||||||
|
have to rename all thread functions from <code>thr_blah</code> to
|
||||||
|
<CODE>pthread_blah</CODE>. This is very annoying. Why did you change
|
||||||
|
all the function names?</A></H4>
|
||||||
|
|
||||||
|
POSIX did it. The <code>thr_*</code> functions correspond to Solaris
|
||||||
|
threads, an older thread interface that you'll find only under
|
||||||
|
Solaris. The <CODE>pthread_*</CODE> functions correspond to POSIX
|
||||||
|
threads, an international standard available for many, many platforms.
|
||||||
|
Even Solaris 2.5 and later support the POSIX threads interface. So,
|
||||||
|
do yourself a favor and rewrite your code to use POSIX threads: this
|
||||||
|
way, it will run unchanged under Linux, Solaris, and quite a lot of
|
||||||
|
other platforms.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="E.4">E.4: How can I suspend and resume a thread from
|
||||||
|
another thread? Solaris has the <CODE>thr_suspend()</CODE> and
|
||||||
|
<CODE>thr_resume()</CODE> functions to do that; why don't you?</A></H4>
|
||||||
|
|
||||||
|
The POSIX standard provides <B>no</B> mechanism by which a thread A can
|
||||||
|
suspend the execution of another thread B, without cooperation from B.
|
||||||
|
The only way to implement a suspend/restart mechanism is to have B
|
||||||
|
check periodically some global variable for a suspend request
|
||||||
|
and then suspend itself on a condition variable, which another thread
|
||||||
|
can signal later to restart B.<P>
|
||||||
|
|
||||||
|
Notice that <CODE>thr_suspend()</CODE> is inherently dangerous and
|
||||||
|
prone to race conditions. For one thing, there is no control on where
|
||||||
|
the target thread stops: it can very well be stopped in the middle of
|
||||||
|
a critical section, while holding mutexes. Also, there is no
|
||||||
|
guarantee on when the target thread will actually stop. For these
|
||||||
|
reasons, you'd be much better off using mutexes and conditions
|
||||||
|
instead. The only situations that really require the ability to
|
||||||
|
suspend a thread are debuggers and some kind of garbage collectors.<P>
|
||||||
|
|
||||||
|
If you really must suspend a thread in LinuxThreads, you can send it a
|
||||||
|
<CODE>SIGSTOP</CODE> signal with <CODE>pthread_kill</CODE>. Send
|
||||||
|
<CODE>SIGCONT</CODE> for restarting it.
|
||||||
|
Beware, this is specific to LinuxThreads and entirely non-portable.
|
||||||
|
Indeed, a truly conforming POSIX threads implementation will stop all
|
||||||
|
threads when one thread receives the <CODE>SIGSTOP</CODE> signal!
|
||||||
|
One day, LinuxThreads will implement that behavior, and the
|
||||||
|
non-portable hack with <CODE>SIGSTOP</CODE> won't work anymore.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="E.5">E.5: LinuxThreads does not implement
|
||||||
|
<CODE>pthread_attr_setstacksize()</CODE> nor
|
||||||
|
<CODE>pthread_attr_setstackaddr()</CODE>. Why? </A></H4>
|
||||||
|
|
||||||
|
These two functions are part of optional components of the POSIX
|
||||||
|
standard, meaning that portable applications should test for the
|
||||||
|
"feature test" macros <CODE>_POSIX_THREAD_ATTR_STACKSIZE</CODE> and
|
||||||
|
<CODE>_POSIX_THREAD_ATTR_STACKADDR</CODE> (respectively) before using these
|
||||||
|
functions.<P>
|
||||||
|
|
||||||
|
<CODE>pthread_attr_setstacksize()</CODE> lets the programmer specify
|
||||||
|
the maximum stack size for a thread. In LinuxThreads, stacks start
|
||||||
|
small (4k) and grow on demand to a fairly large limit (2M), which
|
||||||
|
cannot be modified on a per-thread basis for architectural reasons.
|
||||||
|
Hence there is really no need to specify any stack size yourself: the
|
||||||
|
system does the right thing all by itself. Besides, there is no
|
||||||
|
portable way to estimate the stack requirements of a thread, so
|
||||||
|
setting the stack size is pretty useless anyway.<P>
|
||||||
|
|
||||||
|
<CODE>pthread_attr_setstackaddr()</CODE> is even more questionable: it
|
||||||
|
lets users specify the stack location for a thread. Again,
|
||||||
|
LinuxThreads takes care of that for you. Why you would ever need to
|
||||||
|
set the stack address escapes me.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="E.6">E.6: LinuxThreads does not support the
|
||||||
|
<CODE>PTHREAD_SCOPE_PROCESS</CODE> value of the "contentionscope"
|
||||||
|
attribute. Why? </A></H4>
|
||||||
|
|
||||||
|
With a "one-to-one" model, as in LinuxThreads (one kernel execution
|
||||||
|
context per thread), there is only one scheduler for all processes and
|
||||||
|
all threads on the system. So, there is no way to obtain the behavior of
|
||||||
|
<CODE>PTHREAD_SCOPE_PROCESS</CODE>.
|
||||||
|
|
||||||
|
<H4><A NAME="E.7">E.7: LinuxThreads does not implement process-shared
|
||||||
|
mutexes, conditions, and semaphores. Why?</A></H4>
|
||||||
|
|
||||||
|
This is another optional component of the POSIX standard. Portable
|
||||||
|
applications should test <CODE>_POSIX_THREAD_PROCESS_SHARED</CODE>
|
||||||
|
before using this facility.
|
||||||
|
<P>
|
||||||
|
The goal of this extension is to allow different processes (with
|
||||||
|
different address spaces) to synchronize through mutexes, conditions
|
||||||
|
or semaphores allocated in shared memory (either SVR4 shared memory
|
||||||
|
segments or <CODE>mmap()</CODE>ed files).
|
||||||
|
<P>
|
||||||
|
The reason why this does not work in LinuxThreads is that mutexes,
|
||||||
|
conditions, and semaphores are not self-contained: their waiting
|
||||||
|
queues contain pointers to linked lists of thread descriptors, and
|
||||||
|
these pointers are meaningful only in one address space.
|
||||||
|
<P>
|
||||||
|
Matt Messier and I spent a significant amount of time trying to design a
|
||||||
|
suitable mechanism for sharing waiting queues between processes. We
|
||||||
|
came up with several solutions that combined two of the following
|
||||||
|
three desirable features, but none that combines all three:
|
||||||
|
<UL>
|
||||||
|
<LI>allow sharing between processes having different UIDs
|
||||||
|
<LI>supports cancellation
|
||||||
|
<LI>supports <CODE>pthread_cond_timedwait</CODE>
|
||||||
|
</UL>
|
||||||
|
We concluded that kernel support is required to share mutexes,
|
||||||
|
conditions and semaphores between processes. That's one place where
|
||||||
|
Linus Torvalds's intuition that "all we need in the kernel is
|
||||||
|
<CODE>clone()</CODE>" fails.
|
||||||
|
<P>
|
||||||
|
Until suitable kernel support is available, you'd better use
|
||||||
|
traditional interprocess communications to synchronize different
|
||||||
|
processes: System V semaphores and message queues, or pipes, or sockets.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="F">F. C++ issues</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="F.1">F.1: Are there C++ wrappers for LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
Douglas Schmidt's ACE library contains, among a lot of other
|
||||||
|
things, C++ wrappers for LinuxThreads and quite a number of other
|
||||||
|
thread libraries. Check out
|
||||||
|
<A HREF="http://www.cs.wustl.edu/~schmidt/ACE.html">http://www.cs.wustl.edu/~schmidt/ACE.html</A><P>
|
||||||
|
|
||||||
|
<H4><A NAME="F.2">F.2: I'm trying to use LinuxThreads from a C++
|
||||||
|
program, and the compiler complains about the third argument to
|
||||||
|
<CODE>pthread_create()</CODE> !</A></H4>
|
||||||
|
|
||||||
|
You're probably trying to pass a class member function or some
|
||||||
|
other C++ thing as third argument to <CODE>pthread_create()</CODE>.
|
||||||
|
Recall that <CODE>pthread_create()</CODE> is a C function, and it must
|
||||||
|
be passed a C function as third argument.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="F.3">F.3: I'm trying to use LinuxThreads in conjunction
|
||||||
|
with libg++, and I'm having all sorts of trouble.</A></H4>
|
||||||
|
|
||||||
|
From what I understand, thread support in libg++ is completely broken,
|
||||||
|
especially with respect to locking of iostreams. H.J.Lu wrote:
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
If you want to use thread, I can only suggest egcs and glibc. You
|
||||||
|
can find egcs at
|
||||||
|
<A HREF="http://www.cygnus.com/egcs">http://www.cygnus.com/egcs</A>.
|
||||||
|
egcs has libsdtc++, which is MT safe under glibc 2. If you really
|
||||||
|
want to use the libg++, I have a libg++ add-on for egcs.
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="G">G. Debugging LinuxThreads programs</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="G.1">G.1: Can I debug LinuxThreads program using gdb?</A></H4>
|
||||||
|
|
||||||
|
Essentially, no. gdb is basically not aware of the threads. It
|
||||||
|
will let you debug the main thread, and also inspect the global state,
|
||||||
|
but you won't have any control over the other threads. Worse, you
|
||||||
|
can't put any breakpoint anywhere in the code: if a thread other than
|
||||||
|
the main thread hits the breakpoint, it will just crash!<P>
|
||||||
|
|
||||||
|
For running gdb on the main thread, you need to instruct gdb to ignore
|
||||||
|
the signals used by LinuxThreads. Just do:
|
||||||
|
<PRE>
|
||||||
|
handle SIGUSR1 nostop pass noprint
|
||||||
|
handle SIGUSR2 nostop pass noprint
|
||||||
|
|
||||||
|
</PRE>
|
||||||
|
|
||||||
|
<H4><A NAME="G.2">G.2: What about attaching to a running thread using
|
||||||
|
the <code>attach</code> command of gdb?</A></H4>
|
||||||
|
|
||||||
|
For reasons I don't fully understand, this does not work.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="G.3">G.3: But I know gdb supports threads on some
|
||||||
|
platforms! Why not on Linux?</A></H4>
|
||||||
|
|
||||||
|
You're correct that gdb has some built-in support for threads, in
|
||||||
|
particular the IRIX "sprocs" model, which is a "one thread = one
|
||||||
|
process" model fairly close to LinuxThreads. But gdb under IRIX uses
|
||||||
|
ioctls on <code>/proc</code> to control debugged processes, while
|
||||||
|
under Linux it uses the traditional <CODE>ptrace()</CODE>. The support
|
||||||
|
for threads is built in the <code>/proc</code> interface, but some
|
||||||
|
work remains to be done to have it in the <CODE>ptrace()</CODE>
|
||||||
|
interface. In summary, it should not be impossible to get gdb to work
|
||||||
|
with LinuxThreads, but it's definitely not trivial.
|
||||||
|
|
||||||
|
<H4><A NAME="G.4">G.4: OK, I'll do post-mortem debugging, then. But
|
||||||
|
gdb cannot read core files generated by a multithreaded program! Or,
|
||||||
|
the core file is readable from gcc, but does not correspond to the
|
||||||
|
thread that crashed! What happens?</A></H4>
|
||||||
|
|
||||||
|
Some versions of gdb do indeed have problems with post-mortem
|
||||||
|
debugging in general, but this is not specific to LinuxThreads.
|
||||||
|
Recent Linux distributions seem to have corrected this problem,
|
||||||
|
though.<P>
|
||||||
|
|
||||||
|
Regarding the fact that the core file does not correspond to the
|
||||||
|
thread that crashed, the reason is that the kernel will not dump core
|
||||||
|
for a process that shares its memory with other processes, such as the
|
||||||
|
other threads of your program. So, the thread that crashes silently
|
||||||
|
disappears without generating a core file. Then, all other threads of
|
||||||
|
your program die on the same signal that killed the crashing thread.
|
||||||
|
(This is required behavior according to the POSIX standard.) The last
|
||||||
|
one that dies is no longer sharing its memory with anyone else, so the
|
||||||
|
kernel generates a core file for that thread. Unfortunately, that's
|
||||||
|
not the thread you are interested in.
|
||||||
|
|
||||||
|
<H4><A NAME="G.5">G.5: How can I debug multithreaded programs, then?</A></H4>
|
||||||
|
|
||||||
|
Assertions and <CODE>printf()</CODE> are your best friends. Try to debug
|
||||||
|
sequential parts in a single-threaded program first. Then, put
|
||||||
|
<CODE>printf()</CODE> statements all over the place to get execution traces.
|
||||||
|
Also, check invariants often with the <CODE>assert()</CODE> macro. In truth,
|
||||||
|
there is no other effective way (save for a full formal proof of your
|
||||||
|
program) to track down concurrency bugs. Debuggers are not really
|
||||||
|
effective for concurrency problems, because they disrupt program
|
||||||
|
execution too much.<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="H">H. Compiling multithreaded code; errno madness</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="H.1">H.1: You say all multithreaded code must be compiled
|
||||||
|
with <CODE>_REENTRANT</CODE> defined. What difference does it make?</A></H4>
|
||||||
|
|
||||||
|
It affects include files in three ways:
|
||||||
|
<UL>
|
||||||
|
<LI> The include files define prototypes for the reentrant variants of
|
||||||
|
some of the standard library functions,
|
||||||
|
e.g. <CODE>gethostbyname_r()</CODE> as a reentrant equivalent to
|
||||||
|
<CODE>gethostbyname()</CODE>.<P>
|
||||||
|
|
||||||
|
<LI> If <CODE>_REENTRANT</CODE> is defined, some
|
||||||
|
<code><stdio.h></code> functions are no longer defined as macros,
|
||||||
|
e.g. <CODE>getc()</CODE> and <CODE>putc()</CODE>. In a multithreaded
|
||||||
|
program, stdio functions require additional locking, which the macros
|
||||||
|
don't perform, so we must call functions instead.<P>
|
||||||
|
|
||||||
|
<LI> More importantly, <code><errno.h></code> redefines errno when
|
||||||
|
<CODE>_REENTRANT</CODE> is
|
||||||
|
defined, so that errno refers to the thread-specific errno location
|
||||||
|
rather than the global errno variable. This is achieved by the
|
||||||
|
following <code>#define</code> in <code><errno.h></code>:
|
||||||
|
<PRE>
|
||||||
|
#define errno (*(__errno_location()))
|
||||||
|
</PRE>
|
||||||
|
which causes each reference to errno to call the
|
||||||
|
<CODE>__errno_location()</CODE> function for obtaining the location
|
||||||
|
where error codes are stored. libc provides a default definition of
|
||||||
|
<CODE>__errno_location()</CODE> that always returns
|
||||||
|
<code>&errno</code> (the address of the global errno variable). Thus,
|
||||||
|
for programs not linked with LinuxThreads, defining
|
||||||
|
<CODE>_REENTRANT</CODE> makes no difference w.r.t. errno processing.
|
||||||
|
But LinuxThreads redefines <CODE>__errno_location()</CODE> to return a
|
||||||
|
location in the thread descriptor reserved for holding the current
|
||||||
|
value of errno for the calling thread. Thus, each thread operates on
|
||||||
|
a different errno location.
|
||||||
|
</UL>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H4><A NAME="H.2">H.2: Why is it so important that each thread has its
|
||||||
|
own errno variable? </A></H4>
|
||||||
|
|
||||||
|
If all threads were to store error codes in the same, global errno
|
||||||
|
variable, then the value of errno after a system call or library
|
||||||
|
function returns would be unpredictable: between the time a system
|
||||||
|
call stores its error code in the global errno and your code inspects
|
||||||
|
errno to see which error occurred, another thread might have stored
|
||||||
|
another error code in the same errno location. <P>
|
||||||
|
|
||||||
|
<H4><A NAME="H.3">H.3: What happens if I link LinuxThreads with code
|
||||||
|
not compiled with <CODE>-D_REENTRANT</CODE>?</A></H4>
|
||||||
|
|
||||||
|
Lots of trouble. If the code uses <CODE>getc()</CODE> or
|
||||||
|
<CODE>putc()</CODE>, it will perform I/O without proper interlocking
|
||||||
|
of the stdio buffers; this can cause lost output, duplicate output, or
|
||||||
|
just crash other stdio functions. If the code consults errno, it will
|
||||||
|
get back the wrong error code. The following code fragment is a
|
||||||
|
typical example:
|
||||||
|
<PRE>
|
||||||
|
do {
|
||||||
|
r = read(fd, buf, n);
|
||||||
|
if (r == -1) {
|
||||||
|
if (errno == EINTR) /* an error we can handle */
|
||||||
|
continue;
|
||||||
|
else { /* other errors are fatal */
|
||||||
|
perror("read failed");
|
||||||
|
exit(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (...);
|
||||||
|
</PRE>
|
||||||
|
Assume this code is not compiled with <CODE>-D_REENTRANT</CODE>, and
|
||||||
|
linked with LinuxThreads. At run-time, <CODE>read()</CODE> is
|
||||||
|
interrupted. Since the C library was compiled with
|
||||||
|
<CODE>-D_REENTRANT</CODE>, <CODE>read()</CODE> stores its error code
|
||||||
|
in the location pointed to by <CODE>__errno_location()</CODE>, which
|
||||||
|
is the thread-local errno variable. Then, the code above sees that
|
||||||
|
<CODE>read()</CODE> returns -1 and looks up errno. Since
|
||||||
|
<CODE>_REENTRANT</CODE> is not defined, the reference to errno
|
||||||
|
accesses the global errno variable, which is most likely 0. Hence the
|
||||||
|
code concludes that it cannot handle the error and stops.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="H.4">H.4: With LinuxThreads, I can no longer use the signals
|
||||||
|
<code>SIGUSR1</code> and <code>SIGUSR2</code> in my programs! Why? </A></H4>
|
||||||
|
|
||||||
|
LinuxThreads needs two signals for its internal operation.
|
||||||
|
One is used to suspend and restart threads blocked on mutex, condition
|
||||||
|
or semaphore operations. The other is used for thread cancellation.
|
||||||
|
Since the only two signals not reserved for the Linux kernel are
|
||||||
|
<code>SIGUSR1</code> and <code>SIGUSR2</code>, LinuxThreads has no
|
||||||
|
other choice than using them. I know this is unfortunate, and hope
|
||||||
|
this problem will be addressed in future Linux kernels, either by
|
||||||
|
freeing some of the regular signals (unlikely), or by providing more
|
||||||
|
than 32 signals (as per the POSIX 1003.1b realtime extensions).<P>
|
||||||
|
|
||||||
|
In the meantime, you can try to use kernel-reserved signals either in
|
||||||
|
your program or in LinuxThreads. For instance,
|
||||||
|
<code>SIGSTKFLT</code> and <code>SIGUNUSED</code> appear to be
|
||||||
|
unused in the current Linux kernels for the Intel x86 architecture.
|
||||||
|
To use these in LinuxThreads, the only file you need to change
|
||||||
|
is <code>internals.h</code>, more specifically the two lines:
|
||||||
|
<PRE>
|
||||||
|
#define PTHREAD_SIG_RESTART SIGUSR1
|
||||||
|
#define PTHREAD_SIG_CANCEL SIGUSR2
|
||||||
|
</PRE>
|
||||||
|
Replace them by e.g.
|
||||||
|
<PRE>
|
||||||
|
#define PTHREAD_SIG_RESTART SIGSTKFLT
|
||||||
|
#define PTHREAD_SIG_CANCEL SIGUNUSED
|
||||||
|
</PRE>
|
||||||
|
Warning: you're doing this at your own risks.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="H.5">H.5: Is the stack of one thread visible from the
|
||||||
|
other threads? Can I pass a pointer into my stack to other threads?
|
||||||
|
</A></H4>
|
||||||
|
|
||||||
|
Yes, you can -- if you're very careful. The stacks are indeed visible
|
||||||
|
from all threads in the system. Some non-POSIX thread libraries seem
|
||||||
|
to map the stacks for all threads at the same virtual addresses and
|
||||||
|
change the memory mapping when they switch from one thread to
|
||||||
|
another. But this is not the case for LinuxThreads, as it would make
|
||||||
|
context switching between threads more expensive, and at any rate
|
||||||
|
might not conform to the POSIX standard.<P>
|
||||||
|
|
||||||
|
So, you can take the address of an "auto" variable and pass it to
|
||||||
|
other threads via shared data structures. However, you need to make
|
||||||
|
absolutely sure that the function doing this will not return as long
|
||||||
|
as other threads need to access this address. It's the usual mistake
|
||||||
|
of returning the address of an "auto" variable, only made much worse
|
||||||
|
because of concurrency. It's much, much safer to systematically
|
||||||
|
heap-allocate all shared data structures. <P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="I">I. X-Windows and other libraries</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="I.1">I.1: My program uses both Xlib and LinuxThreads.
|
||||||
|
It stops very early with an "Xlib: unknown 0 error" message. What
|
||||||
|
does this mean? </A></H4>
|
||||||
|
|
||||||
|
That's a prime example of the errno problem described in question <A
|
||||||
|
HREF="#H.2">H.2</A>. The binaries for Xlib you're using have not been
|
||||||
|
compiled with <CODE>-D_REENTRANT</CODE>. It happens Xlib contains a
|
||||||
|
piece of code very much like the one in question <A
|
||||||
|
HREF="#H.2">H.2</A>. So, your Xlib fetches the error code from the
|
||||||
|
wrong errno location and concludes that an error it cannot handle
|
||||||
|
occurred.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="I.2">I.2: So, what can I do to build a multithreaded X
|
||||||
|
Windows client? </A></H4>
|
||||||
|
|
||||||
|
The best solution is to recompile the X libraries with multithreading
|
||||||
|
options set. They contain optional support for multithreading; it's
|
||||||
|
just that all binary distributions for Linux were built without this
|
||||||
|
support. See the file <code>README.Xfree3.3</code> in the LinuxThreads
|
||||||
|
distribution for patches and info on how to compile thread-safe X
|
||||||
|
libraries from the Xfree3.3 distribution. The Xfree3.3 sources are
|
||||||
|
readily available in most Linux distributions, e.g. as a source RPM
|
||||||
|
for RedHat. Be warned, however, that X Windows is a huge system, and
|
||||||
|
recompiling even just the libraries takes a lot of time and disk
|
||||||
|
space.<P>
|
||||||
|
|
||||||
|
Another, less involving solution is to call X functions only from the
|
||||||
|
main thread of your program. Even if all threads have their own errno
|
||||||
|
location, the main thread uses the global errno variable for its errno
|
||||||
|
location. Thus, code not compiled with <code>-D_REENTRANT</code>
|
||||||
|
still "sees" the right error values if it executes in the main thread
|
||||||
|
only. <P>
|
||||||
|
|
||||||
|
<H4><A NAME="I.2">This is a lot of work. Don't you have precompiled
|
||||||
|
thread-safe X libraries that you could distribute?</A></H4>
|
||||||
|
|
||||||
|
No, I don't. Sorry. But you could approach the maintainers of
|
||||||
|
your Linux distribution to see if they would be willing to provide
|
||||||
|
thread-safe X libraries.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="I.3">I.3: Can I use library FOO in a multithreaded
|
||||||
|
program?</A></H4>
|
||||||
|
|
||||||
|
Most libraries cannot be used "as is" in a multithreaded program.
|
||||||
|
For one thing, they are not necessarily thread-safe: calling
|
||||||
|
simultaneously two functions of the library from two threads might not
|
||||||
|
work, due to internal use of global variables and the like. Second,
|
||||||
|
the libraries must have been compiled with <CODE>-D_REENTRANT</CODE> to avoid
|
||||||
|
the errno problems explained in question <A HREF="#H.2">H.2</A>.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H4><A NAME="I.4">I.4: What if I make sure that only one thread calls
|
||||||
|
functions in these libraries?</A></H4>
|
||||||
|
|
||||||
|
This avoids problems with the library not being thread-safe. But
|
||||||
|
you're still vulnerable to errno problems. At the very least, a
|
||||||
|
recompile of the library with <CODE>-D_REENTRANT</CODE> is needed.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H4><A NAME="I.5">I.5: What if I make sure that only the main thread
|
||||||
|
calls functions in these libraries?</A></H4>
|
||||||
|
|
||||||
|
That might actually work. As explained in question <A HREF="#I.1">I.1</A>,
|
||||||
|
the main thread uses the global errno variable, and can therefore
|
||||||
|
execute code not compiled with <CODE>-D_REENTRANT</CODE>.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="I.6">I.6: SVGAlib doesn't work with LinuxThreads. Why?
|
||||||
|
</A></H4>
|
||||||
|
|
||||||
|
Because both LinuxThreads and SVGAlib use the signals
|
||||||
|
<code>SIGUSR1</code> and <code>SIGUSR2</code>. One of the two should
|
||||||
|
be recompiled to use different signals. See question <A
|
||||||
|
HREF="#H.4">H.4</A>.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="J">J. Signals and threads</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="J.1">J.1: When it comes to signals, what is shared
|
||||||
|
between threads and what isn't?</A></H4>
|
||||||
|
|
||||||
|
Signal handlers are shared between all threads: when a thread calls
|
||||||
|
<CODE>sigaction()</CODE>, it sets how the signal is handled not only
|
||||||
|
for itself, but for all other threads in the program as well.<P>
|
||||||
|
|
||||||
|
On the other hand, signal masks are per-thread: each thread chooses
|
||||||
|
which signals it blocks independently of others. At thread creation
|
||||||
|
time, the newly created thread inherits the signal mask of the thread
|
||||||
|
calling <CODE>pthread_create()</CODE>. But afterwards, the new thread
|
||||||
|
can modify its signal mask independently of its creator thread.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="J.2">J.2: When I send a <CODE>SIGKILL</CODE> to a
|
||||||
|
particular thread using <CODE>pthread_kill</CODE>, all my threads are
|
||||||
|
killed!</A></H4>
|
||||||
|
|
||||||
|
That's how it should be. The POSIX standard mandates that all threads
|
||||||
|
should terminate when the process (i.e. the collection of all threads
|
||||||
|
running the program) receives a signal whose effect is to
|
||||||
|
terminate the process (such as <CODE>SIGKILL</CODE> or <CODE>SIGINT</CODE>
|
||||||
|
when no handler is installed on that signal). This behavior makes a
|
||||||
|
lot of sense: when you type "ctrl-C" at the keyboard, or when a thread
|
||||||
|
crashes on a division by zero or a segmentation fault, you really want
|
||||||
|
all threads to stop immediately, not just the one that caused the
|
||||||
|
segmentation violation or that got the <CODE>SIGINT</CODE> signal.
|
||||||
|
(This assumes default behavior for those signals; see question
|
||||||
|
<A HREF="#J.3">J.3</A> if you install handlers for those signals.)<P>
|
||||||
|
|
||||||
|
If you're trying to terminate a thread without bringing the whole
|
||||||
|
process down, use <code>pthread_cancel()</code>.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="J.3">J.3: I've installed a handler on a signal. Which
|
||||||
|
thread executes the handler when the signal is received?</A></H4>
|
||||||
|
|
||||||
|
If the signal is generated by a thread during its execution (e.g. a
|
||||||
|
thread executes a division by zero and thus generates a
|
||||||
|
<CODE>SIGFPE</CODE> signal), then the handler is executed by that
|
||||||
|
thread. This also applies to signals generated by
|
||||||
|
<CODE>raise()</CODE>.<P>
|
||||||
|
|
||||||
|
If the signal is sent to a particular thread using
|
||||||
|
<CODE>pthread_kill()</CODE>, then that thread executes the handler.<P>
|
||||||
|
|
||||||
|
If the signal is sent via <CODE>kill()</CODE> or the tty interface
|
||||||
|
(e.g. by pressing ctrl-C), then the POSIX specs say that the handler
|
||||||
|
is executed by any thread in the process that does not currently block
|
||||||
|
the signal. In other terms, POSIX considers that the signal is sent
|
||||||
|
to the process (the collection of all threads) as a whole, and any
|
||||||
|
thread that is not blocking this signal can then handle it.<P>
|
||||||
|
|
||||||
|
The latter case is where LinuxThreads departs from the POSIX specs.
|
||||||
|
In LinuxThreads, there is no real notion of ``the process as a whole'':
|
||||||
|
in the kernel, each thread is really a distinct process with a
|
||||||
|
distinct PID, and signals sent to the PID of a thread can only be
|
||||||
|
handled by that thread. As long as no thread is blocking the signal,
|
||||||
|
the behavior conforms to the standard: one (unspecified) thread of the
|
||||||
|
program handles the signal. But if the thread to which PID the signal
|
||||||
|
is sent blocks the signal, and some other thread does not block the
|
||||||
|
signal, then LinuxThreads will simply queue in
|
||||||
|
that thread and execute the handler only when that thread unblocks
|
||||||
|
the signal, instead of executing the handler immediately in the other
|
||||||
|
thread that does not block the signal.<P>
|
||||||
|
|
||||||
|
This is to be viewed as a LinuxThreads bug, but I currently don't see
|
||||||
|
any way to implement the POSIX behavior without kernel support.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="J.3">J.3: How shall I go about mixing signals and threads
|
||||||
|
in my program? </A></H4>
|
||||||
|
|
||||||
|
The less you mix them, the better. Notice that all
|
||||||
|
<CODE>pthread_*</CODE> functions are not async-signal safe, meaning
|
||||||
|
that you should not call them from signal handlers. This
|
||||||
|
recommendation is not to be taken lightly: your program can deadlock
|
||||||
|
if you call a <CODE>pthread_*</CODE> function from a signal handler!
|
||||||
|
<P>
|
||||||
|
|
||||||
|
The only sensible things you can do from a signal handler is set a
|
||||||
|
global flag, or call <CODE>sem_post</CODE> on a semaphore, to record
|
||||||
|
the delivery of the signal. The remainder of the program can then
|
||||||
|
either poll the global flag, or use <CODE>sem_wait()</CODE> and
|
||||||
|
<CODE>sem_trywait()</CODE> on the semaphore.<P>
|
||||||
|
|
||||||
|
Another option is to do nothing in the signal handler, and dedicate
|
||||||
|
one thread (preferably the initial thread) to wait synchronously for
|
||||||
|
signals, using <CODE>sigwait()</CODE>, and send messages to the other
|
||||||
|
threads accordingly.
|
||||||
|
|
||||||
|
<H4><A NAME="J.4">J.4: When one thread is blocked in
|
||||||
|
<CODE>sigwait()</CODE>, other threads no longer receive the signals
|
||||||
|
<CODE>sigwait()</CODE> is waiting for! What happens? </A></H4>
|
||||||
|
|
||||||
|
It's an unfortunate consequence of how LinuxThreads implements
|
||||||
|
<CODE>sigwait()</CODE>. Basically, it installs signal handlers on all
|
||||||
|
signals waited for, in order to record which signal was received.
|
||||||
|
Since signal handlers are shared with the other threads, this
|
||||||
|
temporarily deactivates any signal handlers you might have previously
|
||||||
|
installed on these signals.<P>
|
||||||
|
|
||||||
|
Though surprising, this behavior actually seems to conform to the
|
||||||
|
POSIX standard. According to POSIX, <CODE>sigwait()</CODE> is
|
||||||
|
guaranteed to work as expected only if all other threads in the
|
||||||
|
program block the signals waited for (otherwise, the signals could be
|
||||||
|
delivered to other threads than the one doing <CODE>sigwait()</CODE>,
|
||||||
|
which would make <CODE>sigwait()</CODE> useless). In this particular
|
||||||
|
case, the problem described in this question does not appear.<P>
|
||||||
|
|
||||||
|
One day, <CODE>sigwait()</CODE> will be implemented in the kernel,
|
||||||
|
along with others POSIX 1003.1b extensions, and <CODE>sigwait()</CODE>
|
||||||
|
will have a more natural behavior (as well as better performances).<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<P>
|
||||||
|
|
||||||
|
<H2><A NAME="K">K. Internals of LinuxThreads</A></H2>
|
||||||
|
|
||||||
|
<H4><A NAME="K.1">K.1: What is the implementation model for
|
||||||
|
LinuxThreads?</A></H4>
|
||||||
|
|
||||||
|
LinuxThreads follows the so-called "one-to-one" model: each thread is
|
||||||
|
actually a separate process in the kernel. The kernel scheduler takes
|
||||||
|
care of scheduling the threads, just like it schedules regular
|
||||||
|
processes. The threads are created with the Linux
|
||||||
|
<code>clone()</code> system call, which is a generalization of
|
||||||
|
<code>fork()</code> allowing the new process to share the memory
|
||||||
|
space, file descriptors, and signal handlers of the parent.<P>
|
||||||
|
|
||||||
|
Advantages of the "one-to-one" model include:
|
||||||
|
<UL>
|
||||||
|
<LI> minimal overhead on CPU-intensive multiprocessing (with
|
||||||
|
about one thread per processor);
|
||||||
|
<LI> minimal overhead on I/O operations;
|
||||||
|
<LI> a simple and robust implementation (the kernel scheduler does
|
||||||
|
most of the hard work for us).
|
||||||
|
</UL>
|
||||||
|
The main disadvantage is more expensive context switches on mutex and
|
||||||
|
condition operations, which must go through the kernel. This is
|
||||||
|
mitigated by the fact that context switches in the Linux kernel are
|
||||||
|
pretty efficient.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="K.2">K.2: Have you considered other implementation
|
||||||
|
models?</A></H4>
|
||||||
|
|
||||||
|
There are basically two other models. The "many-to-one" model
|
||||||
|
relies on a user-level scheduler that context-switches between the
|
||||||
|
threads entirely in user code; viewed from the kernel, there is only
|
||||||
|
one process running. This model is completely out of the question for
|
||||||
|
me, since it does not take advantage of multiprocessors, and require
|
||||||
|
unholy magic to handle blocking I/O operations properly. There are
|
||||||
|
several user-level thread libraries available for Linux, but I found
|
||||||
|
all of them deficient in functionality, performance, and/or robustness.
|
||||||
|
<P>
|
||||||
|
|
||||||
|
The "many-to-many" model combines both kernel-level and user-level
|
||||||
|
scheduling: several kernel-level threads run concurrently, each
|
||||||
|
executing a user-level scheduler that selects between user threads.
|
||||||
|
Most commercial Unix systems (Solaris, Digital Unix, IRIX) implement
|
||||||
|
POSIX threads this way. This model combines the advantages of both
|
||||||
|
the "many-to-one" and the "one-to-one" model, and is attractive
|
||||||
|
because it avoids the worst-case behaviors of both models --
|
||||||
|
especially on kernels where context switches are expensive, such as
|
||||||
|
Digital Unix. Unfortunately, it is pretty complex to implement, and
|
||||||
|
requires kernel support which Linux does not provide. Linus Torvalds
|
||||||
|
and other Linux kernel developers have always been pushing the
|
||||||
|
"one-to-one" model in the name of overall simplicity, and are doing a
|
||||||
|
pretty good job of making kernel-level context switches between
|
||||||
|
threads efficient. LinuxThreads is just following the general
|
||||||
|
direction they set.<P>
|
||||||
|
|
||||||
|
<H4><A NAME="K.3">K.3: I looked at the LinuxThreads sources, and I saw
|
||||||
|
quite a lot of spinlocks and busy-waiting loops to acquire these
|
||||||
|
spinlocks. Isn't this a big waste of CPU time?</A></H4>
|
||||||
|
|
||||||
|
Look more carefully. Spinlocks are used internally to protect
|
||||||
|
LinuxThreads's data structures, but these locks are held for very
|
||||||
|
short periods of time: 10 instructions or so. The probability that a
|
||||||
|
thread has to loop busy-waiting on a taken spinlock for more than,
|
||||||
|
say, 100 cycles is very, very low. When a thread needs to wait on a
|
||||||
|
mutex, condition, or semaphore, it actually puts itself on a waiting
|
||||||
|
queue, then suspends on a signal, consuming no CPU time at all. The
|
||||||
|
thread will later be restarted by sending it a signal when the state
|
||||||
|
of the mutex, condition, or semaphore changes.<P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
<ADDRESS>Xavier.Leroy@inria.fr</ADDRESS>
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
501
linuxthreads/LICENSE
Normal file
501
linuxthreads/LICENSE
Normal file
@ -0,0 +1,501 @@
|
|||||||
|
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||||
|
**********************************
|
||||||
|
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place -- Suite 330, Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the library GPL. It is
|
||||||
|
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
========
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Library General Public License, applies to some
|
||||||
|
specially designated Free Software Foundation software, and to any
|
||||||
|
other libraries whose authors decide to use it. You can use it for
|
||||||
|
your libraries, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it in
|
||||||
|
new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the library, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link a program with the library, you must provide
|
||||||
|
complete object files to the recipients so that they can relink them
|
||||||
|
with the library, after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
Our method of protecting your rights has two steps: (1) copyright
|
||||||
|
the library, and (2) offer you this license which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
Also, for each distributor's protection, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
library. If the library is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original
|
||||||
|
version, so that any problems introduced by others will not reflect on
|
||||||
|
the original authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that companies distributing free
|
||||||
|
software will individually obtain patent licenses, thus in effect
|
||||||
|
transforming the program into proprietary software. To prevent this,
|
||||||
|
we have made it clear that any patent must be licensed for everyone's
|
||||||
|
free use or not licensed at all.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License, which was designed for utility
|
||||||
|
programs. This license, the GNU Library General Public License,
|
||||||
|
applies to certain designated libraries. This license is quite
|
||||||
|
different from the ordinary one; be sure to read it in full, and don't
|
||||||
|
assume that anything in it is the same as in the ordinary license.
|
||||||
|
|
||||||
|
The reason we have a separate public license for some libraries is
|
||||||
|
that they blur the distinction we usually make between modifying or
|
||||||
|
adding to a program and simply using it. Linking a program with a
|
||||||
|
library, without changing the library, is in some sense simply using
|
||||||
|
the library, and is analogous to running a utility program or
|
||||||
|
application program. However, in a textual and legal sense, the linked
|
||||||
|
executable is a combined work, a derivative of the original library,
|
||||||
|
and the ordinary General Public License treats it as such.
|
||||||
|
|
||||||
|
Because of this blurred distinction, using the ordinary General
|
||||||
|
Public License for libraries did not effectively promote software
|
||||||
|
sharing, because most developers did not use the libraries. We
|
||||||
|
concluded that weaker conditions might promote sharing better.
|
||||||
|
|
||||||
|
However, unrestricted linking of non-free programs would deprive the
|
||||||
|
users of those programs of all benefit from the free status of the
|
||||||
|
libraries themselves. This Library General Public License is intended
|
||||||
|
to permit developers of non-free programs to use free libraries, while
|
||||||
|
preserving your freedom as a user of such programs to change the free
|
||||||
|
libraries that are incorporated in them. (We have not seen how to
|
||||||
|
achieve this as regards changes in header files, but we have achieved
|
||||||
|
it as regards changes in the actual functions of the Library.) The
|
||||||
|
hope is that this will lead to faster development of free libraries.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, while the latter only
|
||||||
|
works together with the library.
|
||||||
|
|
||||||
|
Note that it is possible for a library to be covered by the ordinary
|
||||||
|
General Public License rather than by this special one.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library which
|
||||||
|
contains a notice placed by the copyright holder or other
|
||||||
|
authorized party saying it may be distributed under the terms of
|
||||||
|
this Library General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or
|
||||||
|
translated straightforwardly into another language. (Hereinafter,
|
||||||
|
translation is included without limitation in the term
|
||||||
|
"modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code
|
||||||
|
means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are
|
||||||
|
not covered by this License; they are outside its scope. The act
|
||||||
|
of running a program using the Library is not restricted, and
|
||||||
|
output from such a program is covered only if its contents
|
||||||
|
constitute a work based on the Library (independent of the use of
|
||||||
|
the Library in a tool for writing it). Whether that is true
|
||||||
|
depends on what the Library does and what the program that uses
|
||||||
|
the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided
|
||||||
|
that you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep
|
||||||
|
intact all the notices that refer to this License and to the
|
||||||
|
absence of any warranty; and distribute a copy of this License
|
||||||
|
along with the Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange
|
||||||
|
for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a. The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b. You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c. You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d. If a facility in the modified Library refers to a function or
|
||||||
|
a table of data to be supplied by an application program that
|
||||||
|
uses the facility, other than as an argument passed when the
|
||||||
|
facility is invoked, then you must make a good faith effort
|
||||||
|
to ensure that, in the event an application does not supply
|
||||||
|
such function or table, the facility still operates, and
|
||||||
|
performs whatever part of its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots
|
||||||
|
has a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function
|
||||||
|
must be optional: if the application does not supply it, the
|
||||||
|
square root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the
|
||||||
|
Library, and can be reasonably considered independent and separate
|
||||||
|
works in themselves, then this License, and its terms, do not
|
||||||
|
apply to those sections when you distribute them as separate
|
||||||
|
works. But when you distribute the same sections as part of a
|
||||||
|
whole which is a work based on the Library, the distribution of
|
||||||
|
the whole must be on the terms of this License, whose permissions
|
||||||
|
for other licensees extend to the entire whole, and thus to each
|
||||||
|
and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or
|
||||||
|
contest your rights to work written entirely by you; rather, the
|
||||||
|
intent is to exercise the right to control the distribution of
|
||||||
|
derivative or collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the
|
||||||
|
Library with the Library (or with a work based on the Library) on
|
||||||
|
a volume of a storage or distribution medium does not bring the
|
||||||
|
other work under the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library.
|
||||||
|
To do this, you must alter all the notices that refer to this
|
||||||
|
License, so that they refer to the ordinary GNU General Public
|
||||||
|
License, version 2, instead of to this License. (If a newer
|
||||||
|
version than version 2 of the ordinary GNU General Public License
|
||||||
|
has appeared, then you can specify that version instead if you
|
||||||
|
wish.) Do not make any other change in these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to
|
||||||
|
all subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable
|
||||||
|
form under the terms of Sections 1 and 2 above provided that you
|
||||||
|
accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software
|
||||||
|
interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy
|
||||||
|
the source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being
|
||||||
|
compiled or linked with it, is called a "work that uses the
|
||||||
|
Library". Such a work, in isolation, is not a derivative work of
|
||||||
|
the Library, and therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because
|
||||||
|
it contains portions of the Library), rather than a "work that
|
||||||
|
uses the library". The executable is therefore covered by this
|
||||||
|
License. Section 6 states terms for distribution of such
|
||||||
|
executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header
|
||||||
|
file that is part of the Library, the object code for the work may
|
||||||
|
be a derivative work of the Library even though the source code is
|
||||||
|
not. Whether this is true is especially significant if the work
|
||||||
|
can be linked without the Library, or if the work is itself a
|
||||||
|
library. The threshold for this to be true is not precisely
|
||||||
|
defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a
|
||||||
|
derivative work. (Executables containing this object code plus
|
||||||
|
portions of the Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section
|
||||||
|
6. Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also compile or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered
|
||||||
|
by this License. You must supply a copy of this License. If the
|
||||||
|
work during execution displays copyright notices, you must include
|
||||||
|
the copyright notice for the Library among them, as well as a
|
||||||
|
reference directing the user to the copy of this License. Also,
|
||||||
|
you must do one of these things:
|
||||||
|
|
||||||
|
a. Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including
|
||||||
|
whatever changes were used in the work (which must be
|
||||||
|
distributed under Sections 1 and 2 above); and, if the work
|
||||||
|
is an executable linked with the Library, with the complete
|
||||||
|
machine-readable "work that uses the Library", as object code
|
||||||
|
and/or source code, so that the user can modify the Library
|
||||||
|
and then relink to produce a modified executable containing
|
||||||
|
the modified Library. (It is understood that the user who
|
||||||
|
changes the contents of definitions files in the Library will
|
||||||
|
not necessarily be able to recompile the application to use
|
||||||
|
the modified definitions.)
|
||||||
|
|
||||||
|
b. Accompany the work with a written offer, valid for at least
|
||||||
|
three years, to give the same user the materials specified in
|
||||||
|
Subsection 6a, above, for a charge no more than the cost of
|
||||||
|
performing this distribution.
|
||||||
|
|
||||||
|
c. If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the
|
||||||
|
above specified materials from the same place.
|
||||||
|
|
||||||
|
d. Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special
|
||||||
|
exception, the source code distributed need not include anything
|
||||||
|
that is normally distributed (in either source or binary form)
|
||||||
|
with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that
|
||||||
|
component itself accompanies the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you
|
||||||
|
cannot use both them and the Library together in an executable
|
||||||
|
that you distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other
|
||||||
|
library facilities not covered by this License, and distribute
|
||||||
|
such a combined library, provided that the separate distribution
|
||||||
|
of the work based on the Library and of the other library
|
||||||
|
facilities is otherwise permitted, and provided that you do these
|
||||||
|
two things:
|
||||||
|
|
||||||
|
a. Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b. Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same
|
||||||
|
work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute the
|
||||||
|
Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate
|
||||||
|
your rights under this License. However, parties who have
|
||||||
|
received copies, or rights, from you under this License will not
|
||||||
|
have their licenses terminated so long as such parties remain in
|
||||||
|
full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify
|
||||||
|
or distribute the Library or its derivative works. These actions
|
||||||
|
are prohibited by law if you do not accept this License.
|
||||||
|
Therefore, by modifying or distributing the Library (or any work
|
||||||
|
based on the Library), you indicate your acceptance of this
|
||||||
|
License to do so, and all its terms and conditions for copying,
|
||||||
|
distributing or modifying the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the
|
||||||
|
Library subject to these terms and conditions. You may not impose
|
||||||
|
any further restrictions on the recipients' exercise of the rights
|
||||||
|
granted herein. You are not responsible for enforcing compliance
|
||||||
|
by third parties to this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent
|
||||||
|
issues), conditions are imposed on you (whether by court order,
|
||||||
|
agreement or otherwise) that contradict the conditions of this
|
||||||
|
License, they do not excuse you from the conditions of this
|
||||||
|
License. If you cannot distribute so as to satisfy simultaneously
|
||||||
|
your obligations under this License and any other pertinent
|
||||||
|
obligations, then as a consequence you may not distribute the
|
||||||
|
Library at all. For example, if a patent license would not permit
|
||||||
|
royalty-free redistribution of the Library by all those who
|
||||||
|
receive copies directly or indirectly through you, then the only
|
||||||
|
way you could satisfy both it and this License would be to refrain
|
||||||
|
entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable
|
||||||
|
under any particular circumstance, the balance of the section is
|
||||||
|
intended to apply, and the section as a whole is intended to apply
|
||||||
|
in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of
|
||||||
|
any such claims; this section has the sole purpose of protecting
|
||||||
|
the integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is
|
||||||
|
willing to distribute software through any other system and a
|
||||||
|
licensee cannot impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed
|
||||||
|
to be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces,
|
||||||
|
the original copyright holder who places the Library under this
|
||||||
|
License may add an explicit geographical distribution limitation
|
||||||
|
excluding those countries, so that distribution is permitted only
|
||||||
|
in or among countries not thus excluded. In such case, this
|
||||||
|
License incorporates the limitation as if written in the body of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Library General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library specifies a version number of this License which applies
|
||||||
|
to it and "any later version", you have the option of following
|
||||||
|
the terms and conditions either of that version or of any later
|
||||||
|
version published by the Free Software Foundation. If the Library
|
||||||
|
does not specify a license version number, you may choose any
|
||||||
|
version ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free
|
||||||
|
status of all derivatives of our free software and of promoting
|
||||||
|
the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE
|
||||||
|
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT
|
||||||
|
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
|
||||||
|
QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE
|
||||||
|
LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
|
||||||
|
SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
|
||||||
|
MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE
|
||||||
|
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||||
|
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
|
||||||
|
INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU
|
||||||
|
OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY
|
||||||
|
OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
|
||||||
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
==============================================
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of
|
||||||
|
the ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library.
|
||||||
|
It is safest to attach them to the start of each source file to most
|
||||||
|
effectively convey the exclusion of warranty; and each file should have
|
||||||
|
at least the "copyright" line and a pointer to where the full notice is
|
||||||
|
found.
|
||||||
|
|
||||||
|
ONE LINE TO GIVE THE LIBRARY'S NAME AND AN IDEA OF WHAT IT DOES.
|
||||||
|
Copyright (C) YEAR NAME OF AUTHOR
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU Library General Public License as published
|
||||||
|
by the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper
|
||||||
|
mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or
|
||||||
|
your school, if any, to sign a "copyright disclaimer" for the library,
|
||||||
|
if necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the library
|
||||||
|
`Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
SIGNATURE OF TY COON, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
|
|
44
linuxthreads/Makefile
Normal file
44
linuxthreads/Makefile
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
|
||||||
|
# This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
# The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Library General Public License as
|
||||||
|
# published by the Free Software Foundation; either version 2 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
|
||||||
|
# The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Library General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Library General Public
|
||||||
|
# License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
# Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Sub-makefile for linuxthreads portion of the library.
|
||||||
|
#
|
||||||
|
subdir := linuxthreads
|
||||||
|
|
||||||
|
linuxthreads-version=0.7
|
||||||
|
|
||||||
|
headers := pthread.h semaphore.h bits/semaphore.h
|
||||||
|
distribute := internals.h queue.h restart.h spinlock.h
|
||||||
|
|
||||||
|
routines := weaks
|
||||||
|
|
||||||
|
extra-libs := libpthread
|
||||||
|
extra-libs-others := $(extra-libs)
|
||||||
|
|
||||||
|
libpthread-routines := attr cancel condvar join manager mutex ptfork \
|
||||||
|
pthread signals specific errno lockfile \
|
||||||
|
semaphore wrapsyscall rwlock
|
||||||
|
libpthread-map := libpthread.map
|
||||||
|
|
||||||
|
include ../Rules
|
||||||
|
|
||||||
|
# Depend on libc.so so a DT_NEEDED is generated in the shared objects.
|
||||||
|
# This ensures they will load libc.so for needed symbols if loaded by
|
||||||
|
# a statically-linked program that hasn't already loaded it.
|
||||||
|
$(objpfx)libpthread.so: $(common-objpfx)libc.so
|
163
linuxthreads/README
Normal file
163
linuxthreads/README
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
Linuxthreads - POSIX 1003.1c kernel threads for Linux
|
||||||
|
|
||||||
|
Copyright 1996, 1997 Xavier Leroy (Xavier.Leroy@inria.fr)
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTION:
|
||||||
|
|
||||||
|
This is release 0.7 (late beta) of LinuxThreads, a BiCapitalized
|
||||||
|
implementation of the Posix 1003.1c "pthread" interface for Linux.
|
||||||
|
|
||||||
|
LinuxThreads provides kernel-level threads: each thread is a separate
|
||||||
|
Unix process, sharing its address space with the other threads through
|
||||||
|
the new system call clone(). Scheduling between threads is handled by
|
||||||
|
the kernel scheduler, just like scheduling between Unix processes.
|
||||||
|
|
||||||
|
|
||||||
|
REQUIREMENTS:
|
||||||
|
|
||||||
|
- Linux version 2.0 and up (requires the new clone() system call
|
||||||
|
and the new realtime scheduler).
|
||||||
|
|
||||||
|
- For Intel platforms: libc 5.2.18 or later is required.
|
||||||
|
5.2.18 or 5.4.12 or later are recommended;
|
||||||
|
5.3.12 and 5.4.7 have problems (see the FAQ.html file for more info).
|
||||||
|
|
||||||
|
- Also supports glibc 2 (a.k.a. libc 6), which actually comes with
|
||||||
|
a specially-adapted version of this library.
|
||||||
|
|
||||||
|
- Currently supports Intel, Alpha, Sparc, Motorola 68k, ARM and MIPS
|
||||||
|
platforms.
|
||||||
|
|
||||||
|
- Multiprocessors are supported.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION:
|
||||||
|
|
||||||
|
- Edit the Makefile, set the variables in the "Configuration" section.
|
||||||
|
|
||||||
|
- Do "make".
|
||||||
|
|
||||||
|
- Do "make install".
|
||||||
|
|
||||||
|
|
||||||
|
USING LINUXTHREADS:
|
||||||
|
|
||||||
|
gcc -D_REENTRANT ... -lpthread
|
||||||
|
|
||||||
|
A complete set of manual pages is included. Also see the subdirectory
|
||||||
|
Examples/ for some sample programs.
|
||||||
|
|
||||||
|
|
||||||
|
STATUS:
|
||||||
|
|
||||||
|
- All functions in the Posix 1003.1c base interface implemented.
|
||||||
|
Also supports priority scheduling.
|
||||||
|
|
||||||
|
- For users of libc 5 (H.J.Lu's libc), a number of C library functions
|
||||||
|
are reimplemented or wrapped to make them thread-safe, including:
|
||||||
|
* malloc functions
|
||||||
|
* stdio functions (define _REENTRANT before including <stdio.h>)
|
||||||
|
* per-thread errno variable (define _REENTRANT before including <errno.h>)
|
||||||
|
* directory reading functions (opendir(), etc)
|
||||||
|
* sleep()
|
||||||
|
* gmtime(), localtime()
|
||||||
|
|
||||||
|
New library functions provided:
|
||||||
|
* flockfile(), funlockfile(), ftrylockfile()
|
||||||
|
* reentrant versions of network database functions (gethostbyname_r(), etc)
|
||||||
|
and password functions (getpwnam_r(), etc).
|
||||||
|
|
||||||
|
- libc 6 (glibc 2) provides much better thread support than libc 5,
|
||||||
|
and comes with a specially-adapted version of LinuxThreads.
|
||||||
|
For serious multithreaded programming, you should consider switching
|
||||||
|
to glibc 2. It is available from prep.ai.mit.edu:/pub/gnu and its mirrors.
|
||||||
|
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
|
||||||
|
Many existing libraries are not compatible with LinuxThreads,
|
||||||
|
either because they are not inherently thread-safe, or because they
|
||||||
|
have not been compiled with the -D_REENTRANT. For more info, see the
|
||||||
|
FAQ.html file in this directory.
|
||||||
|
|
||||||
|
A prime example of the latter is Xlib. If you link it with
|
||||||
|
LinuxThreads, you'll probably get an "unknown 0 error" very
|
||||||
|
early. This is just a consequence of the Xlib binaries using the
|
||||||
|
global variable "errno" to fetch error codes, while LinuxThreads and
|
||||||
|
the C library use the per-thread "errno" location.
|
||||||
|
|
||||||
|
See the file README.Xfree3.3 for info on how to compile the Xfree 3.3
|
||||||
|
libraries to make them compatible with LinuxThreads.
|
||||||
|
|
||||||
|
|
||||||
|
KNOWN BUGS AND LIMITATIONS:
|
||||||
|
|
||||||
|
- Threads share pretty much everything they should share according
|
||||||
|
to the standard: memory space, file descriptors, signal handlers,
|
||||||
|
current working directory, etc. One thing that they do not share
|
||||||
|
is their pid's and parent pid's. According to the standard, they
|
||||||
|
should have the same, but that's one thing we cannot achieve
|
||||||
|
in this implementation (until the CLONE_PID flag to clone() becomes
|
||||||
|
usable).
|
||||||
|
|
||||||
|
- The current implementation uses the two signals SIGUSR1 and SIGUSR2,
|
||||||
|
so user-level code cannot employ them. Ideally, there should be two
|
||||||
|
signals reserved for this library. One signal is used for restarting
|
||||||
|
threads blocked on mutexes or conditions; the other is for thread
|
||||||
|
cancellation.
|
||||||
|
|
||||||
|
- The stacks for the threads are allocated high in the memory space,
|
||||||
|
below the stack of the initial process, and spaced 2M apart.
|
||||||
|
Stacks are allocated with the "grow on demand" flag, so they don't
|
||||||
|
use much virtual space initially (4k, currently), but can grow
|
||||||
|
up to 2M if needed.
|
||||||
|
|
||||||
|
Reserving such a large address space for each thread means that,
|
||||||
|
on a 32-bit architecture, no more than about 1000 threads can
|
||||||
|
coexist (assuming a 2Gb address space for user processes),
|
||||||
|
but this is reasonable, since each thread uses up one entry in the
|
||||||
|
kernel's process table, which is usually limited to 512 processes.
|
||||||
|
|
||||||
|
Another potential problem of the "grow on demand" scheme is that
|
||||||
|
nothing prevents the user from mmap'ing something in the 2M address
|
||||||
|
window reserved for a thread stack, possibly causing later extensions of
|
||||||
|
that stack to fail. Mapping at fixed addresses should be avoided
|
||||||
|
when using this library.
|
||||||
|
|
||||||
|
- Signal handling does not fully conform to the Posix standard,
|
||||||
|
due to the fact that threads are here distinct processes that can be
|
||||||
|
sent signals individually, so there's no notion of sending a signal
|
||||||
|
to "the" process (the collection of all threads).
|
||||||
|
More precisely, here is a summary of the standard requirements
|
||||||
|
and how they are met by the implementation:
|
||||||
|
|
||||||
|
1- Synchronous signals (generated by the thread execution, e.g. SIGFPE)
|
||||||
|
are delivered to the thread that raised them.
|
||||||
|
(OK.)
|
||||||
|
|
||||||
|
2- A fatal asynchronous signal terminates all threads in the process.
|
||||||
|
(OK. The thread manager notices when a thread dies on a signal
|
||||||
|
and kills all other threads with the same signal.)
|
||||||
|
|
||||||
|
3- An asynchronous signal will be delivered to one of the threads
|
||||||
|
of the program which does not block the signal (it is unspecified
|
||||||
|
which).
|
||||||
|
(No, the signal is delivered to the thread it's been sent to,
|
||||||
|
based on the pid of the thread. If that thread is currently
|
||||||
|
blocking the signal, the signal remains pending.)
|
||||||
|
|
||||||
|
4- The signal will be delivered to at most one thread.
|
||||||
|
(OK, except for signals generated from the terminal or sent to
|
||||||
|
the process group, which will be delivered to all threads.)
|
||||||
|
|
||||||
|
- The current implementation of the MIPS support assumes a MIPS ISA II
|
||||||
|
processor or better. These processors support atomic operations by
|
||||||
|
ll/sc instructions. Older R2000/R3000 series processors are not
|
||||||
|
supported yet; support for these will have higher overhead.
|
||||||
|
|
||||||
|
- The current implementation of the ARM support assumes that the SWP
|
||||||
|
(atomic swap register with memory) instruction is available. This is
|
||||||
|
the case for all processors except for the ARM1 and ARM2. On StrongARM,
|
||||||
|
the SWP instruction does not bypass the cache, so multi-processor support
|
||||||
|
will be more troublesome.
|
352
linuxthreads/README.Xfree3.2
Normal file
352
linuxthreads/README.Xfree3.2
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
This file describes how to make a threaded X11R6.
|
||||||
|
|
||||||
|
You need the source-code of XFree-3.2. I used the sources of X11R6.1
|
||||||
|
(files: xc-1.tar.gz xc-2.tar.gz xc-3.tar.gz) and the patches to
|
||||||
|
XFree-3.2 (files: README.X11.patch R6.1pl1-3.2.diff.gz cfont32.tgz).
|
||||||
|
|
||||||
|
Untar the xc-?.tar.gz files in a directory called XF3.2 and apply
|
||||||
|
the XFree-3.2 patches as described in README.X11.patch or use the
|
||||||
|
whole XFree86 source.
|
||||||
|
|
||||||
|
Now apply the thread patch with
|
||||||
|
|
||||||
|
patch -p0 < XF3.2.xc.diff
|
||||||
|
|
||||||
|
Go to the XF3.2/xc directory and make the whole thing:
|
||||||
|
nice make World >& world.log &
|
||||||
|
tail -f world.log
|
||||||
|
|
||||||
|
Wait a few hours or interrupt the process after the shared libs
|
||||||
|
are made. The shared libs are:
|
||||||
|
|
||||||
|
XF3.2/xc/lib/ICE/libICE.so.6.0*
|
||||||
|
XF3.2/xc/lib/PEX5/libPEX5.so.6.0*
|
||||||
|
XF3.2/xc/lib/SM/libSM.so.6.0*
|
||||||
|
XF3.2/xc/lib/X11/libX11.so.6.1*
|
||||||
|
XF3.2/xc/lib/XIE/libXIE.so.6.0*
|
||||||
|
XF3.2/xc/lib/XThrStub/libXThrStub.so.6.0*
|
||||||
|
XF3.2/xc/lib/Xaw/libXaw.so.6.1*
|
||||||
|
XF3.2/xc/lib/Xext/libXext.so.6.1*
|
||||||
|
XF3.2/xc/lib/Xi/libXi.so.6.0*
|
||||||
|
XF3.2/xc/lib/Xmu/libXmu.so.6.0*
|
||||||
|
XF3.2/xc/lib/Xt/libXt.so.6.0*
|
||||||
|
XF3.2/xc/lib/Xtst/libXtst.so.6.1*
|
||||||
|
|
||||||
|
(The Program dga didn't compile, but I have not check out why.)
|
||||||
|
|
||||||
|
Now you can copy the resulting libs
|
||||||
|
|
||||||
|
cp XF3.2/xc/lib/*/*.so.?.? /usr/X11R6/lib/
|
||||||
|
|
||||||
|
and create some links
|
||||||
|
|
||||||
|
cd /usr/X11R6/lib/
|
||||||
|
ln -s libXThrStub.so.6.0 libXThrStub.so.6
|
||||||
|
ln -s libXThrStub.so.6 libXThrStub.so
|
||||||
|
|
||||||
|
or use make install (not tested, and needs new configuration).
|
||||||
|
|
||||||
|
It is possible with the libXThrSub to compile X11 programs without linking
|
||||||
|
libpthread to them and not necessary to recompile already installed
|
||||||
|
unthreaded X11 programs, because libXThrSub keeps the dynamic linker quit.
|
||||||
|
On the other hand you can link libpthread to a X11 program to use threads.
|
||||||
|
|
||||||
|
I used linux 2.0.23 and libc 5.4.7 .
|
||||||
|
|
||||||
|
Hans-Helmut B<>hmann hans@expmech.ing.tu-bs.de
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
XF3.2.xc.diff:
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
diff -u --recursive XF3.2.orig/xc/config/cf/linux.cf XF3.2/xc/config/cf/linux.cf
|
||||||
|
--- XF3.2.orig/xc/config/cf/linux.cf Sun Nov 10 17:05:30 1996
|
||||||
|
+++ XF3.2/xc/config/cf/linux.cf Sun Nov 10 16:30:55 1996
|
||||||
|
@@ -61,6 +61,14 @@
|
||||||
|
#define HasSnprintf YES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#define HasPosixThreads YES
|
||||||
|
+#define ThreadedX YES
|
||||||
|
+#define BuildThreadStubLibrary YES
|
||||||
|
+#define NeedUIThrStubs YES
|
||||||
|
+#define HasThreadSafeAPI NO
|
||||||
|
+#define SystemMTDefines -D_REENTRANT
|
||||||
|
+#define ThreadsLibraries -lpthread
|
||||||
|
+
|
||||||
|
#define AvoidNullMakeCommand YES
|
||||||
|
#define StripInstalledPrograms YES
|
||||||
|
#define CompressAllFonts YES
|
||||||
|
@@ -158,7 +166,7 @@
|
||||||
|
#define LdPostLib /* Never needed */
|
||||||
|
|
||||||
|
#ifdef i386Architecture
|
||||||
|
-#define OptimizedCDebugFlags DefaultGcc2i386Opt -m486
|
||||||
|
+#define OptimizedCDebugFlags DefaultGcc2i386Opt -m486 -pipe
|
||||||
|
#define StandardDefines -Dlinux -D__i386__ -D_POSIX_SOURCE \
|
||||||
|
-D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE
|
||||||
|
#define XawI18nDefines -DUSE_XWCHAR_STRING -DUSE_XMBTOWC
|
||||||
|
diff -u --recursive XF3.2.orig/xc/config/cf/lnxLib.tmpl XF3.2/xc/config/cf/lnxLib.tmpl
|
||||||
|
--- XF3.2.orig/xc/config/cf/lnxLib.tmpl Sun Nov 10 17:05:30 1996
|
||||||
|
+++ XF3.2/xc/config/cf/lnxLib.tmpl Sat Nov 9 14:52:39 1996
|
||||||
|
@@ -19,7 +19,7 @@
|
||||||
|
|
||||||
|
#define CplusplusLibC
|
||||||
|
|
||||||
|
-#define SharedX11Reqs
|
||||||
|
+#define SharedX11Reqs -L$(BUILDLIBDIR) -lXThrStub
|
||||||
|
#define SharedOldXReqs $(LDPRELIB) $(XLIBONLY)
|
||||||
|
#define SharedXtReqs $(LDPRELIB) $(XLIBONLY) $(SMLIB) $(ICELIB)
|
||||||
|
#define SharedXawReqs $(LDPRELIB) $(XMULIB) $(XTOOLLIB) $(XLIB)
|
||||||
|
diff -u --recursive XF3.2.orig/xc/include/Xthreads.h XF3.2/xc/include/Xthreads.h
|
||||||
|
--- XF3.2.orig/xc/include/Xthreads.h Thu Dec 7 02:19:09 1995
|
||||||
|
+++ XF3.2/xc/include/Xthreads.h Sat Nov 9 01:04:55 1996
|
||||||
|
@@ -229,12 +229,12 @@
|
||||||
|
#define xcondition_wait(c,m) pthread_cond_wait(c,m)
|
||||||
|
#define xcondition_signal(c) pthread_cond_signal(c)
|
||||||
|
#define xcondition_broadcast(c) pthread_cond_broadcast(c)
|
||||||
|
-#ifdef _DECTHREADS_
|
||||||
|
+#if defined(_DECTHREADS_) || defined(linux)
|
||||||
|
static xthread_t _X_no_thread_id;
|
||||||
|
#define xthread_have_id(id) !pthread_equal(id, _X_no_thread_id)
|
||||||
|
#define xthread_clear_id(id) id = _X_no_thread_id
|
||||||
|
#define xthread_equal(id1,id2) pthread_equal(id1, id2)
|
||||||
|
-#endif /* _DECTHREADS_ */
|
||||||
|
+#endif /* _DECTHREADS_ || linux */
|
||||||
|
#if _CMA_VENDOR_ == _CMA__IBM
|
||||||
|
#ifdef DEBUG /* too much of a hack to enable normally */
|
||||||
|
/* see also cma__obj_set_name() */
|
||||||
|
diff -u --recursive XF3.2.orig/xc/lib/X11/util/makekeys.c XF3.2/xc/lib/X11/util/makekeys.c
|
||||||
|
--- XF3.2.orig/xc/lib/X11/util/makekeys.c Mon Apr 18 02:22:22 1994
|
||||||
|
+++ XF3.2/xc/lib/X11/util/makekeys.c Sat Nov 9 00:44:14 1996
|
||||||
|
@@ -73,7 +73,7 @@
|
||||||
|
register char c;
|
||||||
|
int first;
|
||||||
|
int best_max_rehash;
|
||||||
|
- int best_z;
|
||||||
|
+ int best_z = 0;
|
||||||
|
int num_found;
|
||||||
|
KeySym val;
|
||||||
|
|
||||||
|
diff -u --recursive XF3.2.orig/xc/lib/XThrStub/Imakefile XF3.2/xc/lib/XThrStub/Imakefile
|
||||||
|
--- XF3.2.orig/xc/lib/XThrStub/Imakefile Sun Nov 10 17:08:12 1996
|
||||||
|
+++ XF3.2/xc/lib/XThrStub/Imakefile Sat Nov 9 19:04:51 1996
|
||||||
|
@@ -25,7 +25,7 @@
|
||||||
|
DEFINES = $(ALLOC_DEFINES)
|
||||||
|
INCLUDES =
|
||||||
|
SRCS = $(STUBSRCS)
|
||||||
|
- OBJS = $(STUBOBJS
|
||||||
|
+ OBJS = $(STUBOBJS)
|
||||||
|
LINTLIBS = $(LINTXLIB)
|
||||||
|
|
||||||
|
#include <Library.tmpl>
|
||||||
|
diff -u --recursive XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c XF3.2/xc/lib/XThrStub/UIThrStubs.c
|
||||||
|
--- XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c Sun Nov 10 17:08:12 1996
|
||||||
|
+++ XF3.2/xc/lib/XThrStub/UIThrStubs.c Sun Nov 10 15:14:55 1996
|
||||||
|
@@ -37,16 +37,43 @@
|
||||||
|
* specificies the thread library on the link line.
|
||||||
|
*/
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#include <pthread.h>
|
||||||
|
+#else
|
||||||
|
#include <thread.h>
|
||||||
|
#include <synch.h>
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+static pthread_t no_thread_id;
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
+
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_self = _Xthr_self_stub_
|
||||||
|
+pthread_t
|
||||||
|
+_Xthr_self_stub_()
|
||||||
|
+{
|
||||||
|
+ return(no_thread_id);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak thr_self = _Xthr_self_stub_
|
||||||
|
thread_t
|
||||||
|
_Xthr_self_stub_()
|
||||||
|
{
|
||||||
|
return((thread_t)0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_mutex_init = _Xmutex_init_stub_
|
||||||
|
+int
|
||||||
|
+_Xmutex_init_stub_(m, a)
|
||||||
|
+ pthread_mutex_t *m;
|
||||||
|
+ __const pthread_mutexattr_t *a;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak mutex_init = _Xmutex_init_stub_
|
||||||
|
int
|
||||||
|
_Xmutex_init_stub_(m, t, a)
|
||||||
|
@@ -56,7 +83,17 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_mutex_destroy = _Xmutex_destroy_stub_
|
||||||
|
+int
|
||||||
|
+_Xmutex_destroy_stub_(m)
|
||||||
|
+ pthread_mutex_t *m;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak mutex_destroy = _Xmutex_destroy_stub_
|
||||||
|
int
|
||||||
|
_Xmutex_destroy_stub_(m)
|
||||||
|
@@ -64,7 +101,17 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_mutex_lock = _Xmutex_lock_stub_
|
||||||
|
+int
|
||||||
|
+_Xmutex_lock_stub_(m)
|
||||||
|
+ pthread_mutex_t *m;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak mutex_lock = _Xmutex_lock_stub_
|
||||||
|
int
|
||||||
|
_Xmutex_lock_stub_(m)
|
||||||
|
@@ -72,7 +119,17 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_mutex_unlock = _Xmutex_unlock_stub_
|
||||||
|
+int
|
||||||
|
+_Xmutex_unlock_stub_(m)
|
||||||
|
+ pthread_mutex_t *m;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak mutex_unlock = _Xmutex_unlock_stub_
|
||||||
|
int
|
||||||
|
_Xmutex_unlock_stub_(m)
|
||||||
|
@@ -80,7 +137,18 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_cond_init = _Xcond_init_stub_
|
||||||
|
+int
|
||||||
|
+_Xcond_init_stub_(c, a)
|
||||||
|
+ pthread_cond_t *c;
|
||||||
|
+ __const pthread_condattr_t *a;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak cond_init = _Xcond_init_stub_
|
||||||
|
int
|
||||||
|
_Xcond_init_stub_(c, t, a)
|
||||||
|
@@ -90,7 +158,17 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_cond_destroy = _Xcond_destroy_stub_
|
||||||
|
+int
|
||||||
|
+_Xcond_destroy_stub_(c)
|
||||||
|
+ pthread_cond_t *c;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak cond_destroy = _Xcond_destroy_stub_
|
||||||
|
int
|
||||||
|
_Xcond_destroy_stub_(c)
|
||||||
|
@@ -98,7 +176,18 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_cond_wait = _Xcond_wait_stub_
|
||||||
|
+int
|
||||||
|
+_Xcond_wait_stub_(c,m)
|
||||||
|
+ pthread_cond_t *c;
|
||||||
|
+ pthread_mutex_t *m;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak cond_wait = _Xcond_wait_stub_
|
||||||
|
int
|
||||||
|
_Xcond_wait_stub_(c,m)
|
||||||
|
@@ -107,7 +196,17 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_cond_signal = _Xcond_signal_stub_
|
||||||
|
+int
|
||||||
|
+_Xcond_signal_stub_(c)
|
||||||
|
+ pthread_cond_t *c;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak cond_signal = _Xcond_signal_stub_
|
||||||
|
int
|
||||||
|
_Xcond_signal_stub_(c)
|
||||||
|
@@ -115,7 +214,17 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_cond_broadcast = _Xcond_broadcast_stub_
|
||||||
|
+int
|
||||||
|
+_Xcond_broadcast_stub_(c)
|
||||||
|
+ pthread_cond_t *c;
|
||||||
|
+{
|
||||||
|
+ return(0);
|
||||||
|
+}
|
||||||
|
+#else /* defined(linux) */
|
||||||
|
#pragma weak cond_broadcast = _Xcond_broadcast_stub_
|
||||||
|
int
|
||||||
|
_Xcond_broadcast_stub_(c)
|
||||||
|
@@ -123,3 +232,15 @@
|
||||||
|
{
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
+
|
||||||
|
+#if defined(linux)
|
||||||
|
+#pragma weak pthread_equal = _Xthr_equal_stub_
|
||||||
|
+int
|
||||||
|
+_Xthr_equal_stub_(t1, t2)
|
||||||
|
+ pthread_t t1;
|
||||||
|
+ pthread_t t2;
|
||||||
|
+{
|
||||||
|
+ return(1);
|
||||||
|
+}
|
||||||
|
+#endif /* defined(linux) */
|
||||||
|
-------------------------------------------------------------------------
|
117
linuxthreads/attr.c
Normal file
117
linuxthreads/attr.c
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Handling of thread attributes */
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
int pthread_attr_init(pthread_attr_t *attr)
|
||||||
|
{
|
||||||
|
attr->detachstate = PTHREAD_CREATE_JOINABLE;
|
||||||
|
attr->schedpolicy = SCHED_OTHER;
|
||||||
|
attr->schedparam.sched_priority = 0;
|
||||||
|
attr->inheritsched = PTHREAD_EXPLICIT_SCHED;
|
||||||
|
attr->scope = PTHREAD_SCOPE_SYSTEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_destroy(pthread_attr_t *attr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
|
||||||
|
{
|
||||||
|
if (detachstate < PTHREAD_CREATE_JOINABLE ||
|
||||||
|
detachstate > PTHREAD_CREATE_DETACHED)
|
||||||
|
return EINVAL;
|
||||||
|
attr->detachstate = detachstate;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
|
||||||
|
{
|
||||||
|
*detachstate = attr->detachstate;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_setschedparam(pthread_attr_t *attr,
|
||||||
|
const struct sched_param *param)
|
||||||
|
{
|
||||||
|
int max_prio = __sched_get_priority_max(attr->schedpolicy);
|
||||||
|
int min_prio = __sched_get_priority_min(attr->schedpolicy);
|
||||||
|
|
||||||
|
if (param->sched_priority < min_prio || param->sched_priority > max_prio)
|
||||||
|
return EINVAL;
|
||||||
|
attr->schedparam = *param;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_getschedparam(const pthread_attr_t *attr,
|
||||||
|
struct sched_param *param)
|
||||||
|
{
|
||||||
|
*param = attr->schedparam;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
|
||||||
|
{
|
||||||
|
if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR)
|
||||||
|
return EINVAL;
|
||||||
|
if (policy != SCHED_OTHER && geteuid() != 0)
|
||||||
|
return ENOTSUP;
|
||||||
|
attr->schedpolicy = policy;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
|
||||||
|
{
|
||||||
|
*policy = attr->schedpolicy;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit)
|
||||||
|
{
|
||||||
|
if (inherit != PTHREAD_INHERIT_SCHED && inherit != PTHREAD_EXPLICIT_SCHED)
|
||||||
|
return EINVAL;
|
||||||
|
attr->inheritsched = inherit;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit)
|
||||||
|
{
|
||||||
|
*inherit = attr->inheritsched;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_setscope(pthread_attr_t *attr, int scope)
|
||||||
|
{
|
||||||
|
switch (scope) {
|
||||||
|
case PTHREAD_SCOPE_SYSTEM:
|
||||||
|
attr->scope = scope;
|
||||||
|
return 0;
|
||||||
|
case PTHREAD_SCOPE_PROCESS:
|
||||||
|
return ENOTSUP;
|
||||||
|
default:
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope)
|
||||||
|
{
|
||||||
|
*scope = attr->scope;
|
||||||
|
return 0;
|
||||||
|
}
|
131
linuxthreads/cancel.c
Normal file
131
linuxthreads/cancel.c
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Thread cancellation */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
int pthread_setcancelstate(int state, int * oldstate)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
|
||||||
|
return EINVAL;
|
||||||
|
if (oldstate != NULL) *oldstate = self->p_cancelstate;
|
||||||
|
self->p_cancelstate = state;
|
||||||
|
if (self->p_canceled &&
|
||||||
|
self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
|
||||||
|
self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_setcanceltype(int type, int * oldtype)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
return EINVAL;
|
||||||
|
if (oldtype != NULL) *oldtype = self->p_canceltype;
|
||||||
|
self->p_canceltype = type;
|
||||||
|
if (self->p_canceled &&
|
||||||
|
self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
|
||||||
|
self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cancel(pthread_t thread)
|
||||||
|
{
|
||||||
|
pthread_handle handle = thread_handle(thread);
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
if (invalid_handle(handle, thread)) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return ESRCH;
|
||||||
|
}
|
||||||
|
handle->h_descr->p_canceled = 1;
|
||||||
|
pid = handle->h_descr->p_pid;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
kill(pid, PTHREAD_SIG_CANCEL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pthread_testcancel(void)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
|
||||||
|
void (*routine)(void *), void * arg)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
buffer->routine = routine;
|
||||||
|
buffer->arg = arg;
|
||||||
|
buffer->prev = self->p_cleanup;
|
||||||
|
self->p_cleanup = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
|
||||||
|
int execute)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
if (execute) buffer->routine(buffer->arg);
|
||||||
|
self->p_cleanup = buffer->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
|
||||||
|
void (*routine)(void *), void * arg)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
buffer->routine = routine;
|
||||||
|
buffer->arg = arg;
|
||||||
|
buffer->canceltype = self->p_canceltype;
|
||||||
|
buffer->prev = self->p_cleanup;
|
||||||
|
self->p_canceltype = PTHREAD_CANCEL_DEFERRED;
|
||||||
|
self->p_cleanup = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
|
||||||
|
int execute)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
if (execute) buffer->routine(buffer->arg);
|
||||||
|
self->p_cleanup = buffer->prev;
|
||||||
|
self->p_canceltype = buffer->canceltype;
|
||||||
|
if (self->p_canceled &&
|
||||||
|
self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
|
||||||
|
self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __pthread_perform_cleanup(void)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
struct _pthread_cleanup_buffer * c;
|
||||||
|
for (c = self->p_cleanup; c != NULL; c = c->prev) c->routine(c->arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PIC
|
||||||
|
/* We need a hook to force the cancelation wrappers to be linked in when
|
||||||
|
static libpthread is used. */
|
||||||
|
extern const int __pthread_provide_wrappers;
|
||||||
|
static const int * const __pthread_require_wrappers =
|
||||||
|
&__pthread_provide_wrappers;
|
||||||
|
#endif
|
207
linuxthreads/condvar.c
Normal file
207
linuxthreads/condvar.c
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* and Pavel Krauz (krauz@fsid.cvut.cz). */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Condition variables */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
static void remove_from_queue(pthread_queue * q, pthread_descr th);
|
||||||
|
|
||||||
|
int pthread_cond_init(pthread_cond_t *cond,
|
||||||
|
const pthread_condattr_t *cond_attr)
|
||||||
|
{
|
||||||
|
cond->c_spinlock = 0;
|
||||||
|
queue_init(&cond->c_waiting);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cond_destroy(pthread_cond_t *cond)
|
||||||
|
{
|
||||||
|
pthread_descr head;
|
||||||
|
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
head = cond->c_waiting.head;
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
if (head != NULL) return EBUSY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
volatile pthread_descr self = thread_self();
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
enqueue(&cond->c_waiting, self);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
pthread_mutex_unlock(mutex);
|
||||||
|
suspend_with_cancellation(self);
|
||||||
|
pthread_mutex_lock(mutex);
|
||||||
|
/* This is a cancellation point */
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
|
||||||
|
/* Remove ourselves from the waiting queue if we're still on it */
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
remove_from_queue(&cond->c_waiting, self);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
pthread_cond_timedwait_relative(pthread_cond_t *cond,
|
||||||
|
pthread_mutex_t *mutex,
|
||||||
|
const struct timespec * reltime)
|
||||||
|
{
|
||||||
|
volatile pthread_descr self = thread_self();
|
||||||
|
sigset_t unblock, initial_mask;
|
||||||
|
int retsleep;
|
||||||
|
sigjmp_buf jmpbuf;
|
||||||
|
|
||||||
|
/* Wait on the condition */
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
enqueue(&cond->c_waiting, self);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
pthread_mutex_unlock(mutex);
|
||||||
|
/* Set up a longjmp handler for the restart signal */
|
||||||
|
/* No need to save the signal mask, since PTHREAD_SIG_RESTART will be
|
||||||
|
blocked when doing the siglongjmp, and we'll just leave it blocked. */
|
||||||
|
if (sigsetjmp(jmpbuf, 0) == 0) {
|
||||||
|
self->p_signal_jmp = &jmpbuf;
|
||||||
|
self->p_signal = 0;
|
||||||
|
/* Check for cancellation */
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
|
||||||
|
retsleep = -1;
|
||||||
|
} else {
|
||||||
|
/* Unblock the restart signal */
|
||||||
|
sigemptyset(&unblock);
|
||||||
|
sigaddset(&unblock, PTHREAD_SIG_RESTART);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
|
||||||
|
/* Sleep for the required duration */
|
||||||
|
retsleep = __libc_nanosleep(reltime, NULL);
|
||||||
|
/* Block the restart signal again */
|
||||||
|
sigprocmask(SIG_SETMASK, &initial_mask, NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retsleep = -1;
|
||||||
|
}
|
||||||
|
self->p_signal_jmp = NULL;
|
||||||
|
/* Here, either the condition was signaled (self->p_signal != 0)
|
||||||
|
or we got canceled (self->p_canceled != 0)
|
||||||
|
or the timeout occurred (retsleep == 0)
|
||||||
|
or another interrupt occurred (retsleep == -1) */
|
||||||
|
/* Re-acquire the spinlock */
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
/* This is a cancellation point */
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
|
||||||
|
remove_from_queue(&cond->c_waiting, self);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
pthread_mutex_lock(mutex);
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
/* If not signaled: also remove ourselves and return an error code */
|
||||||
|
if (self->p_signal == 0) {
|
||||||
|
remove_from_queue(&cond->c_waiting, self);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
pthread_mutex_lock(mutex);
|
||||||
|
return retsleep == 0 ? ETIMEDOUT : EINTR;
|
||||||
|
}
|
||||||
|
/* Otherwise, return normally */
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
pthread_mutex_lock(mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||||
|
const struct timespec * abstime)
|
||||||
|
{
|
||||||
|
struct timeval now;
|
||||||
|
struct timespec reltime;
|
||||||
|
/* Compute a time offset relative to now */
|
||||||
|
__gettimeofday(&now, NULL);
|
||||||
|
reltime.tv_sec = abstime->tv_sec - now.tv_sec;
|
||||||
|
reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
|
||||||
|
if (reltime.tv_nsec < 0) {
|
||||||
|
reltime.tv_nsec += 1000000000;
|
||||||
|
reltime.tv_sec -= 1;
|
||||||
|
}
|
||||||
|
if (reltime.tv_sec < 0) return ETIMEDOUT;
|
||||||
|
return pthread_cond_timedwait_relative(cond, mutex, &reltime);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cond_signal(pthread_cond_t *cond)
|
||||||
|
{
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
th = dequeue(&cond->c_waiting);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
if (th != NULL) restart(th);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cond_broadcast(pthread_cond_t *cond)
|
||||||
|
{
|
||||||
|
pthread_queue tosignal;
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire(&cond->c_spinlock);
|
||||||
|
/* Copy the current state of the waiting queue and empty it */
|
||||||
|
tosignal = cond->c_waiting;
|
||||||
|
queue_init(&cond->c_waiting);
|
||||||
|
release(&cond->c_spinlock);
|
||||||
|
/* Now signal each process in the queue */
|
||||||
|
while ((th = dequeue(&tosignal)) != NULL) restart(th);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_condattr_init(pthread_condattr_t *attr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_condattr_destroy(pthread_condattr_t *attr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auxiliary function on queues */
|
||||||
|
|
||||||
|
static void remove_from_queue(pthread_queue * q, pthread_descr th)
|
||||||
|
{
|
||||||
|
pthread_descr t;
|
||||||
|
|
||||||
|
if (q->head == NULL) return;
|
||||||
|
if (q->head == th) {
|
||||||
|
q->head = th->p_nextwaiting;
|
||||||
|
if (q->head == NULL) q->tail = NULL;
|
||||||
|
th->p_nextwaiting = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (t = q->head; t->p_nextwaiting != NULL; t = t->p_nextwaiting) {
|
||||||
|
if (t->p_nextwaiting == th) {
|
||||||
|
t->p_nextwaiting = th->p_nextwaiting;
|
||||||
|
if (th->p_nextwaiting == NULL) q->tail = t;
|
||||||
|
th->p_nextwaiting = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
linuxthreads/configure
vendored
Executable file
5
linuxthreads/configure
vendored
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
# This is only to keep the GNU C library configure mechanism happy.
|
||||||
|
#
|
||||||
|
# Perhaps some day we need a real configuration script for different
|
||||||
|
# kernel versions or so.
|
||||||
|
exit 0
|
32
linuxthreads/errno.c
Normal file
32
linuxthreads/errno.c
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Define the location of errno for the remainder of the C library */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
int * __errno_location()
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
return self->p_errnop;
|
||||||
|
}
|
||||||
|
|
||||||
|
int * __h_errno_location()
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
return self->p_h_errnop;
|
||||||
|
}
|
280
linuxthreads/internals.h
Normal file
280
linuxthreads/internals.h
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Internal data structures */
|
||||||
|
|
||||||
|
/* Includes */
|
||||||
|
|
||||||
|
#include <bits/libc-lock.h> /* for _LIBC_TSD_KEY_N */
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "pt-machine.h"
|
||||||
|
|
||||||
|
/* Arguments passed to thread creation routine */
|
||||||
|
|
||||||
|
struct pthread_start_args {
|
||||||
|
void * (*start_routine)(void *); /* function to run */
|
||||||
|
void * arg; /* its argument */
|
||||||
|
sigset_t mask; /* initial signal mask for thread */
|
||||||
|
int schedpolicy; /* initial scheduling policy (if any) */
|
||||||
|
struct sched_param schedparam; /* initial scheduling parameters (if any) */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* We keep thread specific data in a special data structure, a two-level
|
||||||
|
array. The top-level array contains pointers to dynamically allocated
|
||||||
|
arrays of a certain number of data pointers. So we can implement a
|
||||||
|
sparse array. Each dynamic second-level array has
|
||||||
|
PTHREAD_KEY_2NDLEVEL_SIZE
|
||||||
|
entries. This value shouldn't be too large. */
|
||||||
|
#define PTHREAD_KEY_2NDLEVEL_SIZE 32
|
||||||
|
|
||||||
|
/* We need to address PTHREAD_KEYS_MAX key with PTHREAD_KEY_2NDLEVEL_SIZE
|
||||||
|
keys in each subarray. */
|
||||||
|
#define PTHREAD_KEY_1STLEVEL_SIZE \
|
||||||
|
((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1) \
|
||||||
|
/ PTHREAD_KEY_2NDLEVEL_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
#define PTHREAD_START_ARGS_INITIALIZER { NULL, NULL, {{0, }}, 0, { 0 } }
|
||||||
|
|
||||||
|
/* The type of thread descriptors */
|
||||||
|
|
||||||
|
typedef struct _pthread_descr_struct * pthread_descr;
|
||||||
|
|
||||||
|
struct _pthread_descr_struct {
|
||||||
|
pthread_descr p_nextlive, p_prevlive;
|
||||||
|
/* Double chaining of active threads */
|
||||||
|
pthread_descr p_nextwaiting; /* Next element in the queue holding the thr */
|
||||||
|
pthread_t p_tid; /* Thread identifier */
|
||||||
|
int p_pid; /* PID of Unix process */
|
||||||
|
int p_priority; /* Thread priority (== 0 if not realtime) */
|
||||||
|
int * p_spinlock; /* Spinlock for synchronized accesses */
|
||||||
|
int p_signal; /* last signal received */
|
||||||
|
sigjmp_buf * p_signal_jmp; /* where to siglongjmp on a signal or NULL */
|
||||||
|
sigjmp_buf * p_cancel_jmp; /* where to siglongjmp on a cancel or NULL */
|
||||||
|
char p_terminated; /* true if terminated e.g. by pthread_exit */
|
||||||
|
char p_detached; /* true if detached */
|
||||||
|
char p_exited; /* true if the assoc. process terminated */
|
||||||
|
void * p_retval; /* placeholder for return value */
|
||||||
|
int p_retcode; /* placeholder for return code */
|
||||||
|
pthread_descr p_joining; /* thread joining on that thread or NULL */
|
||||||
|
struct _pthread_cleanup_buffer * p_cleanup; /* cleanup functions */
|
||||||
|
char p_cancelstate; /* cancellation state */
|
||||||
|
char p_canceltype; /* cancellation type (deferred/async) */
|
||||||
|
char p_canceled; /* cancellation request pending */
|
||||||
|
int * p_errnop; /* pointer to used errno variable */
|
||||||
|
int p_errno; /* error returned by last system call */
|
||||||
|
int * p_h_errnop; /* pointer to used h_errno variable */
|
||||||
|
int p_h_errno; /* error returned by last netdb function */
|
||||||
|
struct pthread_start_args p_start_args; /* arguments for thread creation */
|
||||||
|
void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE]; /* thread-specific data */
|
||||||
|
void * p_libc_specific[_LIBC_TSD_KEY_N]; /* thread-specific data for libc */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The type of thread handles. */
|
||||||
|
|
||||||
|
typedef struct pthread_handle_struct * pthread_handle;
|
||||||
|
|
||||||
|
struct pthread_handle_struct {
|
||||||
|
int h_spinlock; /* Spinlock for sychronized access */
|
||||||
|
pthread_descr h_descr; /* Thread descriptor or NULL if invalid */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The type of messages sent to the thread manager thread */
|
||||||
|
|
||||||
|
struct pthread_request {
|
||||||
|
pthread_descr req_thread; /* Thread doing the request */
|
||||||
|
enum { /* Request kind */
|
||||||
|
REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT
|
||||||
|
} req_kind;
|
||||||
|
union { /* Arguments for request */
|
||||||
|
struct { /* For REQ_CREATE: */
|
||||||
|
const pthread_attr_t * attr; /* thread attributes */
|
||||||
|
void * (*fn)(void *); /* start function */
|
||||||
|
void * arg; /* argument to start function */
|
||||||
|
sigset_t mask; /* signal mask */
|
||||||
|
} create;
|
||||||
|
struct { /* For REQ_FREE: */
|
||||||
|
pthread_descr thread; /* descriptor of thread to free */
|
||||||
|
} free;
|
||||||
|
struct { /* For REQ_PROCESS_EXIT: */
|
||||||
|
int code; /* exit status */
|
||||||
|
} exit;
|
||||||
|
} req_args;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Signals used for suspend/restart and for cancellation notification. */
|
||||||
|
|
||||||
|
#ifdef SIGRTMIN
|
||||||
|
/* The have real-time signals. */
|
||||||
|
extern int __pthread_sig_restart;
|
||||||
|
extern int __pthread_sig_cancel;
|
||||||
|
# define PTHREAD_SIG_RESTART __pthread_sig_restart
|
||||||
|
# define PTHREAD_SIG_CANCEL __pthread_sig_cancel
|
||||||
|
#else
|
||||||
|
# define PTHREAD_SIG_RESTART SIGUSR1
|
||||||
|
# define PTHREAD_SIG_CANCEL SIGUSR2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Global array of thread handles, used for validating a thread id
|
||||||
|
and retrieving the corresponding thread descriptor. Also used for
|
||||||
|
mapping the available stack segments. */
|
||||||
|
|
||||||
|
extern struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX];
|
||||||
|
|
||||||
|
/* Descriptor of the initial thread */
|
||||||
|
|
||||||
|
extern struct _pthread_descr_struct __pthread_initial_thread;
|
||||||
|
|
||||||
|
/* Descriptor of the manager thread */
|
||||||
|
|
||||||
|
extern struct _pthread_descr_struct __pthread_manager_thread;
|
||||||
|
|
||||||
|
/* Descriptor of the main thread */
|
||||||
|
|
||||||
|
extern pthread_descr __pthread_main_thread;
|
||||||
|
|
||||||
|
/* Limit between the stack of the initial thread (above) and the
|
||||||
|
stacks of other threads (below). Aligned on a STACK_SIZE boundary.
|
||||||
|
Initially 0, meaning that the current thread is (by definition)
|
||||||
|
the initial thread. */
|
||||||
|
|
||||||
|
extern char *__pthread_initial_thread_bos;
|
||||||
|
|
||||||
|
/* File descriptor for sending requests to the thread manager.
|
||||||
|
Initially -1, meaning that pthread_initialize must be called. */
|
||||||
|
|
||||||
|
extern int __pthread_manager_request;
|
||||||
|
|
||||||
|
/* Other end of the pipe for sending requests to the thread manager. */
|
||||||
|
|
||||||
|
extern int __pthread_manager_reader;
|
||||||
|
|
||||||
|
/* Limits of the thread manager stack. */
|
||||||
|
|
||||||
|
extern char *__pthread_manager_thread_bos;
|
||||||
|
extern char *__pthread_manager_thread_tos;
|
||||||
|
|
||||||
|
/* Pending request for a process-wide exit */
|
||||||
|
|
||||||
|
extern int __pthread_exit_requested, __pthread_exit_code;
|
||||||
|
|
||||||
|
/* Return the handle corresponding to a thread id */
|
||||||
|
|
||||||
|
static inline pthread_handle thread_handle(pthread_t id)
|
||||||
|
{
|
||||||
|
return &__pthread_handles[id % PTHREAD_THREADS_MAX];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate a thread handle. Must have acquired h->h_spinlock before. */
|
||||||
|
|
||||||
|
static inline int invalid_handle(pthread_handle h, pthread_t id)
|
||||||
|
{
|
||||||
|
return h->h_descr == NULL || h->h_descr->p_tid != id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in defaults left unspecified by pt-machine.h. */
|
||||||
|
|
||||||
|
/* The page size we can get from the system. This should likely not be
|
||||||
|
changed by the machine file but, you never know. */
|
||||||
|
#ifndef PAGE_SIZE
|
||||||
|
#define PAGE_SIZE (sysconf (_SC_PAGE_SIZE))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The max size of the thread stack segments. If the default
|
||||||
|
THREAD_SELF implementation is used, this must be a power of two and
|
||||||
|
a multiple of PAGE_SIZE. */
|
||||||
|
#ifndef STACK_SIZE
|
||||||
|
#define STACK_SIZE (2 * 1024 * 1024)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The initial size of the thread stack. Must be a multiple of PAGE_SIZE. */
|
||||||
|
#ifndef INITIAL_STACK_SIZE
|
||||||
|
#define INITIAL_STACK_SIZE (4 * PAGE_SIZE)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Size of the thread manager stack. The "- 32" avoids wasting space
|
||||||
|
with some malloc() implementations. */
|
||||||
|
#ifndef THREAD_MANAGER_STACK_SIZE
|
||||||
|
#define THREAD_MANAGER_STACK_SIZE (2 * PAGE_SIZE - 32)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The base of the "array" of thread stacks. The array will grow down from
|
||||||
|
here. Defaults to the calculated bottom of the initial application
|
||||||
|
stack. */
|
||||||
|
#ifndef THREAD_STACK_START_ADDRESS
|
||||||
|
#define THREAD_STACK_START_ADDRESS __pthread_initial_thread_bos
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#ifndef CURRENT_STACK_FRAME
|
||||||
|
#define CURRENT_STACK_FRAME ({ char __csf; &__csf; })
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Recover thread descriptor for the current thread */
|
||||||
|
|
||||||
|
static inline pthread_descr thread_self (void) __attribute__ ((const));
|
||||||
|
static inline pthread_descr thread_self (void)
|
||||||
|
{
|
||||||
|
#ifdef THREAD_SELF
|
||||||
|
return THREAD_SELF;
|
||||||
|
#else
|
||||||
|
char *sp = CURRENT_STACK_FRAME;
|
||||||
|
if (sp >= __pthread_initial_thread_bos)
|
||||||
|
return &__pthread_initial_thread;
|
||||||
|
else if (sp >= __pthread_manager_thread_bos
|
||||||
|
&& sp < __pthread_manager_thread_tos)
|
||||||
|
return &__pthread_manager_thread;
|
||||||
|
else
|
||||||
|
return (pthread_descr)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debugging */
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include <assert.h>
|
||||||
|
#define ASSERT assert
|
||||||
|
#define MSG __pthread_message
|
||||||
|
#else
|
||||||
|
#define ASSERT(x)
|
||||||
|
#define MSG(msg,arg...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Internal global functions */
|
||||||
|
|
||||||
|
void __pthread_destroy_specifics(void);
|
||||||
|
void __pthread_perform_cleanup(void);
|
||||||
|
void __pthread_sighandler(int sig);
|
||||||
|
void __pthread_message(char * fmt, long arg, ...);
|
||||||
|
int __pthread_manager(void *reqfd);
|
||||||
|
void __pthread_manager_sighandler(int sig);
|
||||||
|
void __pthread_reset_main_thread(void);
|
||||||
|
void __fresetlockfiles(void);
|
||||||
|
|
||||||
|
|
||||||
|
/* Prototypes for the function without cancelation support when the
|
||||||
|
normal version has it. */
|
||||||
|
extern int __libc_close (int fd);
|
||||||
|
extern int __libc_nanosleep (const struct timespec *requested_time,
|
||||||
|
struct timespec *remaining);
|
||||||
|
extern int __libc_read (int fd, void *buf, size_t count);
|
||||||
|
extern pid_t __libc_waitpid (pid_t pid, int *stat_loc, int options);
|
||||||
|
extern int __libc_write (int fd, const void *buf, size_t count);
|
145
linuxthreads/join.c
Normal file
145
linuxthreads/join.c
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Thread termination and joining */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
void pthread_exit(void * retval)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
pthread_descr joining;
|
||||||
|
struct pthread_request request;
|
||||||
|
|
||||||
|
/* Reset the cancellation flag to avoid looping if the cleanup handlers
|
||||||
|
contain cancellation points */
|
||||||
|
self->p_canceled = 0;
|
||||||
|
/* Call cleanup functions and destroy the thread-specific data */
|
||||||
|
__pthread_perform_cleanup();
|
||||||
|
__pthread_destroy_specifics();
|
||||||
|
/* Store return value */
|
||||||
|
acquire(self->p_spinlock);
|
||||||
|
self->p_retval = retval;
|
||||||
|
/* Say that we've terminated */
|
||||||
|
self->p_terminated = 1;
|
||||||
|
/* See if someone is joining on us */
|
||||||
|
joining = self->p_joining;
|
||||||
|
release(self->p_spinlock);
|
||||||
|
/* Restart joining thread if any */
|
||||||
|
if (joining != NULL) restart(joining);
|
||||||
|
/* If this is the initial thread, block until all threads have terminated.
|
||||||
|
If another thread calls exit, we'll be terminated from our signal
|
||||||
|
handler. */
|
||||||
|
if (self == __pthread_main_thread && __pthread_manager_request >= 0) {
|
||||||
|
request.req_thread = self;
|
||||||
|
request.req_kind = REQ_MAIN_THREAD_EXIT;
|
||||||
|
__libc_write(__pthread_manager_request, (char *)&request, sizeof(request));
|
||||||
|
suspend(self);
|
||||||
|
}
|
||||||
|
/* Exit the process (but don't flush stdio streams, and don't run
|
||||||
|
atexit functions). */
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_join(pthread_t thread_id, void ** thread_return)
|
||||||
|
{
|
||||||
|
volatile pthread_descr self = thread_self();
|
||||||
|
struct pthread_request request;
|
||||||
|
pthread_handle handle = thread_handle(thread_id);
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
if (invalid_handle(handle, thread_id)) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return ESRCH;
|
||||||
|
}
|
||||||
|
th = handle->h_descr;
|
||||||
|
if (th == self) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return EDEADLK;
|
||||||
|
}
|
||||||
|
/* If detached or already joined, error */
|
||||||
|
if (th->p_detached || th->p_joining != NULL) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
/* If not terminated yet, suspend ourselves. */
|
||||||
|
if (! th->p_terminated) {
|
||||||
|
th->p_joining = self;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
suspend_with_cancellation(self);
|
||||||
|
/* This is a cancellation point */
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
|
||||||
|
th->p_joining = NULL;
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
}
|
||||||
|
/* Get return value */
|
||||||
|
if (thread_return != NULL) *thread_return = th->p_retval;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
/* Send notification to thread manager */
|
||||||
|
if (__pthread_manager_request >= 0) {
|
||||||
|
request.req_thread = self;
|
||||||
|
request.req_kind = REQ_FREE;
|
||||||
|
request.req_args.free.thread = th;
|
||||||
|
__libc_write(__pthread_manager_request,
|
||||||
|
(char *) &request, sizeof(request));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_detach(pthread_t thread_id)
|
||||||
|
{
|
||||||
|
int terminated;
|
||||||
|
struct pthread_request request;
|
||||||
|
pthread_handle handle = thread_handle(thread_id);
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
if (invalid_handle(handle, thread_id)) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return ESRCH;
|
||||||
|
}
|
||||||
|
th = handle->h_descr;
|
||||||
|
/* If already detached, error */
|
||||||
|
if (th->p_detached) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
/* If already joining, don't do anything. */
|
||||||
|
if (th->p_joining != NULL) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Mark as detached */
|
||||||
|
th->p_detached = 1;
|
||||||
|
terminated = th->p_terminated;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
/* If already terminated, notify thread manager to reclaim resources */
|
||||||
|
if (terminated && __pthread_manager_request >= 0) {
|
||||||
|
request.req_thread = thread_self();
|
||||||
|
request.req_kind = REQ_FREE;
|
||||||
|
request.req_args.free.thread = th;
|
||||||
|
__libc_write(__pthread_manager_request,
|
||||||
|
(char *) &request, sizeof(request));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
62
linuxthreads/libpthread.map
Normal file
62
linuxthreads/libpthread.map
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
GLIBC_2.0 {
|
||||||
|
global:
|
||||||
|
# Hidden entry point (through macros).
|
||||||
|
_pthread_cleanup_pop; _pthread_cleanup_pop_restore; _pthread_cleanup_push;
|
||||||
|
_pthread_cleanup_push_defer;
|
||||||
|
|
||||||
|
# Overwritten libc functions.
|
||||||
|
close; fcntl; fork; fsync; lseek; msync; nanosleep; open; pause; raise;
|
||||||
|
read; system; tcdrain; wait; waitpid; write;
|
||||||
|
|
||||||
|
# POSIX.1c extensions to libc.
|
||||||
|
flockfile; funlockfile; ftrylockfile;
|
||||||
|
|
||||||
|
# Non-standard POSIX1.x functions.
|
||||||
|
pthread_kill_other_threads_np; pthread_mutexattr_getkind_np;
|
||||||
|
pthread_mutexattr_setkind_np;
|
||||||
|
|
||||||
|
# Real POSIX.1c functions.
|
||||||
|
pthread_atfork; pthread_attr_destroy; pthread_attr_getdetachstate;
|
||||||
|
pthread_attr_getinheritsched; pthread_attr_getschedparam;
|
||||||
|
pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_init;
|
||||||
|
pthread_attr_setdetachstate; pthread_attr_setinheritsched;
|
||||||
|
pthread_attr_setschedparam; pthread_attr_setschedpolicy;
|
||||||
|
pthread_attr_setscope; pthread_cancel; pthread_cond_broadcast;
|
||||||
|
pthread_cond_destroy; pthread_cond_init; pthread_cond_signal;
|
||||||
|
pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy;
|
||||||
|
pthread_condattr_init; pthread_create; pthread_detach; pthread_equal;
|
||||||
|
pthread_exit; pthread_getschedparam; pthread_getspecific; pthread_join;
|
||||||
|
pthread_key_create; pthread_key_delete; pthread_kill;
|
||||||
|
pthread_mutex_destroy; pthread_mutex_init; pthread_mutex_lock;
|
||||||
|
pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy;
|
||||||
|
pthread_mutexattr_init; pthread_once; pthread_self; pthread_setcancelstate;
|
||||||
|
pthread_setcanceltype; pthread_setschedparam; pthread_setspecific;
|
||||||
|
pthread_sigmask; pthread_testcancel;
|
||||||
|
|
||||||
|
sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait;
|
||||||
|
sigwait;
|
||||||
|
|
||||||
|
# Protected names for functions used in other shared objects.
|
||||||
|
__pthread_getspecific; __pthread_initialize; __pthread_mutex_destroy;
|
||||||
|
__pthread_mutex_init; __pthread_mutex_lock; __pthread_mutex_trylock;
|
||||||
|
__pthread_mutex_unlock; __pthread_mutexattr_destroy;
|
||||||
|
__pthread_mutexattr_init; __pthread_mutexattr_setkind_np;
|
||||||
|
__pthread_setspecific;
|
||||||
|
|
||||||
|
# The error functions.
|
||||||
|
__errno_location; __h_errno_location;
|
||||||
|
|
||||||
|
local:
|
||||||
|
*;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GLIBC_2.1 {
|
||||||
|
global:
|
||||||
|
# Unix98 extensions.
|
||||||
|
pthread_rwlock_init; pthread_rwlock_destroy; pthread_rwlock_rdlock;
|
||||||
|
pthread_rwlock_tryrdlock; pthread_rwlock_wrlock; pthread_rwlock_trywrlock;
|
||||||
|
pthread_rwlock_unlock; pthread_rwlockattr_init; pthread_rwlockattr_destroy;
|
||||||
|
pthread_rwlockattr_getpshared; pthread_rwlockattr_setpshared;
|
||||||
|
pthread_rwlockattr_getkind_np; pthread_rwlockattr_setkind_np;
|
||||||
|
} GLIBC_2.0;
|
87
linuxthreads/lockfile.c
Normal file
87
linuxthreads/lockfile.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* lockfile - Handle locking and unlocking of stream.
|
||||||
|
Copyright (C) 1996 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include <bits/libc-lock.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
#include "../libio/libioP.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
__flockfile (FILE *stream)
|
||||||
|
{
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
__pthread_mutex_lock (stream->_lock);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
#undef _IO_flockfile
|
||||||
|
strong_alias (__flockfile, _IO_flockfile)
|
||||||
|
#endif
|
||||||
|
weak_alias (__flockfile, flockfile);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
__funlockfile (FILE *stream)
|
||||||
|
{
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
__pthread_mutex_unlock (stream->_lock);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
#undef _IO_funlockfile
|
||||||
|
strong_alias (__funlockfile, _IO_funlockfile)
|
||||||
|
#endif
|
||||||
|
weak_alias (__funlockfile, funlockfile);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
__ftrylockfile (FILE *stream)
|
||||||
|
{
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
return __pthread_mutex_trylock (stream->_lock);
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
strong_alias (__ftrylockfile, _IO_ftrylockfile)
|
||||||
|
#endif
|
||||||
|
weak_alias (__ftrylockfile, ftrylockfile);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
__fresetlockfiles (void)
|
||||||
|
{
|
||||||
|
#ifdef USE_IN_LIBIO
|
||||||
|
_IO_FILE *fp;
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
|
||||||
|
__pthread_mutexattr_init (&attr);
|
||||||
|
__pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
|
||||||
|
|
||||||
|
for (fp = _IO_list_all; fp != NULL; fp = fp->_chain)
|
||||||
|
__pthread_mutex_init (fp->_lock, &attr);
|
||||||
|
|
||||||
|
__pthread_mutexattr_destroy (&attr);
|
||||||
|
#endif
|
||||||
|
}
|
31
linuxthreads/man/Makefile
Normal file
31
linuxthreads/man/Makefile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
SOURCES=pthread_atfork.man pthread_attr_init.man pthread_cancel.man \
|
||||||
|
pthread_cleanup_push.man pthread_cond_init.man \
|
||||||
|
pthread_condattr_init.man pthread_create.man pthread_detach.man \
|
||||||
|
pthread_equal.man pthread_exit.man pthread_join.man \
|
||||||
|
pthread_key_create.man pthread_mutex_init.man \
|
||||||
|
pthread_mutexattr_init.man pthread_once.man pthread_self.man \
|
||||||
|
pthread_setschedparam.man pthread_sigmask.man sem_init.man \
|
||||||
|
pthread_kill_other_threads_np.man
|
||||||
|
|
||||||
|
MANPAGES=$(SOURCES:.man=.3thr)
|
||||||
|
|
||||||
|
PREPRO=perl troffprepro
|
||||||
|
|
||||||
|
MANDIR=/usr/man/man3
|
||||||
|
|
||||||
|
all: $(MANPAGES)
|
||||||
|
|
||||||
|
.SUFFIXES: .man .3thr
|
||||||
|
|
||||||
|
.man.3thr:
|
||||||
|
$(PREPRO) $*.man $*.3thr
|
||||||
|
|
||||||
|
$(MANPAGES): troffprepro
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.3thr
|
||||||
|
rm -f *~
|
||||||
|
|
||||||
|
install:
|
||||||
|
install *.3thr $(MANDIR)
|
||||||
|
@echo "*** Remember to run /usr/sbin/makewhatis `dirname $(MANDIR)` at some point"
|
58
linuxthreads/man/pthread_atfork.man
Normal file
58
linuxthreads/man/pthread_atfork.man
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
.TH PTHREAD_ATFORK 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_atfork \- register handlers to be called at fork(2) time
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
!pthread_atfork! registers handler functions to be called just before
|
||||||
|
and just after a new process is created with !fork!(2). The |prepare|
|
||||||
|
handler will be called from the parent process, just before the new
|
||||||
|
process is created. The |parent| handler will be called from the parent
|
||||||
|
process, just before !fork!(2) returns. The |child| handler will be
|
||||||
|
called from the child process, just before !fork!(2) returns.
|
||||||
|
|
||||||
|
One or several of the three handlers |prepare|, |parent| and |child|
|
||||||
|
can be given as !NULL!, meaning that no handler needs to be called at
|
||||||
|
the corresponding point.
|
||||||
|
|
||||||
|
!pthread_atfork! can be called several times to install several sets
|
||||||
|
of handlers. At !fork!(2) time, the |prepare| handlers are called in
|
||||||
|
LIFO order (last added with !pthread_atfork!, first called before !fork!),
|
||||||
|
while the |parent| and |child| handlers are called in FIFO order
|
||||||
|
(first added, first called).
|
||||||
|
|
||||||
|
To understand the purpose of !pthread_atfork!, recall that !fork!(2)
|
||||||
|
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 !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 !pthread_atfork!
|
||||||
|
as follows: the |prepare| handler locks the global mutexes (in locking
|
||||||
|
order), and the |parent| and |child| handlers unlock them (in
|
||||||
|
reverse order). Alternatively, |prepare| and |parent| can be set to
|
||||||
|
!NULL! and |child| to a function that calls !pthread_mutex_init! on
|
||||||
|
the global mutexes.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
!pthread_atfork! returns 0 on success and a non-zero error code on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
.TP
|
||||||
|
!ENOMEM!
|
||||||
|
insufficient memory available to register the handlers.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!fork!(2),
|
||||||
|
!pthread_mutex_lock!(3),
|
||||||
|
!pthread_mutex_unlock!(3).
|
221
linuxthreads/man/pthread_attr_init.man
Normal file
221
linuxthreads/man/pthread_attr_init.man
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
.TH PTHREAD_ATTR_INIT 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_attr_destroy
|
||||||
|
.XREF pthread_attr_setdetachstate
|
||||||
|
.XREF pthread_attr_getdetachstate
|
||||||
|
.XREF pthread_attr_setschedparam
|
||||||
|
.XREF pthread_attr_getschedparam
|
||||||
|
.XREF pthread_attr_setschedpolicy
|
||||||
|
.XREF pthread_attr_getschedpolicy
|
||||||
|
.XREF pthread_attr_setinheritsched
|
||||||
|
.XREF pthread_attr_getinheritsched
|
||||||
|
.XREF pthread_attr_setscope
|
||||||
|
.XREF pthread_attr_getscope
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_attr_init, pthread_attr_destroy, pthread_attr_setdetachstate, pthread_attr_getdetachstate, pthread_attr_setschedparam, pthread_attr_getschedparam, pthread_attr_setschedpolicy, pthread_attr_getschedpolicy, pthread_attr_setinheritsched, pthread_attr_getinheritsched, pthread_attr_setscope, pthread_attr_getscope \- thread creation attributes
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_attr_init(pthread_attr_t *attr);
|
||||||
|
|
||||||
|
int pthread_attr_destroy(pthread_attr_t *attr);
|
||||||
|
|
||||||
|
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
|
||||||
|
|
||||||
|
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
|
||||||
|
|
||||||
|
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
|
||||||
|
|
||||||
|
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
|
||||||
|
|
||||||
|
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
|
||||||
|
|
||||||
|
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
|
||||||
|
|
||||||
|
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
|
||||||
|
|
||||||
|
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
|
||||||
|
|
||||||
|
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
|
||||||
|
|
||||||
|
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Setting attributes for threads is achieved by filling a
|
||||||
|
thread attribute object |attr| of type !pthread_attr_t!, then passing it as
|
||||||
|
second argument to !pthread_create!(3). Passing !NULL! is equivalent to
|
||||||
|
passing a thread attribute object with all attributes set to their
|
||||||
|
default values.
|
||||||
|
|
||||||
|
!pthread_attr_init! initializes the thread attribute object |attr| and
|
||||||
|
fills it with default values for the attributes. (The default values
|
||||||
|
are listed below for each attribute.)
|
||||||
|
|
||||||
|
Each attribute |attrname| (see below for a list of all attributes) can
|
||||||
|
be individually set using the function !pthread_attr_set!|attrname|
|
||||||
|
and retrieved using the function !pthread_attr_get!|attrname|.
|
||||||
|
|
||||||
|
!pthread_attr_destroy! destroys a thread attribute object, which
|
||||||
|
must not be reused until it is reinitialized. !pthread_attr_destroy!
|
||||||
|
does nothing in the LinuxThreads implementation.
|
||||||
|
|
||||||
|
Attribute objects are consulted only when creating a new thread. The
|
||||||
|
same attribute object can be used for creating several
|
||||||
|
threads. Modifying an attribute object after a call to
|
||||||
|
!pthread_create! does not change the attributes of the thread
|
||||||
|
previously created.
|
||||||
|
|
||||||
|
The following thread attributes are supported:
|
||||||
|
|
||||||
|
.SS detachstate
|
||||||
|
|
||||||
|
Control whether the thread is created in the joinable state (value
|
||||||
|
!PTHREAD_CREATE_JOINABLE!) or in the detached state
|
||||||
|
(!PTHREAD_CREATE_DETACHED!).
|
||||||
|
|
||||||
|
Default value: !PTHREAD_CREATE_JOINABLE!.
|
||||||
|
|
||||||
|
In the joinable state, another thread can synchronize on the thread
|
||||||
|
termination and recover its termination code using !pthread_join!(3),
|
||||||
|
but some of the thread resources are kept allocated after the thread
|
||||||
|
terminates, and reclaimed only when another thread performs
|
||||||
|
!pthread_join!(3) on that thread.
|
||||||
|
|
||||||
|
In the detached state, the thread resources are immediately freed when
|
||||||
|
it terminates, but !pthread_join!(3) cannot be used to synchronize on
|
||||||
|
the thread termination.
|
||||||
|
|
||||||
|
A thread created in the joinable state can later be put in the
|
||||||
|
detached thread using !pthread_detach!(3).
|
||||||
|
|
||||||
|
.SS schedpolicy
|
||||||
|
|
||||||
|
Select the scheduling policy for the thread: one of
|
||||||
|
!SCHED_OTHER! (regular, non-realtime scheduling),
|
||||||
|
!SCHED_RR! (realtime, round-robin) or
|
||||||
|
!SCHED_FIFO! (realtime, first-in first-out). See
|
||||||
|
!sched_setpolicy!(2) for more information on scheduling policies.
|
||||||
|
|
||||||
|
Default value: !SCHED_OTHER!.
|
||||||
|
|
||||||
|
The realtime scheduling policies !SCHED_RR! and !SCHED_FIFO! are
|
||||||
|
available only to processes with superuser privileges.
|
||||||
|
|
||||||
|
The scheduling policy of a thread can be changed after creation with
|
||||||
|
!pthread_setschedparam!(3).
|
||||||
|
|
||||||
|
.SS schedparam
|
||||||
|
|
||||||
|
Contain the scheduling parameters (essentially, the scheduling
|
||||||
|
priority) for the thread. See !sched_setparam!(2) for more information
|
||||||
|
on scheduling parameters.
|
||||||
|
|
||||||
|
Default value: priority is 0.
|
||||||
|
|
||||||
|
This attribute is not significant if the scheduling policy is !SCHED_OTHER!;
|
||||||
|
it only matters for the realtime policies !SCHED_RR! and !SCHED_FIFO!.
|
||||||
|
|
||||||
|
The scheduling priority of a thread can be changed after creation with
|
||||||
|
!pthread_setschedparam!(3).
|
||||||
|
|
||||||
|
.SS inheritsched
|
||||||
|
|
||||||
|
Indicate whether the scheduling policy and scheduling parameters for
|
||||||
|
the newly created thread are determined by the values of the
|
||||||
|
|schedpolicy| and |schedparam| attributes (value
|
||||||
|
!PTHREAD_EXPLICIT_SCHED!) or are inherited from the parent thread
|
||||||
|
(value !PTHREAD_INHERIT_SCHED!).
|
||||||
|
|
||||||
|
Default value: !PTHREAD_EXPLICIT_SCHED!.
|
||||||
|
|
||||||
|
.SS scope
|
||||||
|
|
||||||
|
Define the scheduling contention scope for the created thread. The
|
||||||
|
only value supported in the LinuxThreads implementation is
|
||||||
|
!PTHREAD_SCOPE_SYSTEM!, meaning that the threads contend for CPU time
|
||||||
|
with all processes running on the machine. In particular, thread
|
||||||
|
priorities are interpreted relative to the priorities of all other
|
||||||
|
processes on the machine. The other value specified by the standard,
|
||||||
|
!PTHREAD_SCOPE_PROCESS!, means that scheduling contention occurs only
|
||||||
|
between the threads of the running process: thread priorities are
|
||||||
|
interpreted relative to the priorities of the other threads of the
|
||||||
|
process, regardless of the priorities of other processes.
|
||||||
|
!PTHREAD_SCOPE_PROCESS! is not supported in LinuxThreads.
|
||||||
|
|
||||||
|
Default value: !PTHREAD_SCOPE_SYSTEM!.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
All functions return 0 on success and a non-zero error code on error.
|
||||||
|
On success, the !pthread_attr_get!|attrname| functions also store the
|
||||||
|
current value of the attribute |attrname| in the location pointed to
|
||||||
|
by their second argument.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
The !pthread_attr_setdetachstate! function returns the following error
|
||||||
|
codes on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the specified |detachstate| is not one of !PTHREAD_CREATE_JOINABLE! or
|
||||||
|
!PTHREAD_CREATE_DETACHED!.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_attr_setschedparam! function returns the following error
|
||||||
|
codes on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the priority specified in |param| is outside the range of allowed
|
||||||
|
priorities for the scheduling policy currently in |attr|
|
||||||
|
(1 to 99 for !SCHED_FIFO! and !SCHED_RR!; 0 for !SCHED_OTHER!).
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_attr_setschedpolicy! function returns the following error
|
||||||
|
codes on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the specified |policy| is not one of !SCHED_OTHER!, !SCHED_FIFO!, or
|
||||||
|
!SCHED_RR!.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!ENOTSUP!
|
||||||
|
|policy| is !SCHED_FIFO! or !SCHED_RR!, and the effective user of the
|
||||||
|
calling process is not super-user.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_attr_setinheritsched! function returns the following error
|
||||||
|
codes on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the specified |inherit| is not one of !PTHREAD_INHERIT_SCHED! or
|
||||||
|
!PTHREAD_EXPLICIT_SCHED!.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_attr_setscope! function returns the following error
|
||||||
|
codes on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the specified |scope| is not one of !PTHREAD_SCOPE_SYSTEM! or
|
||||||
|
!PTHREAD_SCOPE_PROCESS!.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!ENOTSUP!
|
||||||
|
the specified |scope| is !PTHREAD_SCOPE_PROCESS! (not supported).
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_create!(3),
|
||||||
|
!pthread_join!(3),
|
||||||
|
!pthread_detach!(3),
|
||||||
|
!pthread_setschedparam!(3).
|
155
linuxthreads/man/pthread_cancel.man
Normal file
155
linuxthreads/man/pthread_cancel.man
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
.TH PTHREAD_CANCEL 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_setcancelstate
|
||||||
|
.XREF pthread_setcanceltype
|
||||||
|
.XREF pthread_testcancel
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_cancel, pthread_setcancelstate, pthread_setcanceltype, pthread_testcancel \- thread cancellation
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_cancel(pthread_t thread);
|
||||||
|
|
||||||
|
int pthread_setcancelstate(int state, int *oldstate);
|
||||||
|
|
||||||
|
int pthread_setcanceltype(int type, int *oldtype);
|
||||||
|
|
||||||
|
void pthread_testcancel(void);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Cancellation is the mechanism by which a thread can terminate the
|
||||||
|
execution of another thread. More precisely, a thread can send a
|
||||||
|
cancellation request to another thread. Depending on its settings, the
|
||||||
|
target thread can then either ignore the request, honor it
|
||||||
|
immediately, or defer it till it reaches a cancellation point.
|
||||||
|
|
||||||
|
When a thread eventually honors a cancellation request, it performs as
|
||||||
|
if !pthread_exit(PTHREAD_CANCELED)! has been called at that point:
|
||||||
|
all cleanup handlers are executed in reverse order, finalization
|
||||||
|
functions for thread-specific data are called, and finally the thread
|
||||||
|
stops executing with the return value !PTHREAD_CANCELED!. See
|
||||||
|
!pthread_exit!(3) for more information.
|
||||||
|
|
||||||
|
!pthread_cancel! sends a cancellation request to the thread denoted
|
||||||
|
by the |thread| argument.
|
||||||
|
|
||||||
|
!pthread_setcancelstate! changes the cancellation state for the
|
||||||
|
calling thread -- that is, whether cancellation requests are ignored
|
||||||
|
or not. The |state| argument is the new cancellation state: either
|
||||||
|
!PTHREAD_CANCEL_ENABLE! to enable cancellation, or
|
||||||
|
!PTHREAD_CANCEL_DISABLE! to disable cancellation (cancellation
|
||||||
|
requests are ignored). If |oldstate| is not !NULL!, the previous
|
||||||
|
cancellation state is stored in the location pointed to by |oldstate|,
|
||||||
|
and can thus be restored later by another call to
|
||||||
|
!pthread_setcancelstate!.
|
||||||
|
|
||||||
|
!pthread_setcanceltype! changes the type of responses to cancellation
|
||||||
|
requests for the calling thread: asynchronous (immediate) or deferred.
|
||||||
|
The |type| argument is the new cancellation type: either
|
||||||
|
!PTHREAD_CANCEL_ASYNCHRONOUS! to cancel the calling thread as soon as
|
||||||
|
the cancellation request is received, or !PTHREAD_CANCEL_DEFERRED! to
|
||||||
|
keep the cancellation request pending until the next cancellation
|
||||||
|
point. If |oldtype| is not !NULL!, the previous
|
||||||
|
cancellation state is stored in the location pointed to by |oldtype|,
|
||||||
|
and can thus be restored later by another call to
|
||||||
|
!pthread_setcanceltype!.
|
||||||
|
|
||||||
|
Threads are always created by !pthread_create!(3) with cancellation
|
||||||
|
enabled and deferred. That is, the initial cancellation state is
|
||||||
|
!PTHREAD_CANCEL_ENABLE! and the initial type is
|
||||||
|
!PTHREAD_CANCEL_DEFERRED!.
|
||||||
|
|
||||||
|
Cancellation points are those points in the program execution where a
|
||||||
|
test for pending cancellation requests is performed and cancellation
|
||||||
|
is executed if positive. The following POSIX threads functions
|
||||||
|
are cancellation points:
|
||||||
|
|
||||||
|
!pthread_join!(3)
|
||||||
|
.br
|
||||||
|
!pthread_cond_wait!(3)
|
||||||
|
.br
|
||||||
|
!pthread_cond_timedwait!(3)
|
||||||
|
.br
|
||||||
|
!pthread_testcancel!(3)
|
||||||
|
.br
|
||||||
|
!sem_wait!(3)
|
||||||
|
.br
|
||||||
|
!sigwait!(3)
|
||||||
|
|
||||||
|
All other POSIX threads functions are guaranteed not to be
|
||||||
|
cancellation points. That is, they never perform cancellation in
|
||||||
|
deferred cancellation mode.
|
||||||
|
|
||||||
|
!pthread_testcancel! does nothing except testing for pending
|
||||||
|
cancellation and executing it. Its purpose is to introduce explicit
|
||||||
|
checks for cancellation in long sequences of code that do not call
|
||||||
|
cancellation point functions otherwise.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
!pthread_cancel!, !pthread_setcancelstate! and
|
||||||
|
!pthread_setcanceltype! return 0 on success and a non-zero error code
|
||||||
|
on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
!pthread_cancel! returns the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!ESRCH!
|
||||||
|
no thread could be found corresponding to that specified by the |thread| ID.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
!pthread_setcancelstate! returns the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the |state| argument is not !PTHREAD_CANCEL_ENABLE! nor
|
||||||
|
!PTHREAD_CANCEL_DISABLE!
|
||||||
|
.RE
|
||||||
|
|
||||||
|
!pthread_setcanceltype! returns the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the |type| argument is not !PTHREAD_CANCEL_DEFERRED! nor
|
||||||
|
!PTHREAD_CANCEL_ASYNCHRONOUS!
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_exit!(3),
|
||||||
|
!pthread_cleanup_push!(3),
|
||||||
|
!pthread_cleanup_pop!(3).
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
|
||||||
|
POSIX specifies that a number of system calls (basically, all
|
||||||
|
system calls that may block, such as !read!(2), !write!(2), !wait!(2),
|
||||||
|
etc.) and library functions that may call these system calls (e.g.
|
||||||
|
!fprintf!(3)) are cancellation points. LinuxThreads is not yet
|
||||||
|
integrated enough with the C library to implement this, and thus none
|
||||||
|
of the C library functions is a cancellation point.
|
||||||
|
|
||||||
|
For system calls at least, there is a workaround. Cancellation
|
||||||
|
requests are transmitted to the target thread by sending it a
|
||||||
|
signal. That signal will interrupt all blocking system calls, causing
|
||||||
|
them to return immediately with the !EINTR! error. So, checking for
|
||||||
|
cancellation during a !read! system call, for instance, can be
|
||||||
|
achieved as follows:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_testcancel();
|
||||||
|
retcode = read(fd, buffer, length);
|
||||||
|
pthread_testcancel();
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
194
linuxthreads/man/pthread_cleanup_push.man
Normal file
194
linuxthreads/man/pthread_cleanup_push.man
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
.TH PTHREAD_CLEANUP 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_cleanup_pop
|
||||||
|
.XREF pthread_cleanup_push_defer_np
|
||||||
|
.XREF pthread_cleanup_pop_restore_np
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_cleanup_push, pthread_cleanup_pop, pthread_cleanup_push_defer_np, pthread_cleanup_pop_restore_np \- install and remove cleanup handlers
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
void pthread_cleanup_push(void (*routine) (void *), void *arg);
|
||||||
|
|
||||||
|
void pthread_cleanup_pop(int execute);
|
||||||
|
|
||||||
|
void pthread_cleanup_push_defer_np(void (*routine) (void *), void *arg);
|
||||||
|
|
||||||
|
void pthread_cleanup_pop_restore_np(int execute);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Cleanup handlers are functions that get called when a thread
|
||||||
|
terminates, either by calling !pthread_exit!(3) or because of
|
||||||
|
cancellation. Cleanup handlers are installed and removed following a
|
||||||
|
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 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 used similarly to free blocks allocated
|
||||||
|
with !malloc!(3) or close file descriptors on thread termination.
|
||||||
|
|
||||||
|
!pthread_cleanup_push! installs the |routine| function with argument
|
||||||
|
|arg| as a cleanup handler. From this point on to the matching
|
||||||
|
!pthread_cleanup_pop!, the function |routine| will be called with
|
||||||
|
arguments |arg| when the thread terminates, either through !pthread_exit!(3)
|
||||||
|
or by cancellation. If several cleanup handlers are active at that
|
||||||
|
point, they are called in LIFO order: the most recently installed
|
||||||
|
handler is called first.
|
||||||
|
|
||||||
|
!pthread_cleanup_pop! removes the most recently installed cleanup
|
||||||
|
handler. If the |execute| argument is not 0, it also executes the
|
||||||
|
handler, by calling the |routine| function with arguments |arg|. If
|
||||||
|
the |execute| argument is 0, the handler is only removed but not
|
||||||
|
executed.
|
||||||
|
|
||||||
|
Matching pairs of !pthread_cleanup_push! and !pthread_cleanup_pop!
|
||||||
|
must occur in the same function, at the same level of block nesting.
|
||||||
|
Actually, !pthread_cleanup_push! and !pthread_cleanup_pop! are macros,
|
||||||
|
and the expansion of !pthread_cleanup_push! introduces an open brace !{!
|
||||||
|
with the matching closing brace !}! being introduced by the expansion
|
||||||
|
of the matching !pthread_cleanup_pop!.
|
||||||
|
|
||||||
|
!pthread_cleanup_push_defer_np! is a non-portable extension that
|
||||||
|
combines !pthread_cleanup_push! and !pthread_setcanceltype!(3).
|
||||||
|
It pushes a cleanup handler just as !pthread_cleanup_push! does, but
|
||||||
|
also saves the current cancellation type and sets it to deferred
|
||||||
|
cancellation. This ensures that the cleanup mechanism is effective
|
||||||
|
even if the thread was initially in asynchronous cancellation mode.
|
||||||
|
|
||||||
|
!pthread_cleanup_pop_restore_np! pops a cleanup handler introduced by
|
||||||
|
!pthread_cleanup_push_defer_np!, and restores the cancellation type to
|
||||||
|
its value at the time !pthread_cleanup_push_defer_np! was called.
|
||||||
|
|
||||||
|
!pthread_cleanup_push_defer_np! and !pthread_cleanup_pop_restore_np!
|
||||||
|
must occur in matching pairs, at the same level of block nesting.
|
||||||
|
|
||||||
|
The following sequence
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_cleanup_push_defer_np(routine, arg);
|
||||||
|
...
|
||||||
|
pthread_cleanup_pop_defer_np(execute);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
is functionally equivalent to (but more compact and more efficient than)
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
{ int oldtype;
|
||||||
|
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
|
||||||
|
pthread_cleanup_push(routine, arg);
|
||||||
|
...
|
||||||
|
pthread_cleanup_pop(execute);
|
||||||
|
pthread_setcanceltype(oldtype, NULL);
|
||||||
|
}
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_exit!(3),
|
||||||
|
!pthread_cancel!(3),
|
||||||
|
!pthread_setcanceltype!(3).
|
||||||
|
|
||||||
|
.SH EXAMPLE
|
||||||
|
|
||||||
|
Here is how to lock a mutex |mut| in such a way that it will be
|
||||||
|
unlocked if the thread is canceled while |mut| is locked:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
/* do some work */
|
||||||
|
pthread_mutex_unlock(&mut);
|
||||||
|
pthread_cleanup_pop(0);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
Equivalently, the last two lines can be replaced by
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_cleanup_pop(1);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
Notice that the code above is safe only in deferred cancellation mode
|
||||||
|
(see !pthread_setcanceltype!(3)). In asynchronous cancellation mode,
|
||||||
|
a cancellation can occur between !pthread_cleanup_push! and
|
||||||
|
!pthread_mutex_lock!, or between !pthread_mutex_unlock! and
|
||||||
|
!pthread_cleanup_pop!, resulting in both cases in the thread trying to
|
||||||
|
unlock a mutex not locked by the current thread. This is the main
|
||||||
|
reason why asynchronous cancellation is difficult to use.
|
||||||
|
|
||||||
|
If the code above must also work in asynchronous cancellation mode,
|
||||||
|
then it must switch to deferred mode for locking and unlocking the
|
||||||
|
mutex:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
|
||||||
|
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
/* do some work */
|
||||||
|
pthread_cleanup_pop(1);
|
||||||
|
pthread_setcanceltype(oldtype, NULL);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
The code above can be rewritten in a more compact and more
|
||||||
|
efficient way, using the non-portable functions
|
||||||
|
!pthread_cleanup_push_defer_np! and !pthread_cleanup_pop_restore_np!:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_cleanup_push_restore_np(pthread_mutex_unlock, (void *) &mut);
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
/* do some work */
|
||||||
|
pthread_cleanup_pop_restore_np(1);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
235
linuxthreads/man/pthread_cond_init.man
Normal file
235
linuxthreads/man/pthread_cond_init.man
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
.TH PTHREAD_COND 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_cond_signal
|
||||||
|
.XREF pthread_cond_broadcast
|
||||||
|
.XREF pthread_cond_wait
|
||||||
|
.XREF pthread_cond_timedwait
|
||||||
|
.XREF pthread_cond_destroy
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_cond_init, pthread_cond_destroy, pthread_cond_signal, pthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait \- operations on conditions
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
|
||||||
|
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
|
||||||
|
|
||||||
|
int pthread_cond_signal(pthread_cond_t *cond);
|
||||||
|
|
||||||
|
int pthread_cond_broadcast(pthread_cond_t *cond);
|
||||||
|
|
||||||
|
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
|
||||||
|
|
||||||
|
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
|
||||||
|
|
||||||
|
int pthread_cond_destroy(pthread_cond_t *cond);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
A condition (short for ``condition variable'') is a synchronization
|
||||||
|
device that allows threads to suspend execution and relinquish the
|
||||||
|
processors until some predicate on shared data is satisfied. The basic
|
||||||
|
operations on conditions are: signal the condition (when the
|
||||||
|
predicate becomes true), and wait for the condition, suspending the
|
||||||
|
thread execution until another thread signals the condition.
|
||||||
|
|
||||||
|
A condition variable must always be associated with a mutex, to avoid
|
||||||
|
the race condition where a thread prepares to wait on a condition
|
||||||
|
variable and another thread signals the condition just before the
|
||||||
|
first thread actually waits on it.
|
||||||
|
|
||||||
|
!pthread_cond_init! initializes the condition variable |cond|, using the
|
||||||
|
condition attributes specified in |cond_attr|, or default attributes
|
||||||
|
if |cond_attr| is !NULL!. The LinuxThreads implementation supports no
|
||||||
|
attributes for conditions, hence the |cond_attr| parameter is actually
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
Variables of type !pthread_cond_t! can also be initialized
|
||||||
|
statically, using the constant !PTHREAD_COND_INITIALIZER!.
|
||||||
|
|
||||||
|
!pthread_cond_signal! restarts one of the threads that are waiting on
|
||||||
|
the condition variable |cond|. If no threads are waiting on |cond|,
|
||||||
|
nothing happens. If several threads are waiting on |cond|, exactly one
|
||||||
|
is restarted, but it is not specified which.
|
||||||
|
|
||||||
|
!pthread_cond_broadcast! restarts all the threads that are waiting on
|
||||||
|
the condition variable |cond|. Nothing happens if no threads are
|
||||||
|
waiting on |cond|.
|
||||||
|
|
||||||
|
!pthread_cond_wait! atomically unlocks the |mutex| (as per
|
||||||
|
!pthread_unlock_mutex!) and waits for the condition variable |cond| to
|
||||||
|
be signaled. The thread execution is suspended and does not consume
|
||||||
|
any CPU time until the condition variable is signaled. The |mutex|
|
||||||
|
must be locked by the calling thread on entrance to
|
||||||
|
!pthread_cond_wait!. Before returning to the calling thread,
|
||||||
|
!pthread_cond_wait! re-acquires |mutex| (as per !pthread_lock_mutex!).
|
||||||
|
|
||||||
|
Unlocking the mutex and suspending on the condition variable is done
|
||||||
|
atomically. Thus, if all threads always acquire the mutex before
|
||||||
|
signaling the condition, this guarantees that the condition cannot be
|
||||||
|
signaled (and thus ignored) between the time a thread locks the mutex
|
||||||
|
and the time it waits on the condition variable.
|
||||||
|
|
||||||
|
!pthread_cond_timedwait! atomically unlocks |mutex| and waits on
|
||||||
|
|cond|, as !pthread_cond_wait! does, but it also bounds the duration
|
||||||
|
of the wait. If |cond| has not been signaled within the amount of time
|
||||||
|
specified by |abstime|, the mutex |mutex| is re-acquired and
|
||||||
|
!pthread_cond_timedwait! returns the error !ETIMEDOUT!.
|
||||||
|
The |abstime| parameter specifies an absolute time, with the same
|
||||||
|
origin as !time!(2) and !gettimeofday!(2): an |abstime| of 0
|
||||||
|
corresponds to 00:00:00 GMT, January 1, 1970.
|
||||||
|
|
||||||
|
!pthread_cond_destroy! destroys a condition variable, freeing the
|
||||||
|
resources it might hold. No threads must be waiting on the condition
|
||||||
|
variable on entrance to !pthread_cond_destroy!. In the LinuxThreads
|
||||||
|
implementation, no resources are associated with condition variables,
|
||||||
|
thus !pthread_cond_destroy! actually does nothing except checking that
|
||||||
|
the condition has no waiting threads.
|
||||||
|
|
||||||
|
.SH CANCELLATION
|
||||||
|
|
||||||
|
!pthread_cond_wait! and !pthread_cond_timedwait! are cancellation
|
||||||
|
points. If a thread is cancelled while suspended in one of these
|
||||||
|
functions, the thread immediately resumes execution, then locks again
|
||||||
|
the |mutex| argument to !pthread_cond_wait! and
|
||||||
|
!pthread_cond_timedwait!, and finally executes the cancellation.
|
||||||
|
Consequently, cleanup handlers are assured that |mutex| is locked when
|
||||||
|
they are called.
|
||||||
|
|
||||||
|
.SH "ASYNC-SIGNAL SAFETY"
|
||||||
|
|
||||||
|
The condition functions are not async-signal safe, and should not be
|
||||||
|
called from a signal handler. In particular, calling
|
||||||
|
!pthread_cond_signal! or !pthread_cond_broadcast! from a signal
|
||||||
|
handler may deadlock the calling thread.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
All condition variable functions return 0 on success and a non-zero
|
||||||
|
error code on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
!pthread_cond_init!, !pthread_cond_signal!, !pthread_cond_broadcast!,
|
||||||
|
and !pthread_cond_wait! never return an error code.
|
||||||
|
|
||||||
|
The !pthread_cond_timedwait! function returns the following error codes
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!ETIMEDOUT!
|
||||||
|
the condition variable was not signaled until the timeout specified by
|
||||||
|
|abstime|
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EINTR!
|
||||||
|
!pthread_cond_timedwait! was interrupted by a signal
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_cond_destroy! function returns the following error code
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EBUSY!
|
||||||
|
some threads are currently waiting on |cond|.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_condattr_init!(3),
|
||||||
|
!pthread_mutex_lock!(3),
|
||||||
|
!pthread_mutex_unlock!(3),
|
||||||
|
!gettimeofday!(2),
|
||||||
|
!nanosleep!(2).
|
||||||
|
|
||||||
|
.SH EXAMPLE
|
||||||
|
|
||||||
|
Consider two shared variables |x| and |y|, protected by the mutex |mut|,
|
||||||
|
and a condition variable |cond| that is to be signaled whenever |x|
|
||||||
|
becomes greater than |y|.
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
int x,y;
|
||||||
|
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
Waiting until |x| is greater than |y| is performed as follows:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
while (x <= y) {
|
||||||
|
pthread_cond_wait(&cond, &mut);
|
||||||
|
}
|
||||||
|
/* operate on x and y */
|
||||||
|
pthread_mutex_unlock(&mut);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
Modifications on |x| and |y| that may cause |x| to become greater than
|
||||||
|
|y| should signal the condition if needed:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
/* modify x and y */
|
||||||
|
if (x > y) pthread_mutex_broadcast(&cond);
|
||||||
|
pthread_mutex_unlock(&mut);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
If it can be proved that at most one waiting thread needs to be waken
|
||||||
|
up (for instance, if there are only two threads communicating through
|
||||||
|
|x| and |y|), !pthread_cond_signal! can be used as a slightly more
|
||||||
|
efficient alternative to !pthread_cond_broadcast!. In doubt, use
|
||||||
|
!pthread_cond_broadcast!.
|
||||||
|
|
||||||
|
To wait for |x| to becomes greater than |y| with a timeout of 5
|
||||||
|
seconds, do:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
struct timeval now;
|
||||||
|
struct timespec timeout;
|
||||||
|
int retcode;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
gettimeofday(&now);
|
||||||
|
timeout.tv_sec = now.tv_sec + 5;
|
||||||
|
timeout.tv_nsec = now.tv_usec * 1000;
|
||||||
|
retcode = 0;
|
||||||
|
while (x <= y && retcode != ETIMEDOUT) {
|
||||||
|
retcode = pthread_cond_timedwait(&cond, &mut, &timeout);
|
||||||
|
}
|
||||||
|
if (retcode == ETIMEDOUT) {
|
||||||
|
/* timeout occurred */
|
||||||
|
} else {
|
||||||
|
/* operate on x and y */
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&mut);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
39
linuxthreads/man/pthread_condattr_init.man
Normal file
39
linuxthreads/man/pthread_condattr_init.man
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
.TH PTHREAD_CONDATTR 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_condattr_destroy
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_condattr_init, pthread_condattr_destroy \- condition creation attributes
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_condattr_init(pthread_condattr_t *attr);
|
||||||
|
|
||||||
|
int pthread_condattr_destroy(pthread_condattr_t *attr);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Condition attributes can be specified at condition creation time, by passing a
|
||||||
|
condition attribute object as second argument to !pthread_cond_init!(3).
|
||||||
|
Passing !NULL! is equivalent to passing a condition attribute object with
|
||||||
|
all attributes set to their default values.
|
||||||
|
|
||||||
|
The LinuxThreads implementation supports no attributes for
|
||||||
|
conditions. The functions on condition attributes are included only
|
||||||
|
for compliance with the POSIX standard.
|
||||||
|
|
||||||
|
!pthread_condattr_init! initializes the condition attribute object
|
||||||
|
|attr| and fills it with default values for the attributes.
|
||||||
|
!pthread_condattr_destroy! destroys a condition attribute object,
|
||||||
|
which must not be reused until it is reinitialized. Both functions do
|
||||||
|
nothing in the LinuxThreads implementation.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
!pthread_condattr_init! and !pthread_condattr_destroy! always return 0.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_cond_init!(3).
|
46
linuxthreads/man/pthread_create.man
Normal file
46
linuxthreads/man/pthread_create.man
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
.TH PTHREAD_CREATE 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_create \- create a new thread
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_create! creates a new thread of control that executes
|
||||||
|
concurrently with the calling thread. The new thread applies the
|
||||||
|
function |start_routine| passing it |arg| as first argument. The new
|
||||||
|
thread terminates either explicitly, by calling !pthread_exit!(3),
|
||||||
|
or implicitly, by returning from the |start_routine| function. The
|
||||||
|
latter case is equivalent to calling !pthread_exit!(3) with the result
|
||||||
|
returned by |start_routine| as exit code.
|
||||||
|
|
||||||
|
The |attr| argument specifies thread attributes to be applied to the
|
||||||
|
new thread. See !pthread_attr_init!(3) for a complete list of thread
|
||||||
|
attributes. The |attr| argument can also be !NULL!, in which case
|
||||||
|
default attributes are used: the created thread is joinable (not
|
||||||
|
detached) and has default (non real-time) scheduling policy.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
On success, the identifier of the newly created thread is stored in
|
||||||
|
the location pointed by the |thread| argument, and a 0 is returned. On
|
||||||
|
error, a non-zero error code is returned.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
.TP
|
||||||
|
!EAGAIN!
|
||||||
|
not enough system resources to create a process for the new thread.
|
||||||
|
.TP
|
||||||
|
!EAGAIN!
|
||||||
|
more than !PTHREAD_THREADS_MAX! threads are already active.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_exit!(3),
|
||||||
|
!pthread_join!(3),
|
||||||
|
!pthread_detach!(3),
|
||||||
|
!pthread_attr_init!(3).
|
44
linuxthreads/man/pthread_detach.man
Normal file
44
linuxthreads/man/pthread_detach.man
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
.TH PTHREAD_DETACH 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_detach \- put a running thread in the detached state
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_detach(pthread_t th);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_detach! put the thread |th| in the detached state. This
|
||||||
|
guarantees that the memory resources consumed by |th| will be freed
|
||||||
|
immediately when |th| terminates. However, this prevents other threads
|
||||||
|
from synchronizing on the termination of |th| using !pthread_join!.
|
||||||
|
|
||||||
|
A thread can be created initially in the detached state, using the
|
||||||
|
!detachstate! attribute to !pthread_create!(3). In contrast,
|
||||||
|
!pthread_detach! applies to threads created in the joinable state, and
|
||||||
|
which need to be put in the detached state later.
|
||||||
|
|
||||||
|
After !pthread_detach! completes, subsequent attempts to perform
|
||||||
|
!pthread_join! on |th| will fail. If another thread is already joining
|
||||||
|
the thread |th| at the time !pthread_detach! is called,
|
||||||
|
!pthread_detach! does nothing and leaves |th| in the joinable state.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
On success, 0 is returned. On error, a non-zero error code is returned.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
.TP
|
||||||
|
!ESRCH!
|
||||||
|
No thread could be found corresponding to that specified by |th|
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the thread |th| is already in the detached state
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_create!(3),
|
||||||
|
!pthread_join!(3),
|
||||||
|
!pthread_attr_setdetachstate!(3).
|
23
linuxthreads/man/pthread_equal.man
Normal file
23
linuxthreads/man/pthread_equal.man
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.TH PTHREAD_EQUAL 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_equal \- compare two thread identifiers
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_equal(pthread_t thread1, pthread_t thread2);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_equal! determines if two thread identifiers refer to the same
|
||||||
|
thread.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
A non-zero value is returned if |thread1| and |thread2| refer to the
|
||||||
|
same thread. Otherwise, 0 is returned.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_self!(3).
|
32
linuxthreads/man/pthread_exit.man
Normal file
32
linuxthreads/man/pthread_exit.man
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.TH PTHREAD_EXIT 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_exit \- terminate the calling thread
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
void pthread_exit(void *retval);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_exit! terminates the execution of the calling thread.
|
||||||
|
All cleanup handlers that have been set for the calling thread with
|
||||||
|
!pthread_cleanup_push!(3) are executed in reverse order (the most
|
||||||
|
recently pushed handler is executed first). Finalization functions for
|
||||||
|
thread-specific data are then called for all keys that have non-!NULL!
|
||||||
|
values associated with them in the calling thread (see
|
||||||
|
!pthread_key_create!(3)). Finally, execution of the calling thread is
|
||||||
|
stopped.
|
||||||
|
|
||||||
|
The |retval| argument is the return value of the thread. It can be
|
||||||
|
consulted from another thread using !pthread_join!(3).
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
The !pthread_exit! function never returns.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_create!(3),
|
||||||
|
!pthread_join!(3).
|
70
linuxthreads/man/pthread_join.man
Normal file
70
linuxthreads/man/pthread_join.man
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
.TH PTHREAD_JOIN 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_join \- wait for termination of another thread
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_join(pthread_t th, void **thread_return);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_join! suspends the execution of the calling thread until the
|
||||||
|
thread identified by |th| terminates, either by calling !pthread_exit!(3)
|
||||||
|
or by being cancelled.
|
||||||
|
|
||||||
|
If |thread_return| is not !NULL!, the return value of |th| is stored
|
||||||
|
in the location pointed to by |thread_return|. The return value of
|
||||||
|
|th| is either the argument it gave to !pthread_exit!(3), or
|
||||||
|
!PTHREAD_CANCELED! if |th| was cancelled.
|
||||||
|
|
||||||
|
The joined thread !th! must be in the joinable state: it must not have
|
||||||
|
been detached using !pthread_detach!(3) or the
|
||||||
|
!PTHREAD_CREATE_DETACHED! attribute to !pthread_create!(3).
|
||||||
|
|
||||||
|
When a joinable thread terminates, its memory resources (thread
|
||||||
|
descriptor and stack) are not deallocated until another thread
|
||||||
|
performs !pthread_join! on it. Therefore, !pthread_join! must be
|
||||||
|
called once for each joinable thread created to avoid memory leaks.
|
||||||
|
|
||||||
|
At most one thread can wait for the termination of a given
|
||||||
|
thread. Calling !pthread_join! on a thread |th| on which another
|
||||||
|
thread is already waiting for termination returns an error.
|
||||||
|
|
||||||
|
.SH CANCELLATION
|
||||||
|
|
||||||
|
!pthread_join! is a cancellation point. If a thread is canceled while
|
||||||
|
suspended in !pthread_join!, the thread execution resumes immediately
|
||||||
|
and the cancellation is executed without waiting for the |th| thread
|
||||||
|
to terminate. If cancellation occurs during !pthread_join!, the |th|
|
||||||
|
thread remains not joined.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
On success, the return value of |th| is stored in the location pointed
|
||||||
|
to by |thread_return|, and 0 is returned. On error, a non-zero error
|
||||||
|
code is returned.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
.TP
|
||||||
|
!ESRCH!
|
||||||
|
No thread could be found corresponding to that specified by |th|.
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
The |th| thread has been detached.
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
Another thread is already waiting on termination of |th|.
|
||||||
|
.TP
|
||||||
|
!EDEADLK!
|
||||||
|
The |th| argument refers to the calling thread.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_exit!(3),
|
||||||
|
!pthread_detach!(3),
|
||||||
|
!pthread_create!(3),
|
||||||
|
!pthread_attr_setdetachstate!(3),
|
||||||
|
!pthread_cleanup_push!(3),
|
||||||
|
!pthread_key_create!(3).
|
151
linuxthreads/man/pthread_key_create.man
Normal file
151
linuxthreads/man/pthread_key_create.man
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
.TH PTHREAD_SPECIFIC 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_key_create, pthread_key_delete, pthread_setspecific, pthread_getspecific \- management of thread-specific data
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
|
||||||
|
|
||||||
|
int pthread_key_delete(pthread_key_t key);
|
||||||
|
|
||||||
|
int pthread_setspecific(pthread_key_t key, const void *pointer);
|
||||||
|
|
||||||
|
void * pthread_getspecific(pthread_key_t key);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Programs often need global or static variables that have different
|
||||||
|
values in different threads. Since threads share one memory space,
|
||||||
|
this cannot be achieved with regular variables. Thread-specific data
|
||||||
|
is the POSIX threads answer to this need.
|
||||||
|
|
||||||
|
Each thread possesses a private memory block, the thread-specific data
|
||||||
|
area, or TSD area for short. This area is indexed by TSD keys. The TSD
|
||||||
|
area associates values of type !void *! to TSD keys. TSD keys are
|
||||||
|
common to all threads, but the value associated with a given TSD key
|
||||||
|
can be different in each thread.
|
||||||
|
|
||||||
|
For concreteness, the TSD areas can be viewed as arrays of !void *!
|
||||||
|
pointers, TSD keys as integer indices into these arrays, and the value
|
||||||
|
of a TSD key as the value of the corresponding array element in the
|
||||||
|
calling thread.
|
||||||
|
|
||||||
|
When a thread is created, its TSD area initially associates !NULL!
|
||||||
|
with all keys.
|
||||||
|
|
||||||
|
!pthread_key_create! allocates a new TSD key. The key is stored in the
|
||||||
|
location pointed to by |key|. There is a limit of !PTHREAD_KEYS_MAX!
|
||||||
|
on the number of keys allocated at a given time. The value initially
|
||||||
|
associated with the returned key is !NULL! in all currently executing
|
||||||
|
threads.
|
||||||
|
|
||||||
|
The |destr_function| argument, if not !NULL!, specifies a destructor
|
||||||
|
function associated with the key. When a thread terminates via
|
||||||
|
!pthread_exit! or by cancellation, |destr_function| is called with
|
||||||
|
arguments the value associated with the key in that thread. The
|
||||||
|
|destr_function| is not called if that value is !NULL!. The order in
|
||||||
|
which destructor functions are called at thread termination time is
|
||||||
|
unspecified.
|
||||||
|
|
||||||
|
Before the destructor function is called, the !NULL! value is
|
||||||
|
associated with the key in the current thread. A destructor function
|
||||||
|
might, however, re-associate non-!NULL! values to that key or some
|
||||||
|
other key. To deal with this, if after all the destructors have been
|
||||||
|
called for all non-!NULL! values, there are still some non-!NULL!
|
||||||
|
values with associated destructors, then the process is repeated. The
|
||||||
|
LinuxThreads implementation stops the process after
|
||||||
|
!PTHREAD_DESTRUCTOR_ITERATIONS! iterations, even if some non-!NULL!
|
||||||
|
values with associated descriptors remain. Other implementations may
|
||||||
|
loop indefinitely.
|
||||||
|
|
||||||
|
!pthread_key_delete! deallocates a TSD key. It does not check whether
|
||||||
|
non-!NULL! values are associated with that key in the currently
|
||||||
|
executing threads, nor call the destructor function associated with
|
||||||
|
the key.
|
||||||
|
|
||||||
|
!pthread_setspecific! changes the value associated with |key| in the
|
||||||
|
calling thread, storing the given |pointer| instead.
|
||||||
|
|
||||||
|
!pthread_getspecific! returns the value currently associated with
|
||||||
|
|key| in the calling thread.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
!pthread_key_create!, !pthread_key_delete!, and !pthread_setspecific!
|
||||||
|
return 0 on success and a non-zero error code on failure. If
|
||||||
|
successful, !pthread_key_create! stores the newly allocated key in the
|
||||||
|
location pointed to by its |key| argument.
|
||||||
|
|
||||||
|
!pthread_getspecific! returns the value associated with |key| on
|
||||||
|
success, and !NULL! on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
!pthread_key_create! returns the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EAGAIN!
|
||||||
|
!PTHREAD_KEYS_MAX! keys are already allocated
|
||||||
|
.RE
|
||||||
|
|
||||||
|
!pthread_key_delete! and !pthread_setspecific! return the following
|
||||||
|
error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
|key| is not a valid, allocated TSD key
|
||||||
|
.RE
|
||||||
|
|
||||||
|
!pthread_getspecific! returns !NULL! if |key| is not a valid,
|
||||||
|
allocated TSD key.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
pthread_create(3), pthread_exit(3), pthread_testcancel(3).
|
||||||
|
|
||||||
|
.SH EXAMPLE
|
||||||
|
|
||||||
|
The following code fragment allocates a thread-specific array of 100
|
||||||
|
characters, with automatic reclaimation at thread exit:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
/* Key for the thread-specific buffer */
|
||||||
|
static pthread_key_t buffer_key;
|
||||||
|
|
||||||
|
/* Once-only initialisation of the key */
|
||||||
|
static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
/* Allocate the thread-specific buffer */
|
||||||
|
void buffer_alloc(void)
|
||||||
|
{
|
||||||
|
pthread_once(&buffer_key_once, buffer_key_alloc);
|
||||||
|
pthread_setspecific(buffer_key, malloc(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the thread-specific buffer */
|
||||||
|
char * get_buffer(void)
|
||||||
|
{
|
||||||
|
return (char *) pthread_getspecific(buffer_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate the key */
|
||||||
|
static void buffer_key_alloc()
|
||||||
|
{
|
||||||
|
pthread_key_create(&buffer_key, buffer_destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the thread-specific buffer */
|
||||||
|
static void buffer_destroy(void * buf)
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
40
linuxthreads/man/pthread_kill_other_threads_np.man
Normal file
40
linuxthreads/man/pthread_kill_other_threads_np.man
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
.TH PTHREAD_KILL_OTHER_THREADS_NP 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_kill_other_threads_np \- terminate all threads in program except calling thread
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
void pthread_kill_other_threads_np(void);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_kill_other_threads_np! is a non-portable LinuxThreads extension.
|
||||||
|
It causes all threads in the program to terminate immediately, except
|
||||||
|
the calling thread which proceeds normally. It is intended to be
|
||||||
|
called just before a thread calls one of the !exec! functions,
|
||||||
|
e.g. !execve!(2).
|
||||||
|
|
||||||
|
Termination of the other threads is not performed through
|
||||||
|
!pthread_cancel!(3) and completely bypasses the cancellation
|
||||||
|
mechanism. Hence, the current settings for cancellation state and
|
||||||
|
cancellation type are ignored, and the cleanup handlers are not
|
||||||
|
executed in the terminated threads.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!execve!(2),
|
||||||
|
!pthread_setcancelstate!(3),
|
||||||
|
!pthread_setcanceltype!(3),
|
||||||
|
!pthread_cancel!(3).
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
|
||||||
|
According to POSIX 1003.1c, a successful !exec*! in one of the threads
|
||||||
|
should terminate automatically all other threads in the program.
|
||||||
|
This behavior is not yet implemented in LinuxThreads.
|
||||||
|
Calling !pthread_kill_other_threads_np! before !exec*! achieves much
|
||||||
|
of the same behavior, except that if !exec*! ultimately fails, then
|
||||||
|
all other threads are already killed.
|
213
linuxthreads/man/pthread_mutex_init.man
Normal file
213
linuxthreads/man/pthread_mutex_init.man
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
.TH PTHREAD_MUTEX 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_mutex_lock
|
||||||
|
.XREF pthread_mutex_unlock
|
||||||
|
.XREF pthread_mutex_trylock
|
||||||
|
.XREF pthread_mutex_destroy
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destroy \- operations on mutexes
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
|
||||||
|
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
|
||||||
|
|
||||||
|
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
|
||||||
|
|
||||||
|
int pthread_mutex_lock(pthread_mutex_t *mutex));
|
||||||
|
|
||||||
|
int pthread_mutex_trylock(pthread_mutex_t *mutex);
|
||||||
|
|
||||||
|
int pthread_mutex_unlock(pthread_mutex_t *mutex);
|
||||||
|
|
||||||
|
int pthread_mutex_destroy(pthread_mutex_t *mutex);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
A mutex is a MUTual EXclusion device, and is useful for protecting
|
||||||
|
shared data structures from concurrent modifications, and implementing
|
||||||
|
critical sections and monitors.
|
||||||
|
|
||||||
|
A mutex has two possible states: unlocked (not owned by any thread),
|
||||||
|
and locked (owned by one thread). A mutex can never be owned by two
|
||||||
|
different threads simultaneously. A thread attempting to lock a mutex
|
||||||
|
that is already locked by another thread is suspended until the owning
|
||||||
|
thread unlocks the mutex first.
|
||||||
|
|
||||||
|
!pthread_mutex_init! initializes the mutex object pointed to by
|
||||||
|
|mutex| according to the mutex attributes specified in |mutexattr|.
|
||||||
|
If |mutexattr| is !NULL!, default attributes are used instead.
|
||||||
|
|
||||||
|
The LinuxThreads implementation supports only one mutex attributes,
|
||||||
|
the |mutex kind|, which is either ``fast'', ``recursive'', or
|
||||||
|
``error checking''. The kind of a mutex determines whether
|
||||||
|
it can be locked again by a thread that already owns it.
|
||||||
|
The default kind is ``fast''. See !pthread_mutexattr_init!(3) for more
|
||||||
|
information on mutex attributes.
|
||||||
|
|
||||||
|
Variables of type !pthread_mutex_t! can also be initialized
|
||||||
|
statically, using the constants !PTHREAD_MUTEX_INITIALIZER! (for fast
|
||||||
|
mutexes), !PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP! (for recursive
|
||||||
|
mutexes), and !PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP! (for error checking
|
||||||
|
mutexes).
|
||||||
|
|
||||||
|
!pthread_mutex_lock! locks the given mutex. If the mutex is currently
|
||||||
|
unlocked, it becomes locked and owned by the calling thread, and
|
||||||
|
!pthread_mutex_lock! returns immediately. If the mutex is already
|
||||||
|
locked by another thread, !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
|
||||||
|
!pthread_mutex_lock! depends on the kind of the mutex. If the mutex is
|
||||||
|
of the ``fast'' kind, the calling thread is suspended until the mutex
|
||||||
|
is unlocked, thus effectively causing the calling thread to
|
||||||
|
deadlock. If the mutex is of the ``error checking'' kind,
|
||||||
|
!pthread_mutex_lock! returns immediately with the error code !EDEADLK!.
|
||||||
|
If the mutex is of the ``recursive'' kind, !pthread_mutex_lock!
|
||||||
|
succeeds and returns immediately, recording the number of times the
|
||||||
|
calling thread has locked the mutex. An equal number of
|
||||||
|
!pthread_mutex_unlock! operations must be performed before the mutex
|
||||||
|
returns to the unlocked state.
|
||||||
|
|
||||||
|
!pthread_mutex_trylock! behaves identically to !pthread_mutex_lock!,
|
||||||
|
except that it does not block the calling thread if the mutex is
|
||||||
|
already locked by another thread (or by the calling thread in the case
|
||||||
|
of a ``fast'' mutex). Instead, !pthread_mutex_trylock! returns
|
||||||
|
immediately with the error code !EBUSY!.
|
||||||
|
|
||||||
|
!pthread_mutex_unlock! unlocks the given mutex. The mutex is assumed
|
||||||
|
to be locked and owned by the calling thread on entrance to
|
||||||
|
!pthread_mutex_unlock!. If the mutex is of the ``fast'' kind,
|
||||||
|
!pthread_mutex_unlock! always returns it to the unlocked state. If it
|
||||||
|
is of the ``recursive'' kind, it decrements the locking count of the
|
||||||
|
mutex (number of !pthread_mutex_lock! operations performed on it by
|
||||||
|
the calling thread), and only when this count reaches zero is the
|
||||||
|
mutex actually unlocked.
|
||||||
|
|
||||||
|
On ``error checking'' mutexes, !pthread_mutex_unlock! actually checks
|
||||||
|
at run-time that the mutex is locked on entrance, and that it was
|
||||||
|
locked by the same thread that is now calling !pthread_mutex_unlock!.
|
||||||
|
If these conditions are not met, an error code is returned and the
|
||||||
|
mutex remains unchanged. ``Fast'' and ``recursive'' mutexes perform
|
||||||
|
no such checks, thus allowing a locked mutex to be unlocked by a
|
||||||
|
thread other than its owner. This is non-portable behavior and must
|
||||||
|
not be relied upon.
|
||||||
|
|
||||||
|
!pthread_mutex_destroy! destroys a mutex object, freeing the resources
|
||||||
|
it might hold. The mutex must be unlocked on entrance. In the
|
||||||
|
LinuxThreads implementation, no resources are associated with mutex
|
||||||
|
objects, thus !pthread_mutex_destroy! actually does nothing except
|
||||||
|
checking that the mutex is unlocked.
|
||||||
|
|
||||||
|
.SH CANCELLATION
|
||||||
|
|
||||||
|
None of the mutex functions is a cancellation point, not even
|
||||||
|
!pthread_mutex_lock!, in spite of the fact that it can suspend a
|
||||||
|
thread for arbitrary durations. This way, the status of mutexes at
|
||||||
|
cancellation points is predictable, allowing cancellation handlers to
|
||||||
|
unlock precisely those mutexes that need to be unlocked before the
|
||||||
|
thread stops executing. Consequently, threads using deferred
|
||||||
|
cancellation should never hold a mutex for extended periods of time.
|
||||||
|
|
||||||
|
.SH "ASYNC-SIGNAL SAFETY"
|
||||||
|
|
||||||
|
The mutex functions are not async-signal safe. What this means is that
|
||||||
|
they should not be called from a signal handler. In particular,
|
||||||
|
calling !pthread_mutex_lock! or !pthread_mutex_unlock! from a signal
|
||||||
|
handler may deadlock the calling thread.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
!pthread_mutex_init! always returns 0. The other mutex functions
|
||||||
|
return 0 on success and a non-zero error code on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
The !pthread_mutex_lock! function returns the following error code
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the mutex has not been properly initialized.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EDEADLK!
|
||||||
|
the mutex is already locked by the calling thread
|
||||||
|
(``error checking'' mutexes only).
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_mutex_trylock! function returns the following error codes
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EBUSY!
|
||||||
|
the mutex could not be acquired because it was currently locked.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the mutex has not been properly initialized.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_mutex_unlock! function returns the following error code
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the mutex has not been properly initialized.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EPERM!
|
||||||
|
the calling thread does not own the mutex (``error checking'' mutexes only).
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_mutex_destroy! function returns the following error code
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EBUSY!
|
||||||
|
the mutex is currently locked.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_mutexattr_init!(3),
|
||||||
|
!pthread_mutexattr_setkind_np!(3),
|
||||||
|
!pthread_cancel!(3).
|
||||||
|
|
||||||
|
.SH EXAMPLE
|
||||||
|
|
||||||
|
A shared global variable |x| can be protected by a mutex as follows:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
int x;
|
||||||
|
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
All accesses and modifications to |x| should be bracketed by calls to
|
||||||
|
!pthread_mutex_lock! and !pthread_mutex_unlock! as follows:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.ft 3
|
||||||
|
.nf
|
||||||
|
.sp
|
||||||
|
pthread_mutex_lock(&mut);
|
||||||
|
/* operate on x */
|
||||||
|
pthread_mutex_unlock(&mut);
|
||||||
|
.ft
|
||||||
|
.LP
|
||||||
|
.RE
|
||||||
|
.fi
|
||||||
|
|
||||||
|
|
84
linuxthreads/man/pthread_mutexattr_init.man
Normal file
84
linuxthreads/man/pthread_mutexattr_init.man
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
.TH PTHREAD_MUTEXATTR 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_mutexattr_destroy
|
||||||
|
.XREF pthread_mutexattr_setkind_np
|
||||||
|
.XREF pthread_mutexattr_getkind_np
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_mutexattr_init, pthread_mutexattr_destroy, pthread_mutexattr_setkind_np, pthread_mutexattr_getkind_np \- mutex creation attributes
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
|
||||||
|
|
||||||
|
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
|
||||||
|
|
||||||
|
int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
|
||||||
|
|
||||||
|
int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
Mutex attributes can be specified at mutex creation time, by passing a
|
||||||
|
mutex attribute object as second argument to !pthread_mutex_init!(3).
|
||||||
|
Passing !NULL! is equivalent to passing a mutex attribute object with
|
||||||
|
all attributes set to their default values.
|
||||||
|
|
||||||
|
!pthread_mutexattr_init! initializes the mutex attribute object |attr|
|
||||||
|
and fills it with default values for the attributes.
|
||||||
|
|
||||||
|
!pthread_mutexattr_destroy! destroys a mutex attribute object, which
|
||||||
|
must not be reused until it is reinitialized. !pthread_mutexattr_destroy!
|
||||||
|
does nothing in the LinuxThreads implementation.
|
||||||
|
|
||||||
|
LinuxThreads supports only one mutex attribute: the mutex kind, which
|
||||||
|
is either !PTHREAD_MUTEX_FAST_NP! for ``fast'' mutexes,
|
||||||
|
!PTHREAD_MUTEX_RECURSIVE_NP! for ``recursive'' mutexes,
|
||||||
|
or !PTHREAD_MUTEX_ERRORCHECK_NP! for ``error checking'' mutexes.
|
||||||
|
As the !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
|
||||||
|
mutex it already owns with !pthread_mutex_lock!(3). If the mutex is of
|
||||||
|
the ``fast'' kind, !pthread_mutex_lock!(3) simply suspends the calling
|
||||||
|
thread forever. If the mutex is of the ``error checking'' kind,
|
||||||
|
!pthread_mutex_lock!(3) returns immediately with the error code
|
||||||
|
!EDEADLK!. If the mutex is of the ``recursive'' kind, the call to
|
||||||
|
!pthread_mutex_lock!(3) 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
|
||||||
|
!pthread_mutex_unlock!(3) the same number of times before the mutex
|
||||||
|
returns to the unlocked state.
|
||||||
|
|
||||||
|
The default mutex kind is ``fast'', that is, !PTHREAD_MUTEX_FAST_NP!.
|
||||||
|
|
||||||
|
!pthread_mutexattr_setkind_np! sets the mutex kind attribute in |attr|
|
||||||
|
to the value specified by |kind|.
|
||||||
|
|
||||||
|
!pthread_mutexattr_getkind_np! retrieves the current value of the
|
||||||
|
mutex kind attribute in |attr| and stores it in the location pointed
|
||||||
|
to by |kind|.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
!pthread_mutexattr_init!, !pthread_mutexattr_destroy! and
|
||||||
|
!pthread_mutexattr_getkind_np! always return 0.
|
||||||
|
|
||||||
|
!pthread_mutexattr_setkind_np! returns 0 on success and a non-zero
|
||||||
|
error code on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
On error, !pthread_mutexattr_setkind_np! returns the following error code:
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
|kind| is neither !PTHREAD_MUTEX_FAST_NP! nor !PTHREAD_MUTEX_RECURSIVE_NP!
|
||||||
|
nor !PTHREAD_MUTEX_ERRORCHECK_NP!
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_mutex_init!(3),
|
||||||
|
!pthread_mutex_lock!(3),
|
||||||
|
!pthread_mutex_unlock!(3).
|
34
linuxthreads/man/pthread_once.man
Normal file
34
linuxthreads/man/pthread_once.man
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
.TH PTHREAD_ONCE 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_once \- once-only initialization
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
pthread_once_t once_control = PTHREAD_ONCE_INIT;
|
||||||
|
|
||||||
|
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
The purpose of !pthread_once! is to ensure that a piece of
|
||||||
|
initialization code is executed at most once. The |once_control|
|
||||||
|
argument points to a static or extern variable statically initialized
|
||||||
|
to !PTHREAD_ONCE_INIT!.
|
||||||
|
|
||||||
|
The first time !pthread_once! is called with a given |once_control|
|
||||||
|
argument, it calls |init_routine| with no argument and changes the
|
||||||
|
value of the |once_control| variable to record that initialization has
|
||||||
|
been performed. Subsequent calls to !pthread_once! with the same
|
||||||
|
!once_control! argument do nothing.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
!pthread_once! always returns 0.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
None.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
23
linuxthreads/man/pthread_self.man
Normal file
23
linuxthreads/man/pthread_self.man
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.TH PTHREAD_SELF 3 LinuxThreads
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_self \- return identifier of current thread
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
pthread_t pthread_self(void);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
!pthread_self! return the thread identifier for the calling thread.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_equal!(3),
|
||||||
|
!pthread_join!(3),
|
||||||
|
!pthread_detach!(3),
|
||||||
|
!pthread_setschedparam!(3),
|
||||||
|
!pthread_getschedparam!(3).
|
||||||
|
|
79
linuxthreads/man/pthread_setschedparam.man
Normal file
79
linuxthreads/man/pthread_setschedparam.man
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
.TH PTHREAD_SETSCHEDPARAM 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_getschedparam
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_setschedparam, pthread_getschedparam \- control thread scheduling parameters
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param);
|
||||||
|
|
||||||
|
int pthread_getschedparam(pthread_t target_thread, int *policy, struct sched_param *param);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
!pthread_setschedparam! sets the scheduling parameters for the thread
|
||||||
|
|target_thread| as indicated by |policy| and |param|. |policy| can be
|
||||||
|
either !SCHED_OTHER! (regular, non-realtime scheduling), !SCHED_RR!
|
||||||
|
(realtime, round-robin) or !SCHED_FIFO! (realtime, first-in
|
||||||
|
first-out). |param| specifies the scheduling priority for the two
|
||||||
|
realtime policies. See !sched_setpolicy!(2) for more information on
|
||||||
|
scheduling policies.
|
||||||
|
|
||||||
|
The realtime scheduling policies !SCHED_RR! and !SCHED_FIFO! are
|
||||||
|
available only to processes with superuser privileges.
|
||||||
|
|
||||||
|
!pthread_getschedparam! retrieves the scheduling policy and scheduling
|
||||||
|
parameters for the thread |target_thread| and store them in the
|
||||||
|
locations pointed to by |policy| and |param|, respectively.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
!pthread_setschedparam! and !pthread_getschedparam! return 0 on
|
||||||
|
success and a non-zero error code on error.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
On error, !pthread_setschedparam! returns the following error codes:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
|policy| is not one of !SCHED_OTHER!, !SCHED_RR!, !SCHED_FIFO!
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
the priority value specified by |param| is not valid for the specified policy
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EPERM!
|
||||||
|
the calling process does not have superuser permissions
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!ESRCH!
|
||||||
|
the |target_thread| is invalid or has already terminated
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EFAULT!
|
||||||
|
|param| points outside the process memory space
|
||||||
|
.RE
|
||||||
|
|
||||||
|
On error, !pthread_getschedparam! returns the following error codes:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!ESRCH!
|
||||||
|
the |target_thread| is invalid or has already terminated
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EFAULT!
|
||||||
|
|policy| or |param| point outside the process memory space
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!sched_setscheduler!(2),
|
||||||
|
!sched_getscheduler!(2),
|
||||||
|
!sched_getparam!(2),
|
||||||
|
!pthread_attr_setschedpolicy!(3),
|
||||||
|
!pthread_attr_setschedparam!(3).
|
123
linuxthreads/man/pthread_sigmask.man
Normal file
123
linuxthreads/man/pthread_sigmask.man
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
.TH PTHREAD_SIGNAL 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF pthread_kill
|
||||||
|
.XREF sigwait
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
pthread_sigmask, pthread_kill, sigwait \- handling of signals in threads
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <pthread.h>
|
||||||
|
.br
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask);
|
||||||
|
|
||||||
|
int pthread_kill(pthread_t thread, int signo);
|
||||||
|
|
||||||
|
int sigwait(const sigset_t *set, int *sig);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
|
||||||
|
!pthread_sigmask! changes the signal mask for the calling thread as
|
||||||
|
described by the |how| and |newmask| arguments. If |oldmask| is not
|
||||||
|
!NULL!, the previous signal mask is stored in the location pointed to
|
||||||
|
by |oldmask|.
|
||||||
|
|
||||||
|
The meaning of the |how| and |newmask| arguments is the same as for
|
||||||
|
!sigprocmask!(2). If |how| is !SIG_SETMASK!, the signal mask is set to
|
||||||
|
|newmask|. If |how| is !SIG_BLOCK!, the signals specified to |newmask|
|
||||||
|
are added to the current signal mask. If |how| is !SIG_UNBLOCK!, the
|
||||||
|
signals specified to |newmask| are removed from the current signal
|
||||||
|
mask.
|
||||||
|
|
||||||
|
Recall that signal masks are set on a per-thread basis, but signal
|
||||||
|
actions and signal handlers, as set with !sigaction!(2), are shared
|
||||||
|
between all threads.
|
||||||
|
|
||||||
|
!pthread_kill! send signal number |signo| to the thread
|
||||||
|
|thread|. The signal is delivered and handled as described in
|
||||||
|
!kill!(2).
|
||||||
|
|
||||||
|
!sigwait! suspends the calling thread until one of the signals
|
||||||
|
in |set| is delivered to the calling thread. It then stores the number
|
||||||
|
of the signal received in the location pointed to by |sig| and
|
||||||
|
returns. The signals in |set| must be blocked and not ignored on
|
||||||
|
entrance to !sigwait!. If the delivered signal has a signal handler
|
||||||
|
function attached, that function is |not| called.
|
||||||
|
|
||||||
|
.SH CANCELLATION
|
||||||
|
|
||||||
|
!sigwait! is a cancellation point.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
On success, 0 is returned. On failure, a non-zero error code is returned.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
The !pthread_sigmask! function returns the following error codes
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
|how| is not one of !SIG_SETMASK!, !SIG_BLOCK!, or !SIG_UNBLOCK!
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!EFAULT!
|
||||||
|
|newmask| or |oldmask| point to invalid addresses
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !pthread_kill! function returns the following error codes
|
||||||
|
on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
|signo| is not a valid signal number
|
||||||
|
|
||||||
|
.TP
|
||||||
|
!ESRCH!
|
||||||
|
the thread |thread| does not exist (e.g. it has already terminated)
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !sigwait! function never returns an error.
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!sigprocmask!(2),
|
||||||
|
!kill!(2),
|
||||||
|
!sigaction!(2),
|
||||||
|
!sigsuspend!(2).
|
||||||
|
|
||||||
|
.SH NOTES
|
||||||
|
|
||||||
|
For !sigwait! to work reliably, the signals being waited for must be
|
||||||
|
blocked in all threads, not only in the calling thread, since
|
||||||
|
otherwise the POSIX semantics for signal delivery do not guarantee
|
||||||
|
that it's the thread doing the !sigwait! that will receive the signal.
|
||||||
|
The best way to achieve this is block those signals before any threads
|
||||||
|
are created, and never unblock them in the program other than by
|
||||||
|
calling !sigwait!.
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
|
||||||
|
Signal handling in LinuxThreads departs significantly from the POSIX
|
||||||
|
standard. According to the standard, ``asynchronous'' (external)
|
||||||
|
signals are addressed to the whole process (the collection of all
|
||||||
|
threads), which then delivers them to one particular thread. The
|
||||||
|
thread that actually receives the signal is any thread that does
|
||||||
|
not currently block the signal.
|
||||||
|
|
||||||
|
In LinuxThreads, each thread is actually a kernel process with its own
|
||||||
|
PID, so external signals are always directed to one particular thread.
|
||||||
|
If, for instance, another thread is blocked in !sigwait! on that
|
||||||
|
signal, it will not be restarted.
|
||||||
|
|
||||||
|
The LinuxThreads implementation of !sigwait! installs dummy signal
|
||||||
|
handlers for the signals in |set| for the duration of the wait. Since
|
||||||
|
signal handlers are shared between all threads, other threads must not
|
||||||
|
attach their own signal handlers to these signals, or alternatively
|
||||||
|
they should all block these signals (which is recommended anyway --
|
||||||
|
see the Notes section).
|
132
linuxthreads/man/sem_init.man
Normal file
132
linuxthreads/man/sem_init.man
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
.TH SEMAPHORES 3 LinuxThreads
|
||||||
|
|
||||||
|
.XREF sem_wait
|
||||||
|
.XREF sem_trywait
|
||||||
|
.XREF sem_post
|
||||||
|
.XREF sem_getvalue
|
||||||
|
.XREF sem_destroy
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
sem_init, sem_wait, sem_trywait, sem_post, sem_getvalue, sem_destroy \- operations on semaphores
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
int sem_init(sem_t *sem, int pshared, unsigned int value);
|
||||||
|
|
||||||
|
int sem_wait(sem_t * sem);
|
||||||
|
|
||||||
|
int sem_trywait(sem_t * sem);
|
||||||
|
|
||||||
|
int sem_post(sem_t * sem);
|
||||||
|
|
||||||
|
int sem_getvalue(sem_t * sem, int * sval);
|
||||||
|
|
||||||
|
int sem_destroy(sem_t * sem);
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This manual page documents POSIX 1003.1b semaphores, not to be
|
||||||
|
confused with SystemV semaphores as described in !ipc!(5), !semctl!(2)
|
||||||
|
and !semop!(2).
|
||||||
|
|
||||||
|
Semaphores are counters for resources shared between threads. The
|
||||||
|
basic operations on semaphores are: increment the counter atomically,
|
||||||
|
and wait until the counter is non-null and decrement it atomically.
|
||||||
|
|
||||||
|
!sem_init! initializes the semaphore object pointed to by |sem|. The
|
||||||
|
count associated with the semaphore is set initially to |value|. The
|
||||||
|
|pshared| argument indicates whether the semaphore is local to the
|
||||||
|
current process (|pshared| is zero) or is to be shared between several
|
||||||
|
processes (|pshared| is not zero). LinuxThreads currently does not
|
||||||
|
support process-shared semaphores, thus !sem_init! always returns with
|
||||||
|
error !ENOSYS! if |pshared| is not zero.
|
||||||
|
|
||||||
|
!sem_wait! suspends the calling thread until the semaphore pointed to
|
||||||
|
by |sem| has non-zero count. It then atomically decreases the
|
||||||
|
semaphore count.
|
||||||
|
|
||||||
|
!sem_trywait! is a non-blocking variant of !sem_wait!. If the
|
||||||
|
semaphore pointed to by |sem| has non-zero count, the count is
|
||||||
|
atomically decreased and !sem_trywait! immediately returns 0.
|
||||||
|
If the semaphore count is zero, !sem_trywait! immediately returns with
|
||||||
|
error !EAGAIN!.
|
||||||
|
|
||||||
|
!sem_post! atomically increases the count of the semaphore pointed to
|
||||||
|
by |sem|. This function never blocks and can safely be used in
|
||||||
|
asynchronous signal handlers.
|
||||||
|
|
||||||
|
!sem_getvalue! stores in the location pointed to by |sval| the current
|
||||||
|
count of the semaphore |sem|.
|
||||||
|
|
||||||
|
!sem_destroy! destroys a semaphore object, freeing the resources it
|
||||||
|
might hold. No threads should be waiting on the semaphore at the time
|
||||||
|
!sem_destroy! is called. In the LinuxThreads implementation, no
|
||||||
|
resources are associated with semaphore objects, thus !sem_destroy!
|
||||||
|
actually does nothing except checking that no thread is waiting on the
|
||||||
|
semaphore.
|
||||||
|
|
||||||
|
.SH CANCELLATION
|
||||||
|
|
||||||
|
!sem_wait! is a cancellation point.
|
||||||
|
|
||||||
|
.SH "ASYNC-SIGNAL SAFETY"
|
||||||
|
|
||||||
|
On processors supporting atomic compare-and-swap (Intel 486, Pentium
|
||||||
|
and later, Alpha, PowerPC, MIPS II, Motorola 68k), the !sem_post!
|
||||||
|
function is async-signal safe and can therefore be
|
||||||
|
called from signal handlers. This is the only thread synchronization
|
||||||
|
function provided by POSIX threads that is async-signal safe.
|
||||||
|
|
||||||
|
On the Intel 386 and the Sparc, the current LinuxThreads
|
||||||
|
implementation of !sem_post! is not async-signal safe by lack of the
|
||||||
|
required atomic operations.
|
||||||
|
|
||||||
|
.SH "RETURN VALUE"
|
||||||
|
|
||||||
|
The !sem_wait! and !sem_getvalue! functions always return 0.
|
||||||
|
All other semaphore functions return 0 on success and -1 on error, in
|
||||||
|
addition to writing an error code in !errno!.
|
||||||
|
|
||||||
|
.SH ERRORS
|
||||||
|
|
||||||
|
The !sem_init! function sets !errno! to the following codes on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EINVAL!
|
||||||
|
|value| exceeds the maximal counter value !SEM_VALUE_MAX!
|
||||||
|
.TP
|
||||||
|
!ENOSYS!
|
||||||
|
|pshared| is not zero
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !sem_trywait! function sets !errno! to the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EAGAIN!
|
||||||
|
the semaphore count is currently 0
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !sem_post! function sets !errno! to the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!ERANGE!
|
||||||
|
after incrementation, the semaphore value would exceed !SEM_VALUE_MAX!
|
||||||
|
(the semaphore count is left unchanged in this case)
|
||||||
|
.RE
|
||||||
|
|
||||||
|
The !sem_destroy! function sets !errno! to the following error code on error:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
!EBUSY!
|
||||||
|
some threads are currently blocked waiting on the semaphore.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
!pthread_mutex_init!(3),
|
||||||
|
!pthread_cond_init!(3),
|
||||||
|
!pthread_cancel!(3),
|
||||||
|
!ipc!(5).
|
||||||
|
|
68
linuxthreads/man/troffprepro
Executable file
68
linuxthreads/man/troffprepro
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/local/bin/perl
|
||||||
|
|
||||||
|
$insynopsis = 0;
|
||||||
|
|
||||||
|
open(INPUT, $ARGV[0]) || die("cannot open $ARGV[0]");
|
||||||
|
open(OUTPUT, "> $ARGV[1]") || die("cannot create $ARGV[1]");
|
||||||
|
|
||||||
|
select(OUTPUT);
|
||||||
|
|
||||||
|
line:
|
||||||
|
while(<INPUT>) {
|
||||||
|
if (/^\.XREF (.*)$/) {
|
||||||
|
$xref = $1;
|
||||||
|
$_ = $ARGV[1];
|
||||||
|
m/^.*\.(([1-8]).*)$/;
|
||||||
|
$suffix = $1;
|
||||||
|
$extension = $2;
|
||||||
|
open(XREF, "> $xref.$suffix");
|
||||||
|
print XREF ".so man$extension/$ARGV[1]\n";
|
||||||
|
close(XREF);
|
||||||
|
next line;
|
||||||
|
}
|
||||||
|
if (/^\.SH/) {
|
||||||
|
$insynopsis = /SYNOPSIS/;
|
||||||
|
print $_;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if ($insynopsis) {
|
||||||
|
if (/^#/) {
|
||||||
|
print ".B ", $_;
|
||||||
|
}
|
||||||
|
elsif (/^[a-z]/) {
|
||||||
|
chop;
|
||||||
|
# if (m/^([a-zA-Z][a-zA-Z0-9_]*\s+[a-zA-Z][a-zA-Z0-9_]*)\(/) {
|
||||||
|
# print ".B \"", $1, "\"\n";
|
||||||
|
# $_ = '(' . $';
|
||||||
|
# }
|
||||||
|
# s/([a-zA-Z][a-zA-Z0-9_]*)(\s*[,()=])/" \1 "\2/g;
|
||||||
|
s/([ *])([a-zA-Z][a-zA-Z0-9_]*)(\s*[,)=])/\1" \2 "\3/g;
|
||||||
|
print ".BI \"", $_, "\"\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print $_;
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
chop;
|
||||||
|
s/!([^!]+)!\|([^|]+)\|([^\s]*)\s*/\n.BI "\1" "\2\3"\n/g;
|
||||||
|
s/([!|])([^!|]+)\1([^\s]*)\s*/do subst($1,$2,$3)/eg;
|
||||||
|
s/^\n+//;
|
||||||
|
s/\n+$//;
|
||||||
|
s/\n\n+/\n/g;
|
||||||
|
print $_, "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
close(INPUT);
|
||||||
|
close(OUTPUT);
|
||||||
|
|
||||||
|
sub subst {
|
||||||
|
local ($a, $b, $c) = @_;
|
||||||
|
if ($c) {
|
||||||
|
"\n" . ($a eq "!" ? ".BR " : ".IR ") . "\"$b\" $c\n"
|
||||||
|
} else {
|
||||||
|
"\n" . ($a eq "!" ? ".B " : ".I ") . "\"$b\"\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
400
linuxthreads/manager.c
Normal file
400
linuxthreads/manager.c
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* The "thread manager" thread: manages creation and termination of threads */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/select.h> /* for select */
|
||||||
|
#include <sys/mman.h> /* for mmap */
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/wait.h> /* for waitpid macros */
|
||||||
|
#include <linux/tasks.h>
|
||||||
|
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
/* Array of active threads. Entry 0 is reserved for the initial thread. */
|
||||||
|
|
||||||
|
struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] =
|
||||||
|
{ { 0, &__pthread_initial_thread}, /* All NULLs */ };
|
||||||
|
|
||||||
|
/* Mapping from stack segment to thread descriptor. */
|
||||||
|
/* Stack segment numbers are also indices into the __pthread_handles array. */
|
||||||
|
/* Stack segment number 0 is reserved for the initial thread. */
|
||||||
|
|
||||||
|
static inline pthread_descr thread_segment(int seg)
|
||||||
|
{
|
||||||
|
return (pthread_descr)(THREAD_STACK_START_ADDRESS - (seg - 1) * STACK_SIZE)
|
||||||
|
- 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flag set in signal handler to record child termination */
|
||||||
|
|
||||||
|
static volatile int terminated_children = 0;
|
||||||
|
|
||||||
|
/* Flag set when the initial thread is blocked on pthread_exit waiting
|
||||||
|
for all other threads to terminate */
|
||||||
|
|
||||||
|
static int main_thread_exiting = 0;
|
||||||
|
|
||||||
|
/* Counter used to generate unique thread identifier.
|
||||||
|
Thread identifier is pthread_threads_counter + segment. */
|
||||||
|
|
||||||
|
static pthread_t pthread_threads_counter = 0;
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
|
||||||
|
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
void * (*start_routine)(void *), void *arg,
|
||||||
|
sigset_t *mask, int father_pid);
|
||||||
|
static void pthread_handle_free(pthread_descr th);
|
||||||
|
static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode);
|
||||||
|
static void pthread_reap_children(void);
|
||||||
|
static void pthread_kill_all_threads(int sig, int main_thread_also);
|
||||||
|
|
||||||
|
/* The server thread managing requests for thread creation and termination */
|
||||||
|
|
||||||
|
int __pthread_manager(void *arg)
|
||||||
|
{
|
||||||
|
int reqfd = (int)arg;
|
||||||
|
sigset_t mask;
|
||||||
|
fd_set readfds;
|
||||||
|
struct timeval timeout;
|
||||||
|
int n;
|
||||||
|
struct pthread_request request;
|
||||||
|
|
||||||
|
/* If we have special thread_self processing, initialize it. */
|
||||||
|
#ifdef INIT_THREAD_SELF
|
||||||
|
INIT_THREAD_SELF(&__pthread_manager_thread);
|
||||||
|
#endif
|
||||||
|
/* Set the error variable. */
|
||||||
|
__pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno;
|
||||||
|
__pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno;
|
||||||
|
/* Block all signals except PTHREAD_SIG_RESTART */
|
||||||
|
sigfillset(&mask);
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_RESTART);
|
||||||
|
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||||
|
/* Enter server loop */
|
||||||
|
while(1) {
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(reqfd, &readfds);
|
||||||
|
timeout.tv_sec = 2;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
n = __select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
|
||||||
|
/* Check for termination of the main thread */
|
||||||
|
if (getppid() == 1) {
|
||||||
|
pthread_kill_all_threads(SIGKILL, 0);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
/* Check for dead children */
|
||||||
|
if (terminated_children) {
|
||||||
|
terminated_children = 0;
|
||||||
|
pthread_reap_children();
|
||||||
|
}
|
||||||
|
/* Read and execute request */
|
||||||
|
if (n == 1 && FD_ISSET(reqfd, &readfds)) {
|
||||||
|
n = __libc_read(reqfd, (char *)&request, sizeof(request));
|
||||||
|
ASSERT(n == sizeof(request));
|
||||||
|
switch(request.req_kind) {
|
||||||
|
case REQ_CREATE:
|
||||||
|
request.req_thread->p_retcode =
|
||||||
|
pthread_handle_create((pthread_t *) &request.req_thread->p_retval,
|
||||||
|
request.req_args.create.attr,
|
||||||
|
request.req_args.create.fn,
|
||||||
|
request.req_args.create.arg,
|
||||||
|
&request.req_args.create.mask,
|
||||||
|
request.req_thread->p_pid);
|
||||||
|
restart(request.req_thread);
|
||||||
|
break;
|
||||||
|
case REQ_FREE:
|
||||||
|
pthread_handle_free(request.req_args.free.thread);
|
||||||
|
break;
|
||||||
|
case REQ_PROCESS_EXIT:
|
||||||
|
pthread_handle_exit(request.req_thread,
|
||||||
|
request.req_args.exit.code);
|
||||||
|
break;
|
||||||
|
case REQ_MAIN_THREAD_EXIT:
|
||||||
|
main_thread_exiting = 1;
|
||||||
|
if (__pthread_main_thread->p_nextlive == __pthread_main_thread) {
|
||||||
|
restart(__pthread_main_thread);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process creation */
|
||||||
|
|
||||||
|
static int pthread_start_thread(void *arg)
|
||||||
|
{
|
||||||
|
pthread_descr self = (pthread_descr) arg;
|
||||||
|
void * outcome;
|
||||||
|
/* Initialize special thread_self processing, if any. */
|
||||||
|
#ifdef INIT_THREAD_SELF
|
||||||
|
INIT_THREAD_SELF(self);
|
||||||
|
#endif
|
||||||
|
/* Make sure our pid field is initialized, just in case we get there
|
||||||
|
before our father has initialized it. */
|
||||||
|
self->p_pid = __getpid();
|
||||||
|
/* Initial signal mask is that of the creating thread. (Otherwise,
|
||||||
|
we'd just inherit the mask of the thread manager.) */
|
||||||
|
sigprocmask(SIG_SETMASK, &self->p_start_args.mask, NULL);
|
||||||
|
/* Set the scheduling policy and priority for the new thread, if needed */
|
||||||
|
if (self->p_start_args.schedpolicy >= 0)
|
||||||
|
__sched_setscheduler(self->p_pid, self->p_start_args.schedpolicy,
|
||||||
|
&self->p_start_args.schedparam);
|
||||||
|
/* Run the thread code */
|
||||||
|
outcome = self->p_start_args.start_routine(self->p_start_args.arg);
|
||||||
|
/* Exit with the given return value */
|
||||||
|
pthread_exit(outcome);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
void * (*start_routine)(void *), void *arg,
|
||||||
|
sigset_t * mask, int father_pid)
|
||||||
|
{
|
||||||
|
size_t sseg;
|
||||||
|
int pid;
|
||||||
|
pthread_descr new_thread;
|
||||||
|
pthread_t new_thread_id;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Find a free stack segment for the current stack */
|
||||||
|
for (sseg = 1; ; sseg++) {
|
||||||
|
if (sseg >= PTHREAD_THREADS_MAX) return EAGAIN;
|
||||||
|
if (__pthread_handles[sseg].h_descr != NULL) continue;
|
||||||
|
new_thread = thread_segment(sseg);
|
||||||
|
/* Allocate space for stack and thread descriptor. */
|
||||||
|
if (mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
|
||||||
|
INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||||
|
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0)
|
||||||
|
!= MAP_FAILED) break;
|
||||||
|
/* It seems part of this segment is already mapped. Try the next. */
|
||||||
|
}
|
||||||
|
/* Allocate new thread identifier */
|
||||||
|
pthread_threads_counter += PTHREAD_THREADS_MAX;
|
||||||
|
new_thread_id = sseg + pthread_threads_counter;
|
||||||
|
/* Initialize the thread descriptor */
|
||||||
|
new_thread->p_nextwaiting = NULL;
|
||||||
|
new_thread->p_tid = new_thread_id;
|
||||||
|
new_thread->p_priority = 0;
|
||||||
|
new_thread->p_spinlock = &(__pthread_handles[sseg].h_spinlock);
|
||||||
|
new_thread->p_signal = 0;
|
||||||
|
new_thread->p_signal_jmp = NULL;
|
||||||
|
new_thread->p_cancel_jmp = NULL;
|
||||||
|
new_thread->p_terminated = 0;
|
||||||
|
new_thread->p_detached = attr == NULL ? 0 : attr->detachstate;
|
||||||
|
new_thread->p_exited = 0;
|
||||||
|
new_thread->p_retval = NULL;
|
||||||
|
new_thread->p_joining = NULL;
|
||||||
|
new_thread->p_cleanup = NULL;
|
||||||
|
new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE;
|
||||||
|
new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED;
|
||||||
|
new_thread->p_canceled = 0;
|
||||||
|
new_thread->p_errnop = &new_thread->p_errno;
|
||||||
|
new_thread->p_errno = 0;
|
||||||
|
new_thread->p_h_errnop = &new_thread->p_h_errno;
|
||||||
|
new_thread->p_h_errno = 0;
|
||||||
|
for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
|
||||||
|
new_thread->p_specific[i] = NULL;
|
||||||
|
/* Initialize the thread handle */
|
||||||
|
__pthread_handles[sseg].h_spinlock = 0; /* should already be 0 */
|
||||||
|
__pthread_handles[sseg].h_descr = new_thread;
|
||||||
|
/* Determine scheduling parameters for the thread */
|
||||||
|
new_thread->p_start_args.schedpolicy = -1;
|
||||||
|
if (attr != NULL) {
|
||||||
|
switch(attr->inheritsched) {
|
||||||
|
case PTHREAD_EXPLICIT_SCHED:
|
||||||
|
new_thread->p_start_args.schedpolicy = attr->schedpolicy;
|
||||||
|
new_thread->p_start_args.schedparam = attr->schedparam;
|
||||||
|
break;
|
||||||
|
case PTHREAD_INHERIT_SCHED:
|
||||||
|
/* schedpolicy doesn't need to be set, only get priority */
|
||||||
|
__sched_getparam(father_pid, &new_thread->p_start_args.schedparam);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
new_thread->p_priority =
|
||||||
|
new_thread->p_start_args.schedparam.sched_priority;
|
||||||
|
}
|
||||||
|
/* Finish setting up arguments to pthread_start_thread */
|
||||||
|
new_thread->p_start_args.start_routine = start_routine;
|
||||||
|
new_thread->p_start_args.arg = arg;
|
||||||
|
new_thread->p_start_args.mask = *mask;
|
||||||
|
/* Do the cloning */
|
||||||
|
pid = __clone(pthread_start_thread, (void **) new_thread,
|
||||||
|
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||||
|
PTHREAD_SIG_RESTART,
|
||||||
|
new_thread);
|
||||||
|
/* Check if cloning succeeded */
|
||||||
|
if (pid == -1) {
|
||||||
|
/* Free the stack */
|
||||||
|
munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
|
||||||
|
INITIAL_STACK_SIZE);
|
||||||
|
__pthread_handles[sseg].h_descr = NULL;
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
/* Insert new thread in doubly linked list of active threads */
|
||||||
|
new_thread->p_prevlive = __pthread_main_thread;
|
||||||
|
new_thread->p_nextlive = __pthread_main_thread->p_nextlive;
|
||||||
|
__pthread_main_thread->p_nextlive->p_prevlive = new_thread;
|
||||||
|
__pthread_main_thread->p_nextlive = new_thread;
|
||||||
|
/* Set pid field of the new thread, in case we get there before the
|
||||||
|
child starts. */
|
||||||
|
new_thread->p_pid = pid;
|
||||||
|
/* We're all set */
|
||||||
|
*thread = new_thread_id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the resources of a thread. */
|
||||||
|
|
||||||
|
static void pthread_free(pthread_descr th)
|
||||||
|
{
|
||||||
|
pthread_handle handle;
|
||||||
|
ASSERT(th->p_exited);
|
||||||
|
/* Make the handle invalid */
|
||||||
|
handle = thread_handle(th->p_tid);
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
handle->h_descr = NULL;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
/* If initial thread, nothing to free */
|
||||||
|
if (th == &__pthread_initial_thread) return;
|
||||||
|
/* Free the stack and thread descriptor area */
|
||||||
|
munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle threads that have exited */
|
||||||
|
|
||||||
|
static void pthread_exited(pid_t pid)
|
||||||
|
{
|
||||||
|
pthread_descr th;
|
||||||
|
int detached;
|
||||||
|
/* Find thread with that pid */
|
||||||
|
for (th = __pthread_main_thread->p_nextlive;
|
||||||
|
th != __pthread_main_thread;
|
||||||
|
th = th->p_nextlive) {
|
||||||
|
if (th->p_pid == pid) {
|
||||||
|
/* Remove thread from list of active threads */
|
||||||
|
th->p_nextlive->p_prevlive = th->p_prevlive;
|
||||||
|
th->p_prevlive->p_nextlive = th->p_nextlive;
|
||||||
|
/* Mark thread as exited, and if detached, free its resources */
|
||||||
|
acquire(th->p_spinlock);
|
||||||
|
th->p_exited = 1;
|
||||||
|
detached = th->p_detached;
|
||||||
|
release(th->p_spinlock);
|
||||||
|
if (detached) pthread_free(th);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If all threads have exited and the main thread is pending on a
|
||||||
|
pthread_exit, wake up the main thread and terminate ourselves. */
|
||||||
|
if (main_thread_exiting &&
|
||||||
|
__pthread_main_thread->p_nextlive == __pthread_main_thread) {
|
||||||
|
restart(__pthread_main_thread);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pthread_reap_children(void)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
while ((pid = __libc_waitpid(-1, &status, WNOHANG | __WCLONE)) > 0) {
|
||||||
|
pthread_exited(pid);
|
||||||
|
if (WIFSIGNALED(status)) {
|
||||||
|
/* If a thread died due to a signal, send the same signal to
|
||||||
|
all other threads, including the main thread. */
|
||||||
|
pthread_kill_all_threads(WTERMSIG(status), 1);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the resources of a thread */
|
||||||
|
|
||||||
|
static void pthread_handle_free(pthread_descr th)
|
||||||
|
{
|
||||||
|
acquire(th->p_spinlock);
|
||||||
|
if (th->p_exited) {
|
||||||
|
release(th->p_spinlock);
|
||||||
|
pthread_free(th);
|
||||||
|
} else {
|
||||||
|
/* The Unix process of the thread is still running.
|
||||||
|
Mark the thread as detached so that the thread manager will
|
||||||
|
deallocate its resources when the Unix process exits. */
|
||||||
|
th->p_detached = 1;
|
||||||
|
release(th->p_spinlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send a signal to all running threads */
|
||||||
|
|
||||||
|
static void pthread_kill_all_threads(int sig, int main_thread_also)
|
||||||
|
{
|
||||||
|
pthread_descr th;
|
||||||
|
for (th = __pthread_main_thread->p_nextlive;
|
||||||
|
th != __pthread_main_thread;
|
||||||
|
th = th->p_nextlive) {
|
||||||
|
kill(th->p_pid, sig);
|
||||||
|
}
|
||||||
|
if (main_thread_also) {
|
||||||
|
kill(__pthread_main_thread->p_pid, sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process-wide exit() */
|
||||||
|
|
||||||
|
static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode)
|
||||||
|
{
|
||||||
|
pthread_descr th;
|
||||||
|
__pthread_exit_requested = 1;
|
||||||
|
__pthread_exit_code = exitcode;
|
||||||
|
/* Send the CANCEL signal to all running threads, including the main
|
||||||
|
thread, but excluding the thread from which the exit request originated
|
||||||
|
(that thread must complete the exit, e.g. calling atexit functions
|
||||||
|
and flushing stdio buffers). */
|
||||||
|
for (th = issuing_thread->p_nextlive;
|
||||||
|
th != issuing_thread;
|
||||||
|
th = th->p_nextlive) {
|
||||||
|
kill(th->p_pid, PTHREAD_SIG_CANCEL);
|
||||||
|
}
|
||||||
|
/* Now, wait for all these threads, so that they don't become zombies
|
||||||
|
and their times are properly added to the thread manager's times. */
|
||||||
|
for (th = issuing_thread->p_nextlive;
|
||||||
|
th != issuing_thread;
|
||||||
|
th = th->p_nextlive) {
|
||||||
|
waitpid(th->p_pid, NULL, __WCLONE);
|
||||||
|
}
|
||||||
|
restart(issuing_thread);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handler for PTHREAD_SIG_RESTART in thread manager thread */
|
||||||
|
|
||||||
|
void __pthread_manager_sighandler(int sig)
|
||||||
|
{
|
||||||
|
terminated_children = 1;
|
||||||
|
}
|
234
linuxthreads/mutex.c
Normal file
234
linuxthreads/mutex.c
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Mutexes */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
int __pthread_mutex_init(pthread_mutex_t * mutex,
|
||||||
|
const pthread_mutexattr_t * mutex_attr)
|
||||||
|
{
|
||||||
|
mutex->m_spinlock = 0;
|
||||||
|
mutex->m_count = 0;
|
||||||
|
mutex->m_owner = NULL;
|
||||||
|
mutex->m_kind =
|
||||||
|
mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->mutexkind;
|
||||||
|
queue_init(&mutex->m_waiting);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutex_init, pthread_mutex_init)
|
||||||
|
|
||||||
|
int __pthread_mutex_destroy(pthread_mutex_t * mutex)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
acquire(&mutex->m_spinlock);
|
||||||
|
count = mutex->m_count;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
if (count > 0) return EBUSY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutex_destroy, pthread_mutex_destroy)
|
||||||
|
|
||||||
|
int __pthread_mutex_trylock(pthread_mutex_t * mutex)
|
||||||
|
{
|
||||||
|
pthread_descr self;
|
||||||
|
|
||||||
|
acquire(&mutex->m_spinlock);
|
||||||
|
switch(mutex->m_kind) {
|
||||||
|
case PTHREAD_MUTEX_FAST_NP:
|
||||||
|
if (mutex->m_count == 0) {
|
||||||
|
mutex->m_count = 1;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PTHREAD_MUTEX_RECURSIVE_NP:
|
||||||
|
self = thread_self();
|
||||||
|
if (mutex->m_count == 0 || mutex->m_owner == self) {
|
||||||
|
mutex->m_count++;
|
||||||
|
mutex->m_owner = self;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||||
|
self = thread_self();
|
||||||
|
if (mutex->m_count == 0) {
|
||||||
|
mutex->m_count = 1;
|
||||||
|
mutex->m_owner = self;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return EBUSY;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutex_trylock, pthread_mutex_trylock)
|
||||||
|
|
||||||
|
int __pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||||
|
{
|
||||||
|
pthread_descr self;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
acquire(&mutex->m_spinlock);
|
||||||
|
switch(mutex->m_kind) {
|
||||||
|
case PTHREAD_MUTEX_FAST_NP:
|
||||||
|
if (mutex->m_count == 0) {
|
||||||
|
mutex->m_count = 1;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
self = thread_self();
|
||||||
|
break;
|
||||||
|
case PTHREAD_MUTEX_RECURSIVE_NP:
|
||||||
|
self = thread_self();
|
||||||
|
if (mutex->m_count == 0 || mutex->m_owner == self) {
|
||||||
|
mutex->m_count++;
|
||||||
|
mutex->m_owner = self;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||||
|
self = thread_self();
|
||||||
|
if (mutex->m_count == 0) {
|
||||||
|
mutex->m_count = 1;
|
||||||
|
mutex->m_owner = self;
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
} else if (mutex->m_owner == self) {
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return EDEADLK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
/* Suspend ourselves, then try again */
|
||||||
|
enqueue(&mutex->m_waiting, self);
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
suspend(self); /* This is not a cancellation point */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutex_lock, pthread_mutex_lock)
|
||||||
|
|
||||||
|
int __pthread_mutex_unlock(pthread_mutex_t * mutex)
|
||||||
|
{
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire(&mutex->m_spinlock);
|
||||||
|
switch (mutex->m_kind) {
|
||||||
|
case PTHREAD_MUTEX_FAST_NP:
|
||||||
|
mutex->m_count = 0;
|
||||||
|
break;
|
||||||
|
case PTHREAD_MUTEX_RECURSIVE_NP:
|
||||||
|
mutex->m_count--;
|
||||||
|
if (mutex->m_count > 0) {
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mutex->m_count = 0; /* so that excess unlocks do not break everything */
|
||||||
|
break;
|
||||||
|
case PTHREAD_MUTEX_ERRORCHECK_NP:
|
||||||
|
if (mutex->m_count == 0 || mutex->m_owner != thread_self()) {
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
mutex->m_count = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
th = dequeue(&mutex->m_waiting);
|
||||||
|
release(&mutex->m_spinlock);
|
||||||
|
if (th != NULL) restart(th);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
|
||||||
|
|
||||||
|
int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
|
||||||
|
{
|
||||||
|
attr->mutexkind = PTHREAD_MUTEX_FAST_NP;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
|
||||||
|
|
||||||
|
int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
|
||||||
|
|
||||||
|
int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
|
||||||
|
{
|
||||||
|
if (kind != PTHREAD_MUTEX_FAST_NP
|
||||||
|
&& kind != PTHREAD_MUTEX_RECURSIVE_NP
|
||||||
|
&& kind != PTHREAD_MUTEX_ERRORCHECK_NP)
|
||||||
|
return EINVAL;
|
||||||
|
attr->mutexkind = kind;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
|
||||||
|
|
||||||
|
int __pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind)
|
||||||
|
{
|
||||||
|
*kind = attr->mutexkind;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np)
|
||||||
|
|
||||||
|
/* Once-only execution */
|
||||||
|
|
||||||
|
static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER;
|
||||||
|
|
||||||
|
enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
|
||||||
|
|
||||||
|
int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
|
||||||
|
{
|
||||||
|
/* Test without locking first for speed */
|
||||||
|
if (*once_control == DONE) return 0;
|
||||||
|
/* Lock and test again */
|
||||||
|
pthread_mutex_lock(&once_masterlock);
|
||||||
|
/* If init_routine is being called from another routine, wait until
|
||||||
|
it completes. */
|
||||||
|
while (*once_control == IN_PROGRESS) {
|
||||||
|
pthread_cond_wait(&once_finished, &once_masterlock);
|
||||||
|
}
|
||||||
|
/* Here *once_control is stable and either NEVER or DONE. */
|
||||||
|
if (*once_control == NEVER) {
|
||||||
|
*once_control = IN_PROGRESS;
|
||||||
|
pthread_mutex_unlock(&once_masterlock);
|
||||||
|
init_routine();
|
||||||
|
pthread_mutex_lock(&once_masterlock);
|
||||||
|
*once_control = DONE;
|
||||||
|
pthread_cond_broadcast(&once_finished);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&once_masterlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_once, pthread_once)
|
97
linuxthreads/ptfork.c
Normal file
97
linuxthreads/ptfork.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* The "atfork" stuff */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
struct handler_list {
|
||||||
|
void (*handler)(void);
|
||||||
|
struct handler_list * next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static pthread_mutex_t pthread_atfork_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static struct handler_list * pthread_atfork_prepare = NULL;
|
||||||
|
static struct handler_list * pthread_atfork_parent = NULL;
|
||||||
|
static struct handler_list * pthread_atfork_child = NULL;
|
||||||
|
|
||||||
|
static void pthread_insert_list(struct handler_list ** list,
|
||||||
|
void (*handler)(void),
|
||||||
|
struct handler_list * newlist,
|
||||||
|
int at_end)
|
||||||
|
{
|
||||||
|
if (handler == NULL) return;
|
||||||
|
if (at_end) {
|
||||||
|
while(*list != NULL) list = &((*list)->next);
|
||||||
|
}
|
||||||
|
newlist->handler = handler;
|
||||||
|
newlist->next = *list;
|
||||||
|
*list = newlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct handler_list_block {
|
||||||
|
struct handler_list prepare, parent, child;
|
||||||
|
};
|
||||||
|
|
||||||
|
int __pthread_atfork(void (*prepare)(void),
|
||||||
|
void (*parent)(void),
|
||||||
|
void (*child)(void))
|
||||||
|
{
|
||||||
|
struct handler_list_block * block =
|
||||||
|
(struct handler_list_block *) malloc(sizeof(struct handler_list_block));
|
||||||
|
if (block == NULL) return ENOMEM;
|
||||||
|
pthread_mutex_lock(&pthread_atfork_lock);
|
||||||
|
/* "prepare" handlers are called in LIFO */
|
||||||
|
pthread_insert_list(&pthread_atfork_prepare, prepare, &block->prepare, 0);
|
||||||
|
/* "parent" handlers are called in FIFO */
|
||||||
|
pthread_insert_list(&pthread_atfork_parent, parent, &block->parent, 1);
|
||||||
|
/* "child" handlers are called in FIFO */
|
||||||
|
pthread_insert_list(&pthread_atfork_child, child, &block->child, 1);
|
||||||
|
pthread_mutex_unlock(&pthread_atfork_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_atfork, pthread_atfork)
|
||||||
|
|
||||||
|
static inline void pthread_call_handlers(struct handler_list * list)
|
||||||
|
{
|
||||||
|
for (/*nothing*/; list != NULL; list = list->next) (list->handler)();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int __fork(void);
|
||||||
|
|
||||||
|
int fork(void)
|
||||||
|
{
|
||||||
|
int pid;
|
||||||
|
struct handler_list * prepare, * child, * parent;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&pthread_atfork_lock);
|
||||||
|
prepare = pthread_atfork_prepare;
|
||||||
|
child = pthread_atfork_child;
|
||||||
|
parent = pthread_atfork_parent;
|
||||||
|
pthread_mutex_unlock(&pthread_atfork_lock);
|
||||||
|
pthread_call_handlers(prepare);
|
||||||
|
pid = __fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
__pthread_reset_main_thread();
|
||||||
|
__fresetlockfiles();
|
||||||
|
pthread_call_handlers(child);
|
||||||
|
} else {
|
||||||
|
pthread_call_handlers(parent);
|
||||||
|
}
|
||||||
|
return pid;
|
||||||
|
}
|
445
linuxthreads/pthread.c
Normal file
445
linuxthreads/pthread.c
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Thread creation, initialization, and basic low-level routines */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
/* Descriptor of the initial thread */
|
||||||
|
|
||||||
|
struct _pthread_descr_struct __pthread_initial_thread = {
|
||||||
|
&__pthread_initial_thread, /* pthread_descr p_nextlive */
|
||||||
|
&__pthread_initial_thread, /* pthread_descr p_prevlive */
|
||||||
|
NULL, /* pthread_descr p_nextwaiting */
|
||||||
|
PTHREAD_THREADS_MAX, /* pthread_t p_tid */
|
||||||
|
0, /* int p_pid */
|
||||||
|
0, /* int p_priority */
|
||||||
|
&__pthread_handles[0].h_spinlock, /* int * p_spinlock */
|
||||||
|
0, /* int p_signal */
|
||||||
|
NULL, /* sigjmp_buf * p_signal_buf */
|
||||||
|
NULL, /* sigjmp_buf * p_cancel_buf */
|
||||||
|
0, /* char p_terminated */
|
||||||
|
0, /* char p_detached */
|
||||||
|
0, /* char p_exited */
|
||||||
|
NULL, /* void * p_retval */
|
||||||
|
0, /* int p_retval */
|
||||||
|
NULL, /* pthread_descr p_joining */
|
||||||
|
NULL, /* struct _pthread_cleanup_buffer * p_cleanup */
|
||||||
|
0, /* char p_cancelstate */
|
||||||
|
0, /* char p_canceltype */
|
||||||
|
0, /* char p_canceled */
|
||||||
|
NULL, /* int *p_errnop */
|
||||||
|
0, /* int p_errno */
|
||||||
|
NULL, /* int *p_h_errnop */
|
||||||
|
0, /* int p_h_errno */
|
||||||
|
PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
|
||||||
|
{NULL} /* void * p_specific[PTHREAD_KEYS_MAX] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Descriptor of the manager thread; none of this is used but the error
|
||||||
|
variables and the address for identification. */
|
||||||
|
|
||||||
|
struct _pthread_descr_struct __pthread_manager_thread = {
|
||||||
|
NULL, /* pthread_descr p_nextlive */
|
||||||
|
NULL, /* pthread_descr p_prevlive */
|
||||||
|
NULL, /* pthread_descr p_nextwaiting */
|
||||||
|
0, /* int p_tid */
|
||||||
|
0, /* int p_pid */
|
||||||
|
0, /* int p_priority */
|
||||||
|
NULL, /* int * p_spinlock */
|
||||||
|
0, /* int p_signal */
|
||||||
|
NULL, /* sigjmp_buf * p_signal_buf */
|
||||||
|
NULL, /* sigjmp_buf * p_cancel_buf */
|
||||||
|
0, /* char p_terminated */
|
||||||
|
0, /* char p_detached */
|
||||||
|
0, /* char p_exited */
|
||||||
|
NULL, /* void * p_retval */
|
||||||
|
0, /* int p_retval */
|
||||||
|
NULL, /* pthread_descr p_joining */
|
||||||
|
NULL, /* struct _pthread_cleanup_buffer * p_cleanup */
|
||||||
|
0, /* char p_cancelstate */
|
||||||
|
0, /* char p_canceltype */
|
||||||
|
0, /* char p_canceled */
|
||||||
|
NULL, /* int *p_errnop */
|
||||||
|
0, /* int p_errno */
|
||||||
|
NULL, /* int *p_h_errnop */
|
||||||
|
0, /* int p_h_errno */
|
||||||
|
PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
|
||||||
|
{NULL} /* void * p_specific[PTHREAD_KEYS_MAX] */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Pointer to the main thread (the father of the thread manager thread) */
|
||||||
|
/* Originally, this is the initial thread, but this changes after fork() */
|
||||||
|
|
||||||
|
pthread_descr __pthread_main_thread = &__pthread_initial_thread;
|
||||||
|
|
||||||
|
/* Limit between the stack of the initial thread (above) and the
|
||||||
|
stacks of other threads (below). Aligned on a STACK_SIZE boundary. */
|
||||||
|
|
||||||
|
char *__pthread_initial_thread_bos = NULL;
|
||||||
|
|
||||||
|
/* File descriptor for sending requests to the thread manager. */
|
||||||
|
/* Initially -1, meaning that the thread manager is not running. */
|
||||||
|
|
||||||
|
int __pthread_manager_request = -1;
|
||||||
|
|
||||||
|
/* Other end of the pipe for sending requests to the thread manager. */
|
||||||
|
|
||||||
|
int __pthread_manager_reader;
|
||||||
|
|
||||||
|
/* PID of thread manager */
|
||||||
|
|
||||||
|
static int __pthread_manager_pid;
|
||||||
|
|
||||||
|
/* Limits of the thread manager stack */
|
||||||
|
|
||||||
|
char *__pthread_manager_thread_bos = NULL;
|
||||||
|
char *__pthread_manager_thread_tos = NULL;
|
||||||
|
|
||||||
|
/* For process-wide exit() */
|
||||||
|
|
||||||
|
int __pthread_exit_requested = 0;
|
||||||
|
int __pthread_exit_code = 0;
|
||||||
|
|
||||||
|
/* Signal numbers used for the communication. */
|
||||||
|
int __pthread_sig_restart;
|
||||||
|
int __pthread_sig_cancel;
|
||||||
|
|
||||||
|
/* These variables are used by the setup code. */
|
||||||
|
extern int _errno;
|
||||||
|
extern int _h_errno;
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
|
||||||
|
static void pthread_exit_process(int retcode, void *arg);
|
||||||
|
static void pthread_handle_sigcancel(int sig);
|
||||||
|
|
||||||
|
/* Initialize the pthread library.
|
||||||
|
Initialization is split in two functions:
|
||||||
|
- a constructor function that blocks the PTHREAD_SIG_RESTART signal
|
||||||
|
(must do this very early, since the program could capture the signal
|
||||||
|
mask with e.g. sigsetjmp before creating the first thread);
|
||||||
|
- a regular function called from pthread_create when needed. */
|
||||||
|
|
||||||
|
static void pthread_initialize(void) __attribute__((constructor));
|
||||||
|
|
||||||
|
static void pthread_initialize(void)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
sigset_t mask;
|
||||||
|
|
||||||
|
/* If already done (e.g. by a constructor called earlier!), bail out */
|
||||||
|
if (__pthread_initial_thread_bos != NULL) return;
|
||||||
|
/* For the initial stack, reserve at least STACK_SIZE bytes of stack
|
||||||
|
below the current stack address, and align that on a
|
||||||
|
STACK_SIZE boundary. */
|
||||||
|
__pthread_initial_thread_bos =
|
||||||
|
(char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));
|
||||||
|
/* Update the descriptor for the initial thread. */
|
||||||
|
__pthread_initial_thread.p_pid = __getpid();
|
||||||
|
/* If we have special thread_self processing, initialize that for the
|
||||||
|
main thread now. */
|
||||||
|
#ifdef INIT_THREAD_SELF
|
||||||
|
INIT_THREAD_SELF(&__pthread_initial_thread);
|
||||||
|
#endif
|
||||||
|
/* The errno/h_errno variable of the main thread are the global ones. */
|
||||||
|
__pthread_initial_thread.p_errnop = &_errno;
|
||||||
|
__pthread_initial_thread.p_h_errnop = &_h_errno;
|
||||||
|
/* Allocate the signals used. */
|
||||||
|
__pthread_sig_restart = __libc_allocate_rtsig (1);
|
||||||
|
__pthread_sig_cancel = __libc_allocate_rtsig (1);
|
||||||
|
if (__pthread_sig_restart < 0 || __pthread_sig_cancel < 0)
|
||||||
|
{
|
||||||
|
/* The kernel does not support real-time signals. Use as before
|
||||||
|
the available signals in the fixed set. */
|
||||||
|
__pthread_sig_restart = SIGUSR1;
|
||||||
|
__pthread_sig_cancel = SIGUSR2;
|
||||||
|
}
|
||||||
|
/* Setup signal handlers for the initial thread.
|
||||||
|
Since signal handlers are shared between threads, these settings
|
||||||
|
will be inherited by all other threads. */
|
||||||
|
sa.sa_handler = __pthread_sighandler;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_RESTART; /* does not matter for regular threads, but
|
||||||
|
better for the thread manager */
|
||||||
|
sigaction(PTHREAD_SIG_RESTART, &sa, NULL);
|
||||||
|
sa.sa_handler = pthread_handle_sigcancel;
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
sigaction(PTHREAD_SIG_CANCEL, &sa, NULL);
|
||||||
|
|
||||||
|
/* Initially, block PTHREAD_SIG_RESTART. Will be unblocked on demand. */
|
||||||
|
sigemptyset(&mask);
|
||||||
|
sigaddset(&mask, PTHREAD_SIG_RESTART);
|
||||||
|
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||||
|
/* Register an exit function to kill all other threads. */
|
||||||
|
/* Do it early so that user-registered atexit functions are called
|
||||||
|
before pthread_exit_process. */
|
||||||
|
__on_exit(pthread_exit_process, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pthread_initialize_manager(void)
|
||||||
|
{
|
||||||
|
int manager_pipe[2];
|
||||||
|
|
||||||
|
/* If basic initialization not done yet (e.g. we're called from a
|
||||||
|
constructor run before our constructor), do it now */
|
||||||
|
if (__pthread_initial_thread_bos == NULL) pthread_initialize();
|
||||||
|
/* Setup stack for thread manager */
|
||||||
|
__pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE);
|
||||||
|
if (__pthread_manager_thread_bos == NULL) return -1;
|
||||||
|
__pthread_manager_thread_tos =
|
||||||
|
__pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE;
|
||||||
|
/* Setup pipe to communicate with thread manager */
|
||||||
|
if (pipe(manager_pipe) == -1) {
|
||||||
|
free(__pthread_manager_thread_bos);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
__pthread_manager_request = manager_pipe[1]; /* writing end */
|
||||||
|
__pthread_manager_reader = manager_pipe[0]; /* reading end */
|
||||||
|
/* Start the thread manager */
|
||||||
|
__pthread_manager_pid =
|
||||||
|
__clone(__pthread_manager, (void **) __pthread_manager_thread_tos,
|
||||||
|
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
||||||
|
(void *)(long)manager_pipe[0]);
|
||||||
|
if (__pthread_manager_pid == -1) {
|
||||||
|
free(__pthread_manager_thread_bos);
|
||||||
|
__libc_close(manager_pipe[0]);
|
||||||
|
__libc_close(manager_pipe[1]);
|
||||||
|
__pthread_manager_request = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thread creation */
|
||||||
|
|
||||||
|
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||||
|
void * (*start_routine)(void *), void *arg)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
struct pthread_request request;
|
||||||
|
if (__pthread_manager_request < 0) {
|
||||||
|
if (pthread_initialize_manager() < 0) return EAGAIN;
|
||||||
|
}
|
||||||
|
request.req_thread = self;
|
||||||
|
request.req_kind = REQ_CREATE;
|
||||||
|
request.req_args.create.attr = attr;
|
||||||
|
request.req_args.create.fn = start_routine;
|
||||||
|
request.req_args.create.arg = arg;
|
||||||
|
sigprocmask(SIG_SETMASK, (const sigset_t *) NULL,
|
||||||
|
&request.req_args.create.mask);
|
||||||
|
__libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
|
||||||
|
suspend(self);
|
||||||
|
if (self->p_retcode == 0) *thread = (pthread_t) self->p_retval;
|
||||||
|
return self->p_retcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple operations on thread identifiers */
|
||||||
|
|
||||||
|
pthread_t pthread_self(void)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
return self->p_tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_equal(pthread_t thread1, pthread_t thread2)
|
||||||
|
{
|
||||||
|
return thread1 == thread2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thread scheduling */
|
||||||
|
|
||||||
|
int pthread_setschedparam(pthread_t thread, int policy,
|
||||||
|
const struct sched_param *param)
|
||||||
|
{
|
||||||
|
pthread_handle handle = thread_handle(thread);
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
if (invalid_handle(handle, thread)) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return ESRCH;
|
||||||
|
}
|
||||||
|
th = handle->h_descr;
|
||||||
|
if (__sched_setscheduler(th->p_pid, policy, param) == -1) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_getschedparam(pthread_t thread, int *policy,
|
||||||
|
struct sched_param *param)
|
||||||
|
{
|
||||||
|
pthread_handle handle = thread_handle(thread);
|
||||||
|
int pid, pol;
|
||||||
|
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
if (invalid_handle(handle, thread)) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return ESRCH;
|
||||||
|
}
|
||||||
|
pid = handle->h_descr->p_pid;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
pol = __sched_getscheduler(pid);
|
||||||
|
if (pol == -1) return errno;
|
||||||
|
if (__sched_getparam(pid, param) == -1) return errno;
|
||||||
|
*policy = pol;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process-wide exit() request */
|
||||||
|
|
||||||
|
static void pthread_exit_process(int retcode, void *arg)
|
||||||
|
{
|
||||||
|
struct pthread_request request;
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
|
||||||
|
if (__pthread_manager_request >= 0) {
|
||||||
|
request.req_thread = self;
|
||||||
|
request.req_kind = REQ_PROCESS_EXIT;
|
||||||
|
request.req_args.exit.code = retcode;
|
||||||
|
__libc_write(__pthread_manager_request,
|
||||||
|
(char *) &request, sizeof(request));
|
||||||
|
suspend(self);
|
||||||
|
/* Main thread should accumulate times for thread manager and its
|
||||||
|
children, so that timings for main thread account for all threads. */
|
||||||
|
if (self == __pthread_main_thread)
|
||||||
|
waitpid(__pthread_manager_pid, NULL, __WCLONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The handler for the RESTART signal just records the signal received
|
||||||
|
in the thread descriptor, and optionally performs a siglongjmp
|
||||||
|
(for pthread_cond_timedwait). Also used in sigwait.
|
||||||
|
For the thread manager thread, redirect the signal to
|
||||||
|
__pthread_manager_sighandler. */
|
||||||
|
|
||||||
|
void __pthread_sighandler(int sig)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
if (self == &__pthread_manager_thread) {
|
||||||
|
__pthread_manager_sighandler(sig);
|
||||||
|
} else {
|
||||||
|
self->p_signal = sig;
|
||||||
|
if (self->p_signal_jmp != NULL) siglongjmp(*self->p_signal_jmp, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The handler for the CANCEL signal checks for cancellation
|
||||||
|
(in asynchronous mode) and for process-wide exit and exec requests. */
|
||||||
|
|
||||||
|
static void pthread_handle_sigcancel(int sig)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
sigjmp_buf * jmpbuf;
|
||||||
|
|
||||||
|
if (__pthread_exit_requested) {
|
||||||
|
/* Main thread should accumulate times for thread manager and its
|
||||||
|
children, so that timings for main thread account for all threads. */
|
||||||
|
if (self == __pthread_main_thread)
|
||||||
|
waitpid(__pthread_manager_pid, NULL, __WCLONE);
|
||||||
|
_exit(__pthread_exit_code);
|
||||||
|
}
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
|
||||||
|
if (self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
jmpbuf = self->p_cancel_jmp;
|
||||||
|
if (jmpbuf != NULL) {
|
||||||
|
self->p_cancel_jmp = NULL;
|
||||||
|
siglongjmp(*jmpbuf, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the state of the thread machinery after a fork().
|
||||||
|
Close the pipe used for requests and set the main thread to the forked
|
||||||
|
thread.
|
||||||
|
Notice that we can't free the stack segments, as the forked thread
|
||||||
|
may hold pointers into them. */
|
||||||
|
|
||||||
|
void __pthread_reset_main_thread()
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
|
||||||
|
if (__pthread_manager_request != -1) {
|
||||||
|
/* Free the thread manager stack */
|
||||||
|
free(__pthread_manager_thread_bos);
|
||||||
|
__pthread_manager_thread_bos = __pthread_manager_thread_tos = NULL;
|
||||||
|
/* Close the two ends of the pipe */
|
||||||
|
__libc_close(__pthread_manager_request);
|
||||||
|
__libc_close(__pthread_manager_reader);
|
||||||
|
__pthread_manager_request = __pthread_manager_reader = -1;
|
||||||
|
}
|
||||||
|
/* Update the pid of the main thread */
|
||||||
|
self->p_pid = __getpid();
|
||||||
|
/* Make the forked thread the main thread */
|
||||||
|
__pthread_main_thread = self;
|
||||||
|
self->p_nextlive = self;
|
||||||
|
self->p_prevlive = self;
|
||||||
|
/* Now this thread modifies the global variables. */
|
||||||
|
self->p_errnop = &_errno;
|
||||||
|
self->p_h_errnop = &_h_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process-wide exec() request */
|
||||||
|
|
||||||
|
void __pthread_kill_other_threads_np(void)
|
||||||
|
{
|
||||||
|
/* Terminate all other threads and thread manager */
|
||||||
|
pthread_exit_process(0, NULL);
|
||||||
|
/* Make current thread the main thread in case the calling thread
|
||||||
|
changes its mind, does not exec(), and creates new threads instead. */
|
||||||
|
__pthread_reset_main_thread();
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np)
|
||||||
|
|
||||||
|
/* Debugging aid */
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
void __pthread_message(char * fmt, long arg)
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
va_list args;
|
||||||
|
sprintf(buffer, "%05d : ", __getpid());
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buffer + 8, sizeof(buffer) - 8, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
__libc_write(2, buffer, strlen(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PIC
|
||||||
|
/* We need a hook to force the cancelation wrappers to be linked in when
|
||||||
|
static libpthread is used. */
|
||||||
|
extern const int __pthread_provide_wrappers;
|
||||||
|
static const int *const __pthread_require_wrappers =
|
||||||
|
&__pthread_provide_wrappers;
|
||||||
|
#endif
|
62
linuxthreads/queue.h
Normal file
62
linuxthreads/queue.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Waiting queues */
|
||||||
|
|
||||||
|
typedef struct _pthread_queue pthread_queue;
|
||||||
|
|
||||||
|
static inline void queue_init(pthread_queue * q)
|
||||||
|
{
|
||||||
|
q->head = q->tail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void enqueue(pthread_queue * q, pthread_descr th)
|
||||||
|
{
|
||||||
|
int prio;
|
||||||
|
pthread_descr * elt;
|
||||||
|
|
||||||
|
ASSERT(th->p_nextwaiting == NULL);
|
||||||
|
if (q->tail == NULL) {
|
||||||
|
q->head = th;
|
||||||
|
q->tail = th;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prio = th->p_priority;
|
||||||
|
if (prio > 0) {
|
||||||
|
/* Insert in queue according to priority order */
|
||||||
|
for (elt = &(q->head); *elt != NULL; elt = &((*elt)->p_nextwaiting)) {
|
||||||
|
if (prio > (*elt)->p_priority) {
|
||||||
|
th->p_nextwaiting = *elt;
|
||||||
|
*elt = th;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Priority is no greater than any thread in the queue.
|
||||||
|
Insert at end of queue */
|
||||||
|
q->tail->p_nextwaiting = th;
|
||||||
|
q->tail = th;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pthread_descr dequeue(pthread_queue * q)
|
||||||
|
{
|
||||||
|
pthread_descr th;
|
||||||
|
th = q->head;
|
||||||
|
if (th != NULL) {
|
||||||
|
q->head = th->p_nextwaiting;
|
||||||
|
if (q->head == NULL) q->tail = NULL;
|
||||||
|
th->p_nextwaiting = NULL;
|
||||||
|
}
|
||||||
|
return th;
|
||||||
|
}
|
57
linuxthreads/restart.h
Normal file
57
linuxthreads/restart.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
/* Primitives for controlling thread execution */
|
||||||
|
|
||||||
|
static inline void restart(pthread_descr th)
|
||||||
|
{
|
||||||
|
kill(th->p_pid, PTHREAD_SIG_RESTART);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void suspend(pthread_descr self)
|
||||||
|
{
|
||||||
|
sigset_t mask;
|
||||||
|
|
||||||
|
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */
|
||||||
|
do {
|
||||||
|
self->p_signal = 0;
|
||||||
|
sigsuspend(&mask); /* Wait for signal */
|
||||||
|
} while (self->p_signal != PTHREAD_SIG_RESTART);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void suspend_with_cancellation(pthread_descr self)
|
||||||
|
{
|
||||||
|
sigset_t mask;
|
||||||
|
sigjmp_buf jmpbuf;
|
||||||
|
|
||||||
|
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */
|
||||||
|
/* No need to save the signal mask, we'll restore it ourselves */
|
||||||
|
if (sigsetjmp(jmpbuf, 0) == 0) {
|
||||||
|
self->p_cancel_jmp = &jmpbuf;
|
||||||
|
if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
|
||||||
|
do {
|
||||||
|
self->p_signal = 0;
|
||||||
|
sigsuspend(&mask); /* Wait for a signal */
|
||||||
|
} while (self->p_signal != PTHREAD_SIG_RESTART);
|
||||||
|
}
|
||||||
|
self->p_cancel_jmp = NULL;
|
||||||
|
} else {
|
||||||
|
sigaddset(&mask, PTHREAD_SIG_RESTART); /* Reblock the restart signal */
|
||||||
|
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||||
|
}
|
||||||
|
}
|
276
linuxthreads/rwlock.c
Normal file
276
linuxthreads/rwlock.c
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
/* Read-write lock implementation.
|
||||||
|
Copyright (C) 1998 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Xavier Leroy <Xavier.Leroy@inria.fr>
|
||||||
|
and Ulrich Drepper <drepper@cygnus.com>, 1998.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include "internals.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "restart.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_init (pthread_rwlock_t *rwlock,
|
||||||
|
const pthread_rwlockattr_t *attr)
|
||||||
|
{
|
||||||
|
rwlock->rw_spinlock = 0;
|
||||||
|
rwlock->rw_readers = 0;
|
||||||
|
rwlock->rw_writer = NULL;
|
||||||
|
|
||||||
|
queue_init(&rwlock->rw_read_waiting);
|
||||||
|
queue_init(&rwlock->rw_write_waiting);
|
||||||
|
|
||||||
|
if (attr == NULL)
|
||||||
|
{
|
||||||
|
rwlock->rw_kind = PTHREAD_RWLOCK_DEFAULT_NP;
|
||||||
|
rwlock->rw_pshared = PTHREAD_PROCESS_PRIVATE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rwlock->rw_kind = attr->lockkind;
|
||||||
|
rwlock->rw_pshared = attr->pshared;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
|
||||||
|
{
|
||||||
|
int readers;
|
||||||
|
_pthread_descr writer;
|
||||||
|
|
||||||
|
acquire (&rwlock->rw_spinlock);
|
||||||
|
readers = rwlock->rw_readers;
|
||||||
|
writer = rwlock->rw_writer;
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
|
||||||
|
if (readers > 0 || writer != NULL)
|
||||||
|
return EBUSY;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
|
||||||
|
{
|
||||||
|
pthread_descr self;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
acquire (&rwlock->rw_spinlock);
|
||||||
|
if (rwlock->rw_writer == NULL
|
||||||
|
|| (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
|
||||||
|
&& rwlock->rw_readers != 0))
|
||||||
|
/* We can add a reader lock. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Suspend ourselves, then try again */
|
||||||
|
self = thread_self ();
|
||||||
|
enqueue (&rwlock->rw_read_waiting, self);
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
suspend (self); /* This is not a cancellation point */
|
||||||
|
}
|
||||||
|
|
||||||
|
++rwlock->rw_readers;
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
|
||||||
|
{
|
||||||
|
int result = EBUSY;
|
||||||
|
|
||||||
|
acquire (&rwlock->rw_spinlock);
|
||||||
|
if (rwlock->rw_writer == NULL
|
||||||
|
|| (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
|
||||||
|
&& rwlock->rw_readers != 0))
|
||||||
|
{
|
||||||
|
++rwlock->rw_readers;
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self ();
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
acquire (&rwlock->rw_spinlock);
|
||||||
|
if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL)
|
||||||
|
{
|
||||||
|
rwlock->rw_writer = self;
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suspend ourselves, then try again */
|
||||||
|
enqueue (&rwlock->rw_write_waiting, self);
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
suspend (self); /* This is not a cancellation point */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
|
||||||
|
{
|
||||||
|
int result = EBUSY;
|
||||||
|
|
||||||
|
acquire (&rwlock->rw_spinlock);
|
||||||
|
if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL)
|
||||||
|
{
|
||||||
|
rwlock->rw_writer = thread_self ();
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
|
||||||
|
{
|
||||||
|
struct _pthread_queue torestart;
|
||||||
|
pthread_descr th;
|
||||||
|
|
||||||
|
acquire (&rwlock->rw_spinlock);
|
||||||
|
if (rwlock->rw_writer != NULL)
|
||||||
|
{
|
||||||
|
/* Unlocking a write lock. */
|
||||||
|
if (rwlock->rw_writer != thread_self ())
|
||||||
|
{
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
rwlock->rw_writer = NULL;
|
||||||
|
|
||||||
|
if (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
|
||||||
|
|| (th = dequeue (&rwlock->rw_write_waiting)) == NULL)
|
||||||
|
{
|
||||||
|
/* Restart all waiting readers. */
|
||||||
|
torestart = rwlock->rw_read_waiting;
|
||||||
|
queue_init (&rwlock->rw_read_waiting);
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
while ((th = dequeue (&torestart)) != NULL)
|
||||||
|
restart (th);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Restart one waiting writer. */
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
restart (th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Unlocking a read lock. */
|
||||||
|
if (rwlock->rw_readers == 0)
|
||||||
|
{
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
return EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
--rwlock->rw_readers;
|
||||||
|
if (rwlock->rw_readers == 0)
|
||||||
|
/* Restart one waiting writer, if any. */
|
||||||
|
th = dequeue (&rwlock->rw_write_waiting);
|
||||||
|
else
|
||||||
|
th = NULL;
|
||||||
|
|
||||||
|
release (&rwlock->rw_spinlock);
|
||||||
|
if (th != NULL)
|
||||||
|
restart (th);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
|
||||||
|
{
|
||||||
|
attr->lockkind = 0;
|
||||||
|
attr->pshared = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared)
|
||||||
|
{
|
||||||
|
*pshared = attr->pshared;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared)
|
||||||
|
{
|
||||||
|
if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
attr->pshared = pshared;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t *attr, int *pref)
|
||||||
|
{
|
||||||
|
*pref = attr->lockkind;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
pthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref)
|
||||||
|
{
|
||||||
|
if (pref != PTHREAD_RWLOCK_PREFER_READER_NP
|
||||||
|
&& pref != PTHREAD_RWLOCK_PREFER_WRITER_NP
|
||||||
|
&& pref != PTHREAD_RWLOCK_DEFAULT_NP)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
attr->lockkind = pref;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
236
linuxthreads/semaphore.c
Normal file
236
linuxthreads/semaphore.c
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Semaphores a la POSIX 1003.1b */
|
||||||
|
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "semaphore.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "restart.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined HAS_COMPARE_AND_SWAP && !defined TEST_FOR_COMPARE_AND_SWAP
|
||||||
|
/* If we have no atomic compare and swap, fake it using an extra spinlock. */
|
||||||
|
|
||||||
|
#include "spinlock.h"
|
||||||
|
static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
acquire(&sem->sem_spinlock);
|
||||||
|
ret = (sem->sem_status == oldval);
|
||||||
|
if (ret) sem->sem_status = newval;
|
||||||
|
release(&sem->sem_spinlock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined TEST_FOR_COMPARE_AND_SWAP
|
||||||
|
|
||||||
|
#include "spinlock.h"
|
||||||
|
static int has_compare_and_swap = -1; /* to be determined at run-time */
|
||||||
|
|
||||||
|
static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (has_compare_and_swap == 1)
|
||||||
|
return __compare_and_swap(&sem->sem_status, oldval, newval);
|
||||||
|
|
||||||
|
acquire(&sem->sem_spinlock);
|
||||||
|
ret = (sem->sem_status == oldval);
|
||||||
|
if (ret) sem->sem_status = newval;
|
||||||
|
release(&sem->sem_spinlock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* But if we do have an atomic compare and swap, use it! */
|
||||||
|
|
||||||
|
static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
|
||||||
|
{
|
||||||
|
return __compare_and_swap(&sem->sem_status, oldval, newval);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* The state of a semaphore is represented by a long int encoding
|
||||||
|
either the semaphore count if >= 0 and no thread is waiting on it,
|
||||||
|
or the head of the list of threads waiting for the semaphore.
|
||||||
|
To distinguish the two cases, we encode the semaphore count N
|
||||||
|
as 2N+1, so that it has the lowest bit set.
|
||||||
|
|
||||||
|
A sequence of sem_wait operations on a semaphore initialized to N
|
||||||
|
result in the following successive states:
|
||||||
|
2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ...
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void sem_restart_list(pthread_descr waiting);
|
||||||
|
|
||||||
|
int sem_init(sem_t *sem, int pshared, unsigned int value)
|
||||||
|
{
|
||||||
|
if ((long)value > SEM_VALUE_MAX) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pshared) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#ifdef TEST_FOR_COMPARE_AND_SWAP
|
||||||
|
if (has_compare_and_swap == -1) {
|
||||||
|
has_compare_and_swap = compare_and_swap_is_available();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
|
||||||
|
sem->sem_spinlock = 0;
|
||||||
|
#endif
|
||||||
|
sem->sem_status = ((long)value << 1) + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sem_wait(sem_t * sem)
|
||||||
|
{
|
||||||
|
long oldstatus, newstatus;
|
||||||
|
volatile pthread_descr self = thread_self();
|
||||||
|
pthread_descr * th;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
do {
|
||||||
|
oldstatus = sem->sem_status;
|
||||||
|
if ((oldstatus & 1) && (oldstatus != 1))
|
||||||
|
newstatus = oldstatus - 2;
|
||||||
|
else {
|
||||||
|
newstatus = (long) self;
|
||||||
|
self->p_nextwaiting = (pthread_descr) oldstatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
|
||||||
|
if (newstatus & 1)
|
||||||
|
/* We got the semaphore. */
|
||||||
|
return 0;
|
||||||
|
/* Wait for sem_post or cancellation */
|
||||||
|
suspend_with_cancellation(self);
|
||||||
|
/* This is a cancellation point */
|
||||||
|
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
|
||||||
|
/* Remove ourselves from the waiting list if we're still on it */
|
||||||
|
/* First check if we're at the head of the list. */
|
||||||
|
do {
|
||||||
|
oldstatus = sem->sem_status;
|
||||||
|
if (oldstatus != (long) self) break;
|
||||||
|
newstatus = (long) self->p_nextwaiting;
|
||||||
|
}
|
||||||
|
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
|
||||||
|
/* Now, check if we're somewhere in the list.
|
||||||
|
There's a race condition with sem_post here, but it does not matter:
|
||||||
|
the net result is that at the time pthread_exit is called,
|
||||||
|
self is no longer reachable from sem->sem_status. */
|
||||||
|
if (oldstatus != (long) self && (oldstatus & 1) == 0) {
|
||||||
|
for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
|
||||||
|
*th != NULL && *th != (pthread_descr) 1;
|
||||||
|
th = &((*th)->p_nextwaiting)) {
|
||||||
|
if (*th == self) {
|
||||||
|
*th = self->p_nextwaiting;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_exit(PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sem_trywait(sem_t * sem)
|
||||||
|
{
|
||||||
|
long oldstatus, newstatus;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldstatus = sem->sem_status;
|
||||||
|
if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
newstatus = oldstatus - 2;
|
||||||
|
}
|
||||||
|
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sem_post(sem_t * sem)
|
||||||
|
{
|
||||||
|
long oldstatus, newstatus;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldstatus = sem->sem_status;
|
||||||
|
if ((oldstatus & 1) == 0)
|
||||||
|
newstatus = 3;
|
||||||
|
else {
|
||||||
|
if (oldstatus >= SEM_VALUE_MAX) {
|
||||||
|
/* Overflow */
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
newstatus = oldstatus + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
|
||||||
|
if ((oldstatus & 1) == 0)
|
||||||
|
sem_restart_list((pthread_descr) oldstatus);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sem_getvalue(sem_t * sem, int * sval)
|
||||||
|
{
|
||||||
|
long status = sem->sem_status;
|
||||||
|
if (status & 1)
|
||||||
|
*sval = (int)((unsigned long) status >> 1);
|
||||||
|
else
|
||||||
|
*sval = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sem_destroy(sem_t * sem)
|
||||||
|
{
|
||||||
|
if ((sem->sem_status & 1) == 0) {
|
||||||
|
errno = EBUSY;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auxiliary function for restarting all threads on a waiting list,
|
||||||
|
in priority order. */
|
||||||
|
|
||||||
|
static void sem_restart_list(pthread_descr waiting)
|
||||||
|
{
|
||||||
|
pthread_descr th, towake, *p;
|
||||||
|
|
||||||
|
/* Sort list of waiting threads by decreasing priority (insertion sort) */
|
||||||
|
towake = NULL;
|
||||||
|
while (waiting != (pthread_descr) 1) {
|
||||||
|
th = waiting;
|
||||||
|
waiting = waiting->p_nextwaiting;
|
||||||
|
p = &towake;
|
||||||
|
while (*p != NULL && th->p_priority < (*p)->p_priority)
|
||||||
|
p = &((*p)->p_nextwaiting);
|
||||||
|
th->p_nextwaiting = *p;
|
||||||
|
*p = th;
|
||||||
|
}
|
||||||
|
/* Wake up threads in priority order */
|
||||||
|
while (towake != NULL) {
|
||||||
|
th = towake;
|
||||||
|
towake = towake->p_nextwaiting;
|
||||||
|
th->p_nextwaiting = NULL;
|
||||||
|
restart(th);
|
||||||
|
}
|
||||||
|
}
|
38
linuxthreads/semaphore.h
Normal file
38
linuxthreads/semaphore.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
#ifndef _SEMAPHORE_H
|
||||||
|
#define _SEMAPHORE_H 1
|
||||||
|
|
||||||
|
#include <features.h>
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#define SEM_VALUE_MAX INT_MAX
|
||||||
|
|
||||||
|
/* Get the semaphore structure definition. */
|
||||||
|
#include <bits/semaphore.h>
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
extern int sem_init __P((sem_t *__sem, int __pshared, unsigned int __value));
|
||||||
|
extern int sem_destroy __P((sem_t *__sem));
|
||||||
|
extern int sem_wait __P((sem_t *__sem));
|
||||||
|
extern int sem_trywait __P((sem_t *__sem));
|
||||||
|
extern int sem_post __P((sem_t *__sem));
|
||||||
|
extern int sem_getvalue __P((sem_t *__sem, int *__sval));
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif /* semaphore.h */
|
2
linuxthreads/shlib-versions
Normal file
2
linuxthreads/shlib-versions
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Xavier Leroy's Linux clone based thread library.
|
||||||
|
.*-.*-linux.* libpthread=0
|
148
linuxthreads/signals.c
Normal file
148
linuxthreads/signals.c
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Handling of signals */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
|
||||||
|
int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
|
||||||
|
{
|
||||||
|
sigset_t mask;
|
||||||
|
|
||||||
|
if (newmask != NULL) {
|
||||||
|
mask = *newmask;
|
||||||
|
/* Don't allow PTHREAD_SIG_RESTART to be unmasked.
|
||||||
|
Don't allow PTHREAD_SIG_CANCEL to be masked. */
|
||||||
|
switch(how) {
|
||||||
|
case SIG_SETMASK:
|
||||||
|
sigaddset(&mask, PTHREAD_SIG_RESTART);
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_CANCEL);
|
||||||
|
break;
|
||||||
|
case SIG_BLOCK:
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_CANCEL);
|
||||||
|
break;
|
||||||
|
case SIG_UNBLOCK:
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_RESTART);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newmask = &mask;
|
||||||
|
}
|
||||||
|
if (sigprocmask(how, newmask, oldmask) == -1)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_kill(pthread_t thread, int signo)
|
||||||
|
{
|
||||||
|
pthread_handle handle = thread_handle(thread);
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
acquire(&handle->h_spinlock);
|
||||||
|
if (invalid_handle(handle, thread)) {
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
return ESRCH;
|
||||||
|
}
|
||||||
|
pid = handle->h_descr->p_pid;
|
||||||
|
release(&handle->h_spinlock);
|
||||||
|
if (kill(pid, signo) == -1)
|
||||||
|
return errno;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The set of signals on which some thread is doing a sigwait */
|
||||||
|
static sigset_t sigwaited;
|
||||||
|
static pthread_mutex_t sigwaited_mut = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static pthread_cond_t sigwaited_changed = PTHREAD_COND_INITIALIZER;
|
||||||
|
|
||||||
|
int sigwait(const sigset_t * set, int * sig)
|
||||||
|
{
|
||||||
|
volatile pthread_descr self = thread_self();
|
||||||
|
sigset_t mask;
|
||||||
|
int s;
|
||||||
|
struct sigaction action, saved_signals[NSIG];
|
||||||
|
sigjmp_buf jmpbuf;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sigwaited_mut);
|
||||||
|
/* Make sure no other thread is waiting on our signals */
|
||||||
|
test_again:
|
||||||
|
for (s = 1; s < NSIG; s++) {
|
||||||
|
if (sigismember(set, s) && sigismember(&sigwaited, s)) {
|
||||||
|
pthread_cond_wait(&sigwaited_changed, &sigwaited_mut);
|
||||||
|
goto test_again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Get ready to block all signals except those in set
|
||||||
|
and the cancellation signal */
|
||||||
|
sigfillset(&mask);
|
||||||
|
sigdelset(&mask, PTHREAD_SIG_CANCEL);
|
||||||
|
/* Signals in set are assumed blocked on entrance */
|
||||||
|
/* Install our signal handler on all signals in set,
|
||||||
|
and unblock them in mask.
|
||||||
|
Also mark those signals as being sigwaited on */
|
||||||
|
for (s = 1; s < NSIG; s++) {
|
||||||
|
if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
|
||||||
|
sigdelset(&mask, s);
|
||||||
|
action.sa_handler = __pthread_sighandler;
|
||||||
|
sigemptyset(&action.sa_mask);
|
||||||
|
action.sa_flags = 0;
|
||||||
|
sigaction(s, &action, &(saved_signals[s]));
|
||||||
|
sigaddset(&sigwaited, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&sigwaited_mut);
|
||||||
|
|
||||||
|
/* Test for cancellation */
|
||||||
|
if (sigsetjmp(jmpbuf, 1) == 0) {
|
||||||
|
self->p_cancel_jmp = &jmpbuf;
|
||||||
|
if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
|
||||||
|
/* Reset the signal count */
|
||||||
|
self->p_signal = 0;
|
||||||
|
/* Unblock the signals and wait for them */
|
||||||
|
sigsuspend(&mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->p_cancel_jmp = NULL;
|
||||||
|
/* The signals are now reblocked. Restore the sighandlers. */
|
||||||
|
pthread_mutex_lock(&sigwaited_mut);
|
||||||
|
for (s = 1; s < NSIG; s++) {
|
||||||
|
if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
|
||||||
|
sigaction(s, &(saved_signals[s]), NULL);
|
||||||
|
sigdelset(&sigwaited, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_cond_broadcast(&sigwaited_changed);
|
||||||
|
pthread_mutex_unlock(&sigwaited_mut);
|
||||||
|
/* Check for cancellation */
|
||||||
|
pthread_testcancel();
|
||||||
|
/* We should have self->p_signal != 0 and equal to the signal received */
|
||||||
|
*sig = self->p_signal;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int raise (int sig)
|
||||||
|
{
|
||||||
|
int retcode = pthread_kill(pthread_self(), sig);
|
||||||
|
if (retcode == 0)
|
||||||
|
return 0;
|
||||||
|
else {
|
||||||
|
errno = retcode;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
174
linuxthreads/specific.c
Normal file
174
linuxthreads/specific.c
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Thread-specific data */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "pthread.h"
|
||||||
|
#include "internals.h"
|
||||||
|
|
||||||
|
typedef void (*destr_function)(void *);
|
||||||
|
|
||||||
|
/* Table of keys. */
|
||||||
|
|
||||||
|
struct pthread_key_struct {
|
||||||
|
int in_use; /* already allocated? */
|
||||||
|
destr_function destr; /* destruction routine */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] =
|
||||||
|
{ { 0, NULL } };
|
||||||
|
|
||||||
|
/* Mutex to protect access to pthread_keys */
|
||||||
|
|
||||||
|
static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
/* Create a new key */
|
||||||
|
|
||||||
|
int __pthread_key_create(pthread_key_t * key, destr_function destr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&pthread_keys_mutex);
|
||||||
|
for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
|
||||||
|
if (! pthread_keys[i].in_use) {
|
||||||
|
/* Mark key in use */
|
||||||
|
pthread_keys[i].in_use = 1;
|
||||||
|
pthread_keys[i].destr = destr;
|
||||||
|
pthread_mutex_unlock(&pthread_keys_mutex);
|
||||||
|
*key = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pthread_keys_mutex);
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_key_create, pthread_key_create)
|
||||||
|
|
||||||
|
/* Delete a key */
|
||||||
|
|
||||||
|
int pthread_key_delete(pthread_key_t key)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
pthread_descr th;
|
||||||
|
unsigned int idx1st, idx2nd;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&pthread_keys_mutex);
|
||||||
|
if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) {
|
||||||
|
pthread_mutex_unlock(&pthread_keys_mutex);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
pthread_keys[key].in_use = 0;
|
||||||
|
pthread_keys[key].destr = NULL;
|
||||||
|
/* Set the value of the key to NULL in all running threads, so that
|
||||||
|
if the key is reallocated later by pthread_key_create, its
|
||||||
|
associated values will be NULL in all threads. */
|
||||||
|
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
|
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
|
th = self;
|
||||||
|
do {
|
||||||
|
if (th->p_specific[idx1st] != NULL)
|
||||||
|
th->p_specific[idx1st][idx2nd] = NULL;
|
||||||
|
th = th->p_nextlive;
|
||||||
|
} while (th != self);
|
||||||
|
pthread_mutex_unlock(&pthread_keys_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the value of a key */
|
||||||
|
|
||||||
|
int __pthread_setspecific(pthread_key_t key, const void * pointer)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
unsigned int idx1st, idx2nd;
|
||||||
|
|
||||||
|
if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use)
|
||||||
|
return EINVAL;
|
||||||
|
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
|
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
|
if (self->p_specific[idx1st] == NULL) {
|
||||||
|
self->p_specific[idx1st] =
|
||||||
|
calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *));
|
||||||
|
if (self->p_specific[idx1st] == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
self->p_specific[idx1st][idx2nd] = (void *) pointer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_setspecific, pthread_setspecific)
|
||||||
|
|
||||||
|
/* Get the value of a key */
|
||||||
|
|
||||||
|
void * __pthread_getspecific(pthread_key_t key)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
unsigned int idx1st, idx2nd;
|
||||||
|
|
||||||
|
if (key >= PTHREAD_KEYS_MAX)
|
||||||
|
return NULL;
|
||||||
|
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
|
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
|
if (self->p_specific[idx1st] == NULL || !pthread_keys[key].in_use)
|
||||||
|
return NULL;
|
||||||
|
return self->p_specific[idx1st][idx2nd];
|
||||||
|
}
|
||||||
|
weak_alias (__pthread_getspecific, pthread_getspecific)
|
||||||
|
|
||||||
|
/* Call the destruction routines on all keys */
|
||||||
|
|
||||||
|
void __pthread_destroy_specifics()
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
int i, j, round, found_nonzero;
|
||||||
|
destr_function destr;
|
||||||
|
void * data;
|
||||||
|
|
||||||
|
for (round = 0, found_nonzero = 1;
|
||||||
|
found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
|
||||||
|
round++) {
|
||||||
|
found_nonzero = 0;
|
||||||
|
for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
|
||||||
|
if (self->p_specific[i] != NULL)
|
||||||
|
for (j = 0; j < PTHREAD_KEY_2NDLEVEL_SIZE; j++) {
|
||||||
|
destr = pthread_keys[i * PTHREAD_KEY_2NDLEVEL_SIZE + j].destr;
|
||||||
|
data = self->p_specific[i][j];
|
||||||
|
if (destr != NULL && data != NULL) {
|
||||||
|
self->p_specific[i][j] = NULL;
|
||||||
|
destr(data);
|
||||||
|
found_nonzero = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
|
||||||
|
if (self->p_specific[i] != NULL) free(self->p_specific[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thread-specific data for libc. */
|
||||||
|
|
||||||
|
int __libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
|
||||||
|
self->p_libc_specific[key] = (void *) pointer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * __libc_internal_tsd_get(enum __libc_tsd_key_t key)
|
||||||
|
{
|
||||||
|
pthread_descr self = thread_self();
|
||||||
|
|
||||||
|
return self->p_libc_specific[key];
|
||||||
|
}
|
30
linuxthreads/spinlock.h
Normal file
30
linuxthreads/spinlock.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) and */
|
||||||
|
/* Richard Henderson (rth@tamu.edu) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
/* Spin locks */
|
||||||
|
|
||||||
|
static inline void acquire(int * spinlock)
|
||||||
|
{
|
||||||
|
while (testandset(spinlock)) __sched_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void release(int * spinlock)
|
||||||
|
{
|
||||||
|
#ifndef RELEASE
|
||||||
|
*spinlock = 0;
|
||||||
|
#else
|
||||||
|
RELEASE(spinlock);
|
||||||
|
#endif
|
||||||
|
}
|
31
linuxthreads/sysdeps/alpha/bits/semaphore.h
Normal file
31
linuxthreads/sysdeps/alpha/bits/semaphore.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#ifndef _SEMAPHORE_H
|
||||||
|
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Due to the implementation of the load-locked/store-conditional
|
||||||
|
instructions, we cannot pack semaphores closer than a cache line
|
||||||
|
or risk threads deadlocking on unrelated semaphores. */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long int sem_status;
|
||||||
|
long int sem_reserved[3];
|
||||||
|
} sem_t;
|
102
linuxthreads/sysdeps/alpha/pt-machine.h
Normal file
102
linuxthreads/sysdeps/alpha/pt-machine.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
Alpha version.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include <asm/pal.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline long testandset(int *spinlock)
|
||||||
|
{
|
||||||
|
long ret, temp;
|
||||||
|
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"/* Inline spinlock test & set */\n"
|
||||||
|
"1:\t"
|
||||||
|
"ldl_l %0,%3\n\t"
|
||||||
|
"bne %0,2f\n\t"
|
||||||
|
"or $31,1,%1\n\t"
|
||||||
|
"stl_c %1,%2\n\t"
|
||||||
|
"beq %1,1b\n"
|
||||||
|
"2:\tmb\n"
|
||||||
|
"/* End spinlock test & set */"
|
||||||
|
: "=&r"(ret), "=&r"(temp), "=m"(*spinlock)
|
||||||
|
: "m"(*spinlock)
|
||||||
|
: "memory");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spinlock release; default is just set to zero. */
|
||||||
|
#define RELEASE(spinlock) \
|
||||||
|
__asm__ __volatile__("mb" : : : "memory"); \
|
||||||
|
*spinlock = 0
|
||||||
|
|
||||||
|
|
||||||
|
/* Begin allocating thread stacks at this address. Default is to allocate
|
||||||
|
them just below the initial program stack. */
|
||||||
|
#define THREAD_STACK_START_ADDRESS 0x40000000000
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char *stack_pointer __asm__("$30");
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the thread descriptor for the current thread. */
|
||||||
|
#define THREAD_SELF \
|
||||||
|
({ \
|
||||||
|
register pthread_descr __self __asm__("$0"); \
|
||||||
|
__asm__ ("call_pal %1" : "=r"(__self) : "i"(PAL_rduniq) : "$0"); \
|
||||||
|
__self; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/* Initialize the thread-unique value. */
|
||||||
|
#define INIT_THREAD_SELF(descr) \
|
||||||
|
{ \
|
||||||
|
register pthread_descr __self __asm__("$16") = (descr); \
|
||||||
|
__asm__ __volatile__ ("call_pal %1" : : "r"(__self), "i"(PAL_wruniq)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Compare-and-swap for semaphores. */
|
||||||
|
|
||||||
|
#define HAS_COMPARE_AND_SWAP
|
||||||
|
extern inline int __compare_and_swap(long * p, long oldval, long newval)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"/* Inline compare & swap */\n"
|
||||||
|
"1:\t"
|
||||||
|
"ldq_l %0,%4\n\t"
|
||||||
|
"cmpeq %0,%2,%0\n\t"
|
||||||
|
"beq %0,2f\n\t"
|
||||||
|
"mov %3,%0\n\t"
|
||||||
|
"stq_c %0,%1\n\t"
|
||||||
|
"beq %0,1b\n\t"
|
||||||
|
"2:\tmb\n"
|
||||||
|
"/* End compare & swap */"
|
||||||
|
: "=&r"(ret), "=m"(*p)
|
||||||
|
: "r"(oldval), "r"(newval), "m"(*p));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
1
linuxthreads/sysdeps/arm/Implies
Normal file
1
linuxthreads/sysdeps/arm/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/no-cmpxchg
|
44
linuxthreads/sysdeps/arm/pt-machine.h
Normal file
44
linuxthreads/sysdeps/arm/pt-machine.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
ARM version.
|
||||||
|
Copyright (C) 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Philip Blundell <philb@gnu.org>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
|
||||||
|
/* This will not work on ARM1 or ARM2 because SWP is lacking on those
|
||||||
|
machines. Unfortunately we have no way to detect this at compile
|
||||||
|
time; let's hope nobody tries to use one. */
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline int
|
||||||
|
testandset (int *spinlock)
|
||||||
|
{
|
||||||
|
register unsigned int ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("swp %0, %1, [%2]"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "0"(1), "r"(spinlock));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__ ("sp");
|
1
linuxthreads/sysdeps/i386/Implies
Normal file
1
linuxthreads/sysdeps/i386/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/no-cmpxchg
|
93
linuxthreads/sysdeps/i386/pt-machine.h
Normal file
93
linuxthreads/sysdeps/i386/pt-machine.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
i386 version.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline int
|
||||||
|
testandset (int *spinlock)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("xchgl %0, %1"
|
||||||
|
: "=r"(ret), "=m"(*spinlock)
|
||||||
|
: "0"(1), "m"(*spinlock));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__ ("%esp");
|
||||||
|
|
||||||
|
|
||||||
|
/* Compare-and-swap for semaphores.
|
||||||
|
Available on the 486 and above, but not on the 386.
|
||||||
|
We test dynamically whether it's available or not. */
|
||||||
|
|
||||||
|
#define HAS_COMPARE_AND_SWAP
|
||||||
|
#define TEST_FOR_COMPARE_AND_SWAP
|
||||||
|
|
||||||
|
extern inline int
|
||||||
|
__compare_and_swap (long int *p, long int oldval, long int newval)
|
||||||
|
{
|
||||||
|
char ret;
|
||||||
|
long int readval;
|
||||||
|
|
||||||
|
__asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
|
||||||
|
: "=q" (ret), "=m" (*p), "=a" (readval)
|
||||||
|
: "r" (newval), "m" (*p), "a" (oldval));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern inline int
|
||||||
|
get_eflags (void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
__asm__ __volatile__ ("pushfl; popl %0" : "=r" (res) : );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern inline void
|
||||||
|
set_eflags (int newflags)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__ ("pushl %0; popfl" : : "r" (newflags) : "cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern inline int
|
||||||
|
compare_and_swap_is_available (void)
|
||||||
|
{
|
||||||
|
int oldflags = get_eflags ();
|
||||||
|
int changed;
|
||||||
|
/* Flip AC bit in EFLAGS. */
|
||||||
|
set_eflags (oldflags ^ 0x40000);
|
||||||
|
/* See if bit changed. */
|
||||||
|
changed = (get_eflags () ^ oldflags) & 0x40000;
|
||||||
|
/* Restore EFLAGS. */
|
||||||
|
set_eflags (oldflags);
|
||||||
|
/* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
|
||||||
|
Otherwise, it's a 486 or above and it has cmpxchg. */
|
||||||
|
return changed != 0;
|
||||||
|
}
|
1
linuxthreads/sysdeps/m68k/Implies
Normal file
1
linuxthreads/sysdeps/m68k/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/cmpxchg
|
58
linuxthreads/sysdeps/m68k/pt-machine.h
Normal file
58
linuxthreads/sysdeps/m68k/pt-machine.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
m68k version.
|
||||||
|
Copyright (C) 1996 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||||
|
not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline int
|
||||||
|
testandset (int *spinlock)
|
||||||
|
{
|
||||||
|
char ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("tas %1; sne %0"
|
||||||
|
: "=dm"(ret), "=m"(*spinlock)
|
||||||
|
: "m"(*spinlock)
|
||||||
|
: "cc");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__ ("%sp");
|
||||||
|
|
||||||
|
|
||||||
|
/* Compare-and-swap for semaphores. */
|
||||||
|
|
||||||
|
#define HAS_COMPARE_AND_SWAP
|
||||||
|
extern inline int
|
||||||
|
__compare_and_swap (long int *p, long int oldval, long int newval)
|
||||||
|
{
|
||||||
|
char ret;
|
||||||
|
long int readval;
|
||||||
|
|
||||||
|
__asm__ __volatile__ ("casl %2, %3, %1; seq %0"
|
||||||
|
: "=dm" (ret), "=m" (*p), "=d" (readval)
|
||||||
|
: "d" (newval), "m" (*p), "2" (oldval));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
1
linuxthreads/sysdeps/mips/Implies
Normal file
1
linuxthreads/sysdeps/mips/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/cmpxchg
|
84
linuxthreads/sysdeps/mips/pt-machine.h
Normal file
84
linuxthreads/sysdeps/mips/pt-machine.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>.
|
||||||
|
Based on the Alpha version by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||||
|
not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
TODO: This version makes use of MIPS ISA 2 features. It won't
|
||||||
|
work on ISA 1. These machines will have to take the overhead of
|
||||||
|
a sysmips(MIPS_ATOMIC_SET, ...) syscall which isn't implemented
|
||||||
|
yet correctly. There is however a better solution for R3000
|
||||||
|
uniprocessor machines possible. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline long testandset(int *spinlock)
|
||||||
|
{
|
||||||
|
long ret, temp;
|
||||||
|
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"# Inline spinlock test & set\n\t"
|
||||||
|
".set\tmips2\n"
|
||||||
|
"1:\tll\t%0,%3\n\t"
|
||||||
|
"bnez\t%0,2f\n\t"
|
||||||
|
".set\tnoreorder\n\t"
|
||||||
|
"li\t%1,1\n\t"
|
||||||
|
".set\treorder\n\t"
|
||||||
|
"sc\t%1,%2\n\t"
|
||||||
|
"beqz\t%1,1b\n"
|
||||||
|
"2:\t.set\tmips0\n\t"
|
||||||
|
"/* End spinlock test & set */"
|
||||||
|
: "=&r"(ret), "=&r" (temp), "=m"(*spinlock)
|
||||||
|
: "m"(*spinlock)
|
||||||
|
: "memory");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__ ("$29");
|
||||||
|
|
||||||
|
|
||||||
|
/* Compare-and-swap for semaphores. */
|
||||||
|
|
||||||
|
#define HAS_COMPARE_AND_SWAP
|
||||||
|
extern inline int __compare_and_swap(long * p, long oldval, long newval)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"/* Inline compare & swap */\n\t"
|
||||||
|
".set\tmips2\n"
|
||||||
|
"1:\tll\t%0,%4\n\t"
|
||||||
|
".set\tnoreorder\n\t"
|
||||||
|
"bne\t%0,%2,2f\n\t"
|
||||||
|
"move\t%0,%3\n\t"
|
||||||
|
".set\treorder\n\t"
|
||||||
|
"sc\t%0,%1\n\t"
|
||||||
|
"beqz\t%0,1b\n"
|
||||||
|
"2:\t.set\tmips0\n\t"
|
||||||
|
"/* End compare & swap */"
|
||||||
|
: "=&r"(ret), "=m"(*p)
|
||||||
|
: "r"(oldval), "r"(newval), "m"(*p));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
1
linuxthreads/sysdeps/powerpc/Implies
Normal file
1
linuxthreads/sysdeps/powerpc/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/cmpxchg
|
32
linuxthreads/sysdeps/powerpc/bits/semaphore.h
Normal file
32
linuxthreads/sysdeps/powerpc/bits/semaphore.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#ifndef _SEMAPHORE_H
|
||||||
|
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Due to the implementation of the load-locked/store-conditional
|
||||||
|
instructions, we cannot pack semaphores closer than a cache line
|
||||||
|
or risk threads deadlocking on unrelated semaphores. */
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int sem_status;
|
||||||
|
int sem_reserved[7];
|
||||||
|
} sem_t;
|
84
linuxthreads/sysdeps/powerpc/pt-machine.h
Normal file
84
linuxthreads/sysdeps/powerpc/pt-machine.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
powerpc version.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||||
|
not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
/* These routines are from Appendix G of the 'PowerPC 601 RISC Microprocessor
|
||||||
|
User's Manual', by IBM and Motorola. */
|
||||||
|
|
||||||
|
/* For multiprocessor systems, we want to ensure all memory accesses
|
||||||
|
are completed before we reset a lock. */
|
||||||
|
#if 0
|
||||||
|
/* on non multiprocessor systems, you can just: */
|
||||||
|
#define sync() /* nothing */
|
||||||
|
#else
|
||||||
|
#define sync() __asm__ __volatile__ ("sync")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline int
|
||||||
|
testandset (int *spinlock)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sync();
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"0: lwarx %0,0,%1 ;"
|
||||||
|
" cmpwi %0,0;"
|
||||||
|
" bne 1f;"
|
||||||
|
" stwcx. %2,0,%1;"
|
||||||
|
" bne- 0b;"
|
||||||
|
"1: "
|
||||||
|
: "=&r"(ret)
|
||||||
|
: "r"(spinlock), "r"(1)
|
||||||
|
: "cr0", "memory");
|
||||||
|
sync();
|
||||||
|
|
||||||
|
return ret != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__ ("r1");
|
||||||
|
|
||||||
|
/* Compare-and-swap for semaphores. */
|
||||||
|
/* note that test-and-set(x) is the same as compare-and-swap(x, 0, 1) */
|
||||||
|
|
||||||
|
#define HAS_COMPARE_AND_SWAP
|
||||||
|
extern inline int
|
||||||
|
__compare_and_swap (int *p, int oldval, int newval)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sync();
|
||||||
|
__asm__ __volatile__(
|
||||||
|
"0: lwarx %0,0,%1 ;"
|
||||||
|
" xor. %0,%3,%0;"
|
||||||
|
" bne 1f;"
|
||||||
|
" stwcx. %2,0,%1;"
|
||||||
|
" bne- 0b;"
|
||||||
|
"1: "
|
||||||
|
: "=&r"(ret)
|
||||||
|
: "r"(p), "r"(newval), "r"(oldval)
|
||||||
|
: "cr0", "memory");
|
||||||
|
sync();
|
||||||
|
return ret == 0;
|
||||||
|
}
|
3
linuxthreads/sysdeps/pthread/Makefile
Normal file
3
linuxthreads/sysdeps/pthread/Makefile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ifeq ($(subdir),libio)
|
||||||
|
sysdep_headers += bits/stdio-lock.h
|
||||||
|
endif
|
208
linuxthreads/sysdeps/pthread/bits/libc-lock.h
Normal file
208
linuxthreads/sysdeps/pthread/bits/libc-lock.h
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/* libc-internal interface for mutex locks. LinuxThreads version.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#ifndef _BITS_LIBC_LOCK_H
|
||||||
|
#define _BITS_LIBC_LOCK_H 1
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* Mutex type. */
|
||||||
|
#ifdef _LIBC
|
||||||
|
typedef pthread_mutex_t __libc_lock_t;
|
||||||
|
#else
|
||||||
|
typedef struct __libc_lock_opaque__ __libc_lock_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Type for key to thread-specific data. */
|
||||||
|
typedef pthread_key_t __libc_key_t;
|
||||||
|
|
||||||
|
/* Define a lock variable NAME with storage class CLASS. The lock must be
|
||||||
|
initialized with __libc_lock_init before it can be used (or define it
|
||||||
|
with __libc_lock_define_initialized, below). Use `extern' for CLASS to
|
||||||
|
declare a lock defined in another module. In public structure
|
||||||
|
definitions you must use a pointer to the lock structure (i.e., NAME
|
||||||
|
begins with a `*'), because its storage size will not be known outside
|
||||||
|
of libc. */
|
||||||
|
#define __libc_lock_define(CLASS,NAME) \
|
||||||
|
CLASS __libc_lock_t NAME;
|
||||||
|
|
||||||
|
/* Define an initialized lock variable NAME with storage class CLASS. */
|
||||||
|
#define __libc_lock_define_initialized(CLASS,NAME) \
|
||||||
|
CLASS __libc_lock_t NAME = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
/* Define an initialized recursive lock variable NAME with storage
|
||||||
|
class CLASS. */
|
||||||
|
#define __libc_lock_define_initialized_recursive(CLASS,NAME) \
|
||||||
|
CLASS __libc_lock_t NAME = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
|
||||||
|
/* Initialize the named lock variable, leaving it in a consistent, unlocked
|
||||||
|
state. */
|
||||||
|
#define __libc_lock_init(NAME) \
|
||||||
|
(__pthread_mutex_init != NULL ? __pthread_mutex_init (&(NAME), NULL) : 0);
|
||||||
|
|
||||||
|
/* Same as last but this time we initialize a recursive mutex. */
|
||||||
|
#define __libc_lock_init_recursive(NAME) \
|
||||||
|
do { \
|
||||||
|
if (__pthread_mutex_init != NULL) \
|
||||||
|
{ \
|
||||||
|
pthread_mutexattr_t __attr; \
|
||||||
|
__pthread_mutexattr_init (&__attr); \
|
||||||
|
__pthread_mutexattr_setkind_np (&__attr, PTHREAD_MUTEX_RECURSIVE_NP); \
|
||||||
|
__pthread_mutex_init (&(NAME), &__attr); \
|
||||||
|
__pthread_mutexattr_destroy (&__attr); \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
/* Finalize the named lock variable, which must be locked. It cannot be
|
||||||
|
used again until __libc_lock_init is called again on it. This must be
|
||||||
|
called on a lock variable before the containing storage is reused. */
|
||||||
|
#define __libc_lock_fini(NAME) \
|
||||||
|
(__pthread_mutex_destroy != NULL ? __pthread_mutex_destroy (&(NAME)) : 0);
|
||||||
|
|
||||||
|
/* Finalize recursive named lock. */
|
||||||
|
#define __libc_lock_fini_recursive(NAME) __libc_lock_fini (NAME)
|
||||||
|
|
||||||
|
/* Lock the named lock variable. */
|
||||||
|
#define __libc_lock_lock(NAME) \
|
||||||
|
(__pthread_mutex_lock != NULL ? __pthread_mutex_lock (&(NAME)) : 0);
|
||||||
|
|
||||||
|
/* Lock the recursive named lock variable. */
|
||||||
|
#define __libc_lock_lock_recursive(NAME) __libc_lock_lock (NAME)
|
||||||
|
|
||||||
|
/* Try to lock the named lock variable. */
|
||||||
|
#define __libc_lock_trylock(NAME) \
|
||||||
|
(__pthread_mutex_trylock != NULL ? __pthread_mutex_trylock (&(NAME)) : 0)
|
||||||
|
|
||||||
|
/* Try to lock the recursive named lock variable. */
|
||||||
|
#define __libc_lock_trylock_recursive(NAME) __libc_lock_trylock (NAME)
|
||||||
|
|
||||||
|
/* Unlock the named lock variable. */
|
||||||
|
#define __libc_lock_unlock(NAME) \
|
||||||
|
(__pthread_mutex_unlock != NULL ? __pthread_mutex_unlock (&(NAME)) : 0);
|
||||||
|
|
||||||
|
/* Unlock the recursive named lock variable. */
|
||||||
|
#define __libc_lock_unlock_recursive(NAME) __libc_lock_unlock (NAME)
|
||||||
|
|
||||||
|
|
||||||
|
/* Define once control variable. */
|
||||||
|
#define __libc_once_define(CLASS, NAME) \
|
||||||
|
CLASS pthread_once_t NAME = PTHREAD_ONCE_INIT
|
||||||
|
|
||||||
|
/* Call handler iff the first call. */
|
||||||
|
#define __libc_once(ONCE_CONTROL, INIT_FUNCTION) \
|
||||||
|
do { \
|
||||||
|
if (__pthread_once != NULL) \
|
||||||
|
__pthread_once (&(ONCE_CONTROL), (INIT_FUNCTION)); \
|
||||||
|
else if ((ONCE_CONTROL) == 0) { \
|
||||||
|
INIT_FUNCTION (); \
|
||||||
|
(ONCE_CONTROL) = 1; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Start critical region with cleanup. */
|
||||||
|
#define __libc_cleanup_region_start(FCT, ARG) \
|
||||||
|
{ struct _pthread_cleanup_buffer _buffer; \
|
||||||
|
if (_pthread_cleanup_push_defer != NULL) { \
|
||||||
|
_pthread_cleanup_push_defer (&_buffer, (FCT), (ARG)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End critical region with cleanup. */
|
||||||
|
#define __libc_cleanup_region_end(DOIT) \
|
||||||
|
if (_pthread_cleanup_push_defer != NULL) { \
|
||||||
|
_pthread_cleanup_pop_restore (&_buffer, (DOIT)); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create thread-specific key. */
|
||||||
|
#define __libc_key_create(KEY, DESTRUCTOR) \
|
||||||
|
(__pthread_key_create != NULL ? __pthread_key_create (KEY, DESTRUCTOR) : 1)
|
||||||
|
|
||||||
|
/* Get thread-specific data. */
|
||||||
|
#define __libc_getspecific(KEY) \
|
||||||
|
(__pthread_getspecific != NULL ? __pthread_getspecific (KEY) : NULL)
|
||||||
|
|
||||||
|
/* Set thread-specific data. */
|
||||||
|
#define __libc_setspecific(KEY, VALUE) \
|
||||||
|
(__pthread_setspecific != NULL ? __pthread_setspecific (KEY, VALUE) : 0)
|
||||||
|
|
||||||
|
#ifdef _LIBC
|
||||||
|
|
||||||
|
/* Fast thread-specific data internal to libc. */
|
||||||
|
enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, _LIBC_TSD_KEY_N };
|
||||||
|
|
||||||
|
extern void *__libc_internal_tsd_get __P ((enum __libc_tsd_key_t));
|
||||||
|
extern int __libc_internal_tsd_set __P ((enum __libc_tsd_key_t,
|
||||||
|
__const void *));
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Register handlers to execute before and after `fork'. */
|
||||||
|
#define __libc_atfork(PREPARE, PARENT, CHILD) \
|
||||||
|
(__pthread_atfork != NULL ? __pthread_atfork (PREPARE, PARENT, CHILD) : 0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Make the pthread functions weak so that we can elide them from
|
||||||
|
single-threaded processes. */
|
||||||
|
#ifdef weak_extern
|
||||||
|
weak_extern (__pthread_mutex_init)
|
||||||
|
weak_extern (__pthread_mutex_destroy)
|
||||||
|
weak_extern (__pthread_mutex_lock)
|
||||||
|
weak_extern (__pthread_mutex_trylock)
|
||||||
|
weak_extern (__pthread_mutex_unlock)
|
||||||
|
weak_extern (__pthread_mutexattr_init)
|
||||||
|
weak_extern (__pthread_mutexattr_destroy)
|
||||||
|
weak_extern (__pthread_mutexattr_setkind_np)
|
||||||
|
weak_extern (__pthread_key_create)
|
||||||
|
weak_extern (__pthread_setspecific)
|
||||||
|
weak_extern (__pthread_getspecific)
|
||||||
|
weak_extern (__libc_internal_tsd_get)
|
||||||
|
weak_extern (__libc_internal_tsd_set)
|
||||||
|
weak_extern (__pthread_once)
|
||||||
|
weak_extern (__pthread_initialize)
|
||||||
|
weak_extern (__pthread_atfork)
|
||||||
|
weak_extern (_pthread_cleanup_push_defer)
|
||||||
|
weak_extern (_pthread_cleanup_pop_restore)
|
||||||
|
#else
|
||||||
|
# pragma weak __pthread_mutex_init
|
||||||
|
# pragma weak __pthread_mutex_destroy
|
||||||
|
# pragma weak __pthread_mutex_lock
|
||||||
|
# pragma weak __pthread_mutex_trylock
|
||||||
|
# pragma weak __pthread_mutex_unlock
|
||||||
|
# pragma weak __pthread_mutexattr_init
|
||||||
|
# pragma weak __pthread_mutexattr_destroy
|
||||||
|
# pragma weak __pthread_mutexattr_setkind_np
|
||||||
|
# pragma weak __pthread_key_create
|
||||||
|
# pragma weak __pthread_setspecific
|
||||||
|
# pragma weak __pthread_getspecific
|
||||||
|
# pragma weak __libc_internal_tsd_get
|
||||||
|
# pragma weak __libc_internal_tsd_set
|
||||||
|
# pragma weak __pthread_once
|
||||||
|
# pragma weak __pthread_initialize
|
||||||
|
# pragma weak __pthread_atfork
|
||||||
|
# pragma weak _pthread_cleanup_push_defer
|
||||||
|
# pragma weak _pthread_cleanup_pop_restore
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* We need portable names for some functions. E.g., when they are
|
||||||
|
used as argument to __libc_cleanup_region_start. */
|
||||||
|
#define __libc_mutex_unlock __pthread_mutex_unlock
|
||||||
|
|
||||||
|
#endif /* bits/libc-lock.h */
|
35
linuxthreads/sysdeps/pthread/bits/stdio-lock.h
Normal file
35
linuxthreads/sysdeps/pthread/bits/stdio-lock.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* Thread package specific definitions of stream lock type.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
typedef pthread_mutex_t _IO_lock_t;
|
||||||
|
|
||||||
|
/* We need recursive (counting) mutexes. */
|
||||||
|
#define _IO_lock_initializer PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
|
||||||
|
|
||||||
|
|
||||||
|
#define _IO_cleanup_region_start(_fct, _fp) \
|
||||||
|
__libc_cleanup_region_start (_fct, _fp)
|
||||||
|
#define _IO_cleanup_region_end(_doit) \
|
||||||
|
__libc_cleanup_region_end (_doit)
|
||||||
|
#define _IO_lock_init(_name) \
|
||||||
|
__libc_lock_init_recursive (_name)
|
||||||
|
#define _IO_lock_fini(_name) \
|
||||||
|
__libc_lock_fini_recursive (_name)
|
26
linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h
Normal file
26
linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#ifndef _SEMAPHORE_H
|
||||||
|
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long int sem_status;
|
||||||
|
} sem_t;
|
27
linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h
Normal file
27
linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#ifndef _SEMAPHORE_H
|
||||||
|
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long int sem_status;
|
||||||
|
int sem_spinlock;
|
||||||
|
} sem_t;
|
578
linuxthreads/sysdeps/pthread/pthread.h
Normal file
578
linuxthreads/sysdeps/pthread/pthread.h
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
/* Linuxthreads - a simple clone()-based implementation of Posix */
|
||||||
|
/* threads for Linux. */
|
||||||
|
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or */
|
||||||
|
/* modify it under the terms of the GNU Library General Public License */
|
||||||
|
/* as published by the Free Software Foundation; either version 2 */
|
||||||
|
/* of the License, or (at your option) any later version. */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU Library General Public License for more details. */
|
||||||
|
|
||||||
|
#ifndef _PTHREAD_H
|
||||||
|
#define _PTHREAD_H 1
|
||||||
|
|
||||||
|
#include <features.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define __need_sigset_t
|
||||||
|
#include <signal.h>
|
||||||
|
#define __need_timespec
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* Linux has no ENOTSUP error code. */
|
||||||
|
#ifndef ENOTSUP
|
||||||
|
#define ENOTSUP EOPNOTSUPP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
|
||||||
|
/*** Types ***/
|
||||||
|
|
||||||
|
/* Thread identifiers */
|
||||||
|
typedef unsigned long int pthread_t;
|
||||||
|
|
||||||
|
/* Thread descriptors */
|
||||||
|
typedef struct _pthread_descr_struct *_pthread_descr;
|
||||||
|
|
||||||
|
/* Waiting queues (not abstract because mutexes and conditions aren't). */
|
||||||
|
struct _pthread_queue
|
||||||
|
{
|
||||||
|
_pthread_descr head; /* First element, or NULL if queue empty. */
|
||||||
|
_pthread_descr tail; /* Last element, or NULL if queue empty. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Mutexes (not abstract because of PTHREAD_MUTEX_INITIALIZER). */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int m_spinlock; /* Spin lock to guarantee mutual exclusion. */
|
||||||
|
int m_count; /* 0 if free, > 0 if taken. */
|
||||||
|
_pthread_descr m_owner; /* Owner of mutex (for recursive mutexes) */
|
||||||
|
int m_kind; /* Kind of mutex */
|
||||||
|
struct _pthread_queue m_waiting; /* Threads waiting on this mutex. */
|
||||||
|
} pthread_mutex_t;
|
||||||
|
|
||||||
|
#define PTHREAD_MUTEX_INITIALIZER \
|
||||||
|
{0, 0, 0, PTHREAD_MUTEX_FAST_NP, {0, 0}}
|
||||||
|
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \
|
||||||
|
{0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, {0, 0}}
|
||||||
|
|
||||||
|
/* Conditions (not abstract because of PTHREAD_COND_INITIALIZER */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int c_spinlock; /* Spin lock to protect the queue. */
|
||||||
|
struct _pthread_queue c_waiting; /* Threads waiting on this condition. */
|
||||||
|
} pthread_cond_t;
|
||||||
|
|
||||||
|
#define PTHREAD_COND_INITIALIZER {0, {0, 0}}
|
||||||
|
|
||||||
|
#ifdef __USE_UNIX98
|
||||||
|
/* Read-write locks. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int rw_spinlock; /* Spin lock to guarantee mutual exclusion */
|
||||||
|
int rw_readers; /* Number of readers */
|
||||||
|
_pthread_descr rw_writer; /* Identity of writer, or NULL if none */
|
||||||
|
struct _pthread_queue rw_read_waiting; /* Threads waiting for reading */
|
||||||
|
struct _pthread_queue rw_write_waiting; /* Threads waiting for writing */
|
||||||
|
int rw_kind; /* Reader/Writer preference selection */
|
||||||
|
int rw_pshared; /* Shared between processes or not */
|
||||||
|
} pthread_rwlock_t;
|
||||||
|
|
||||||
|
# define PTHREAD_RWLOCK_INITIALIZER \
|
||||||
|
{ 0, 0, 0, {0, 0}, {0, 0}, \
|
||||||
|
PTHREAD_RWLOCK_DEFAULT_NP, PTHREAD_PROCESS_PRIVATE }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Attributes */
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PTHREAD_CREATE_JOINABLE,
|
||||||
|
PTHREAD_CREATE_DETACHED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PTHREAD_INHERIT_SCHED,
|
||||||
|
PTHREAD_EXPLICIT_SCHED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PTHREAD_SCOPE_SYSTEM,
|
||||||
|
PTHREAD_SCOPE_PROCESS
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int detachstate;
|
||||||
|
int schedpolicy;
|
||||||
|
struct sched_param schedparam;
|
||||||
|
int inheritsched;
|
||||||
|
int scope;
|
||||||
|
} pthread_attr_t;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PTHREAD_MUTEX_FAST_NP,
|
||||||
|
PTHREAD_MUTEX_RECURSIVE_NP,
|
||||||
|
PTHREAD_MUTEX_ERRORCHECK_NP
|
||||||
|
#ifdef __USE_UNIX98
|
||||||
|
,
|
||||||
|
PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,
|
||||||
|
PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
|
||||||
|
PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
|
||||||
|
PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int mutexkind;
|
||||||
|
} pthread_mutexattr_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int dummy;
|
||||||
|
} pthread_condattr_t;
|
||||||
|
|
||||||
|
#ifdef __USE_UNIX98
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PTHREAD_PROCESS_PRIVATE,
|
||||||
|
PTHREAD_PROCESS_SHARED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PTHREAD_RWLOCK_PREFER_READER_NP,
|
||||||
|
PTHREAD_RWLOCK_PREFER_WRITER_NP,
|
||||||
|
PTHREAD_RWLOCK_DEFAULT_NP = PTHREAD_RWLOCK_PREFER_WRITER_NP
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int lockkind;
|
||||||
|
int pshared;
|
||||||
|
} pthread_rwlockattr_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Keys for thread-specific data */
|
||||||
|
|
||||||
|
typedef unsigned int pthread_key_t;
|
||||||
|
|
||||||
|
/* Once-only execution */
|
||||||
|
|
||||||
|
typedef int pthread_once_t;
|
||||||
|
|
||||||
|
#define PTHREAD_ONCE_INIT 0
|
||||||
|
|
||||||
|
/* Cleanup buffers */
|
||||||
|
|
||||||
|
struct _pthread_cleanup_buffer
|
||||||
|
{
|
||||||
|
void (*routine) __P ((void *)); /* Function to call. */
|
||||||
|
void *arg; /* Its argument. */
|
||||||
|
int canceltype; /* Saved cancellation type. */
|
||||||
|
struct _pthread_cleanup_buffer *prev; /* Chaining of cleanup functions. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Cancellation */
|
||||||
|
|
||||||
|
enum { PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE };
|
||||||
|
enum { PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS };
|
||||||
|
#define PTHREAD_CANCELED ((void *) -1)
|
||||||
|
|
||||||
|
|
||||||
|
/* Function for handling threads. */
|
||||||
|
|
||||||
|
/* Create a thread with given attributes ATTR (or default attributes
|
||||||
|
if ATTR is NULL), and call function START_ROUTINE with given
|
||||||
|
arguments ARG. */
|
||||||
|
extern int pthread_create __P ((pthread_t *__thread,
|
||||||
|
__const pthread_attr_t *__attr,
|
||||||
|
void *(*__start_routine) (void *),
|
||||||
|
void *__arg));
|
||||||
|
|
||||||
|
/* Obtain the identifier of the current thread. */
|
||||||
|
extern pthread_t pthread_self __P ((void));
|
||||||
|
|
||||||
|
/* Compare two thread identifiers. */
|
||||||
|
extern int pthread_equal __P ((pthread_t __thread1, pthread_t __thread2));
|
||||||
|
|
||||||
|
/* Terminate calling thread. */
|
||||||
|
extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
|
||||||
|
|
||||||
|
/* Make calling thread wait for termination of the thread TH. The
|
||||||
|
exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
|
||||||
|
is not NULL. */
|
||||||
|
extern int pthread_join __P ((pthread_t __th, void **__thread_return));
|
||||||
|
|
||||||
|
/* Indicate that the thread TH is never to be joined with PTHREAD_JOIN.
|
||||||
|
The resources of TH will therefore be freed immediately when it
|
||||||
|
terminates, instead of waiting for another thread to perform PTHREAD_JOIN
|
||||||
|
on it. */
|
||||||
|
extern int pthread_detach __P ((pthread_t __th));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling attributes. */
|
||||||
|
|
||||||
|
/* Initialize thread attribute *ATTR with default attributes
|
||||||
|
(detachstate is PTHREAD_JOINABLE, scheduling policy is SCHED_OTHER). */
|
||||||
|
extern int pthread_attr_init __P ((pthread_attr_t *__attr));
|
||||||
|
|
||||||
|
/* Destroy thread attribute *ATTR. */
|
||||||
|
extern int pthread_attr_destroy __P ((pthread_attr_t *__attr));
|
||||||
|
|
||||||
|
/* Set the `detachstate' attribute in *ATTR according to DETACHSTATE. */
|
||||||
|
extern int pthread_attr_setdetachstate __P ((pthread_attr_t *__attr,
|
||||||
|
int __detachstate));
|
||||||
|
|
||||||
|
/* Return in *DETACHSTATE the `detachstate' attribute in *ATTR. */
|
||||||
|
extern int pthread_attr_getdetachstate __P ((__const pthread_attr_t *__attr,
|
||||||
|
int *__detachstate));
|
||||||
|
|
||||||
|
/* Set scheduling parameters (priority, etc) in *ATTR according to PARAM. */
|
||||||
|
extern int pthread_attr_setschedparam __P ((pthread_attr_t *__attr,
|
||||||
|
__const struct sched_param *__param));
|
||||||
|
|
||||||
|
/* Return in *PARAM the scheduling parameters of *ATTR. */
|
||||||
|
extern int pthread_attr_getschedparam __P ((__const pthread_attr_t *__attr,
|
||||||
|
struct sched_param *__param));
|
||||||
|
|
||||||
|
/* Set scheduling policy in *ATTR according to POLICY. */
|
||||||
|
extern int pthread_attr_setschedpolicy __P ((pthread_attr_t *__attr,
|
||||||
|
int __policy));
|
||||||
|
|
||||||
|
/* Return in *POLICY the scheduling policy of *ATTR. */
|
||||||
|
extern int pthread_attr_getschedpolicy __P ((__const pthread_attr_t *__attr,
|
||||||
|
int *__policy));
|
||||||
|
|
||||||
|
/* Set scheduling inheritance mode in *ATTR according to INHERIT. */
|
||||||
|
extern int pthread_attr_setinheritsched __P ((pthread_attr_t *__attr,
|
||||||
|
int __inherit));
|
||||||
|
|
||||||
|
/* Return in *INHERIT the scheduling inheritance mode of *ATTR. */
|
||||||
|
extern int pthread_attr_getinheritsched __P ((__const pthread_attr_t *__attr,
|
||||||
|
int *__inherit));
|
||||||
|
|
||||||
|
/* Set scheduling contention scope in *ATTR according to SCOPE. */
|
||||||
|
extern int pthread_attr_setscope __P ((pthread_attr_t *__attr, int __scope));
|
||||||
|
|
||||||
|
/* Return in *SCOPE the scheduling contention scope of *ATTR. */
|
||||||
|
extern int pthread_attr_getscope __P ((__const pthread_attr_t *__attr,
|
||||||
|
int *__scope));
|
||||||
|
|
||||||
|
/* Functions for scheduling control. */
|
||||||
|
|
||||||
|
/* Set the scheduling parameters for TARGET_THREAD according to POLICY
|
||||||
|
and *PARAM. */
|
||||||
|
extern int pthread_setschedparam __P ((pthread_t __target_thread, int __policy,
|
||||||
|
__const struct sched_param *__param));
|
||||||
|
|
||||||
|
/* Return in *POLICY and *PARAM the scheduling parameters for TARGET_THREAD. */
|
||||||
|
extern int pthread_getschedparam __P ((pthread_t __target_thread,
|
||||||
|
int *__policy,
|
||||||
|
struct sched_param *__param));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for mutex handling. */
|
||||||
|
|
||||||
|
/* Initialize MUTEX using attributes in *MUTEX_ATTR, or use the
|
||||||
|
default values if later is NULL. */
|
||||||
|
extern int __pthread_mutex_init __P ((pthread_mutex_t *__mutex,
|
||||||
|
__const pthread_mutexattr_t *__mutex_attr));
|
||||||
|
extern int pthread_mutex_init __P ((pthread_mutex_t *__mutex,
|
||||||
|
__const pthread_mutexattr_t *__mutex_attr));
|
||||||
|
|
||||||
|
/* Destroy MUTEX. */
|
||||||
|
extern int __pthread_mutex_destroy __P ((pthread_mutex_t *__mutex));
|
||||||
|
extern int pthread_mutex_destroy __P ((pthread_mutex_t *__mutex));
|
||||||
|
|
||||||
|
/* Try to lock MUTEX. */
|
||||||
|
extern int __pthread_mutex_trylock __P ((pthread_mutex_t *__mutex));
|
||||||
|
extern int pthread_mutex_trylock __P ((pthread_mutex_t *__mutex));
|
||||||
|
|
||||||
|
/* Wait until lock for MUTEX becomes available and lock it. */
|
||||||
|
extern int __pthread_mutex_lock __P ((pthread_mutex_t *__mutex));
|
||||||
|
extern int pthread_mutex_lock __P ((pthread_mutex_t *__mutex));
|
||||||
|
|
||||||
|
/* Unlock MUTEX. */
|
||||||
|
extern int __pthread_mutex_unlock __P ((pthread_mutex_t *__mutex));
|
||||||
|
extern int pthread_mutex_unlock __P ((pthread_mutex_t *__mutex));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling mutex attributes. */
|
||||||
|
|
||||||
|
/* Initialize mutex attribute object ATTR with default attributes
|
||||||
|
(kind is PTHREAD_MUTEX_FAST_NP). */
|
||||||
|
extern int __pthread_mutexattr_init __P ((pthread_mutexattr_t *__attr));
|
||||||
|
extern int pthread_mutexattr_init __P ((pthread_mutexattr_t *__attr));
|
||||||
|
|
||||||
|
/* Destroy mutex attribute object ATTR. */
|
||||||
|
extern int __pthread_mutexattr_destroy __P ((pthread_mutexattr_t *__attr));
|
||||||
|
extern int pthread_mutexattr_destroy __P ((pthread_mutexattr_t *__attr));
|
||||||
|
|
||||||
|
/* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_FAST_NP
|
||||||
|
or PTHREAD_MUTEX_RECURSIVE_NP). */
|
||||||
|
extern int __pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
|
||||||
|
int __kind));
|
||||||
|
extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
|
||||||
|
int __kind));
|
||||||
|
/* Return in *KIND the mutex kind attribute in *ATTR. */
|
||||||
|
extern int pthread_mutexattr_getkind_np __P ((__const pthread_mutexattr_t *__attr,
|
||||||
|
int *__kind));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling conditional variables. */
|
||||||
|
|
||||||
|
/* Initialize condition variable COND using attributes ATTR, or use
|
||||||
|
the default values if later is NULL. */
|
||||||
|
extern int pthread_cond_init __P ((pthread_cond_t *__cond,
|
||||||
|
__const pthread_condattr_t *__cond_attr));
|
||||||
|
|
||||||
|
/* Destroy condition variable COND. */
|
||||||
|
extern int pthread_cond_destroy __P ((pthread_cond_t *__cond));
|
||||||
|
|
||||||
|
/* Wake up one thread waiting for condition variable COND. */
|
||||||
|
extern int pthread_cond_signal __P ((pthread_cond_t *__cond));
|
||||||
|
|
||||||
|
/* Wake up all threads waiting for condition variables COND. */
|
||||||
|
extern int pthread_cond_broadcast __P ((pthread_cond_t *__cond));
|
||||||
|
|
||||||
|
/* Wait for condition variable COND to be signaled or broadcast.
|
||||||
|
MUTEX is assumed to be locked before. */
|
||||||
|
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,
|
||||||
|
pthread_mutex_t *__mutex));
|
||||||
|
|
||||||
|
/* Wait for condition variable COND to be signaled or broadcast until
|
||||||
|
ABSTIME. MUTEX is assumed to be locked before. ABSTIME is an
|
||||||
|
absolute time specification; zero is the beginning of the epoch
|
||||||
|
(00:00:00 GMT, January 1, 1970). */
|
||||||
|
extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,
|
||||||
|
pthread_mutex_t *__mutex,
|
||||||
|
__const struct timespec *__abstime));
|
||||||
|
|
||||||
|
/* Functions for handling condition variable attributes. */
|
||||||
|
|
||||||
|
/* Initialize condition variable attribute ATTR. */
|
||||||
|
extern int pthread_condattr_init __P ((pthread_condattr_t *__attr));
|
||||||
|
|
||||||
|
/* Destroy condition variable attribute ATTR. */
|
||||||
|
extern int pthread_condattr_destroy __P ((pthread_condattr_t *__attr));
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __USE_UNIX98
|
||||||
|
/* Functions for handling read-write locks. */
|
||||||
|
|
||||||
|
/* Initialize read-write lock RWLOCK using attributes ATTR, or use
|
||||||
|
the default values if later is NULL. */
|
||||||
|
extern int pthread_rwlock_init __P ((pthread_rwlock_t *__rwlock,
|
||||||
|
__const pthread_rwlockattr_t *__attr));
|
||||||
|
|
||||||
|
/* Destroy read-write lock RWLOCK. */
|
||||||
|
extern int pthread_rwlock_destroy __P ((pthread_rwlock_t *__rwlock));
|
||||||
|
|
||||||
|
/* Acquire read lock for RWLOCK. */
|
||||||
|
extern int pthread_rwlock_rdlock __P ((pthread_rwlock_t *__rwlock));
|
||||||
|
|
||||||
|
/* Try to acquire read lock for RWLOCK. */
|
||||||
|
extern int pthread_rwlock_tryrdlock __P ((pthread_rwlock_t *__rwlock));
|
||||||
|
|
||||||
|
/* Acquire write lock for RWLOCK. */
|
||||||
|
extern int pthread_rwlock_wrlock __P ((pthread_rwlock_t *__rwlock));
|
||||||
|
|
||||||
|
/* Try to acquire writelock for RWLOCK. */
|
||||||
|
extern int pthread_rwlock_trywrlock __P ((pthread_rwlock_t *__rwlock));
|
||||||
|
|
||||||
|
/* Unlock RWLOCK. */
|
||||||
|
extern int pthread_rwlock_unlock __P ((pthread_rwlock_t *__rwlock));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling read-write lock attributes. */
|
||||||
|
|
||||||
|
/* Initialize attribute object ATTR with default values. */
|
||||||
|
extern int pthread_rwlockattr_init __P ((pthread_rwlockattr_t *__attr));
|
||||||
|
|
||||||
|
/* Destroy attribute object ATTR. */
|
||||||
|
extern int pthread_rwlockattr_destroy __P ((pthread_rwlockattr_t *__attr));
|
||||||
|
|
||||||
|
/* Return current setting of process-shared attribute of ATTR in PSHARED. */
|
||||||
|
extern int pthread_rwlockattr_getpshared __P ((__const
|
||||||
|
pthread_rwlockattr_t *__attr,
|
||||||
|
int *__pshared));
|
||||||
|
|
||||||
|
/* Set process-shared attribute of ATTR to PSHARED. */
|
||||||
|
extern int pthread_rwlockattr_setpshared __P ((pthread_rwlockattr_t *__attr,
|
||||||
|
int __pshared));
|
||||||
|
|
||||||
|
/* Return current setting of reader/writer preference. */
|
||||||
|
extern int pthread_rwlockattr_getkind_np __P ((__const
|
||||||
|
pthread_rwlockattr_t *__attr,
|
||||||
|
int *__pref));
|
||||||
|
|
||||||
|
/* Set reader/write preference. */
|
||||||
|
extern int pthread_rwlockattr_setkind_np __P ((pthread_rwlockattr_t *__attr,
|
||||||
|
int __pref));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling thread-specific data */
|
||||||
|
|
||||||
|
/* Create a key value identifying a location in the thread-specific data
|
||||||
|
area. Each thread maintains a distinct thread-specific data area.
|
||||||
|
DESTR_FUNCTION, if non-NULL, is called with
|
||||||
|
the value associated to that key when the key is destroyed.
|
||||||
|
DESTR_FUNCTION is not called if the value associated is NULL
|
||||||
|
when the key is destroyed. */
|
||||||
|
extern int __pthread_key_create __P ((pthread_key_t *__key,
|
||||||
|
void (*__destr_function) (void *)));
|
||||||
|
extern int pthread_key_create __P ((pthread_key_t *__key,
|
||||||
|
void (*__destr_function) (void *)));
|
||||||
|
|
||||||
|
/* Destroy KEY. */
|
||||||
|
extern int pthread_key_delete __P ((pthread_key_t __key));
|
||||||
|
|
||||||
|
/* Store POINTER in the thread-specific data slot identified by KEY. */
|
||||||
|
extern int __pthread_setspecific __P ((pthread_key_t __key,
|
||||||
|
__const void *__pointer));
|
||||||
|
extern int pthread_setspecific __P ((pthread_key_t __key,
|
||||||
|
__const void *__pointer));
|
||||||
|
|
||||||
|
/* Return current value of the thread-specific data slot identified by KEY. */
|
||||||
|
extern void *__pthread_getspecific __P ((pthread_key_t __key));
|
||||||
|
extern void *pthread_getspecific __P ((pthread_key_t __key));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling initialization */
|
||||||
|
|
||||||
|
/* Guarantee that the initialization function INIT_ROUTINE will be called
|
||||||
|
only once, even if pthread_once is executed several times with the
|
||||||
|
same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or
|
||||||
|
extern variable initialized to PTHREAD_ONCE_INIT. */
|
||||||
|
extern int __pthread_once __P ((pthread_once_t *__once_control,
|
||||||
|
void (*__init_routine) (void)));
|
||||||
|
extern int pthread_once __P ((pthread_once_t *__once_control,
|
||||||
|
void (*__init_routine) (void)));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling cancellation. */
|
||||||
|
|
||||||
|
/* Set cancelability state of current thread to STATE, returning old
|
||||||
|
state in *OLDSTATE if OLDSTATE is not NULL. */
|
||||||
|
extern int pthread_setcancelstate __P ((int __state, int *__oldstate));
|
||||||
|
|
||||||
|
/* Set cancellation state of current thread to TYPE, returning the old
|
||||||
|
type in *OLDTYPE if OLDTYPE is not NULL. */
|
||||||
|
extern int __pthread_setcanceltype __P ((int __type, int *__oldtype));
|
||||||
|
extern int pthread_setcanceltype __P ((int __type, int *__oldtype));
|
||||||
|
|
||||||
|
/* Cancel THREAD immediately or at the next possibility. */
|
||||||
|
extern int pthread_cancel __P ((pthread_t __thread));
|
||||||
|
|
||||||
|
/* Test for pending cancellation for the current thread and terminate
|
||||||
|
the thread as per pthread_exit(PTHREAD_CANCELED) if it has been
|
||||||
|
cancelled. */
|
||||||
|
extern void pthread_testcancel __P ((void));
|
||||||
|
|
||||||
|
|
||||||
|
/* Install a cleanup handler: ROUTINE will be called with arguments ARG
|
||||||
|
when the thread is cancelled or calls pthread_exit. ROUTINE will also
|
||||||
|
be called with arguments ARG when the matching pthread_cleanup_pop
|
||||||
|
is executed with non-zero EXECUTE argument.
|
||||||
|
pthread_cleanup_push and pthread_cleanup_pop are macros and must always
|
||||||
|
be used in matching pairs at the same nesting level of braces. */
|
||||||
|
|
||||||
|
#define pthread_cleanup_push(routine,arg) \
|
||||||
|
{ struct _pthread_cleanup_buffer _buffer; \
|
||||||
|
_pthread_cleanup_push (&_buffer, (routine), (arg));
|
||||||
|
|
||||||
|
extern void _pthread_cleanup_push __P ((struct _pthread_cleanup_buffer *__buffer,
|
||||||
|
void (*__routine) (void *),
|
||||||
|
void *__arg));
|
||||||
|
|
||||||
|
/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
|
||||||
|
If EXECUTE is non-zero, the handler function is called. */
|
||||||
|
|
||||||
|
#define pthread_cleanup_pop(execute) \
|
||||||
|
_pthread_cleanup_pop (&_buffer, (execute)); }
|
||||||
|
|
||||||
|
extern void _pthread_cleanup_pop __P ((struct _pthread_cleanup_buffer *__buffer,
|
||||||
|
int __execute));
|
||||||
|
|
||||||
|
/* Install a cleanup handler as pthread_cleanup_push does, but also
|
||||||
|
saves the current cancellation type and set it to deferred cancellation. */
|
||||||
|
|
||||||
|
#define pthread_cleanup_push_defer_np(routine,arg) \
|
||||||
|
{ struct _pthread_cleanup_buffer _buffer; \
|
||||||
|
_pthread_cleanup_push_defer (&_buffer, (routine), (arg));
|
||||||
|
|
||||||
|
extern void _pthread_cleanup_push_defer __P ((struct _pthread_cleanup_buffer *__buffer,
|
||||||
|
void (*__routine) (void *),
|
||||||
|
void *__arg));
|
||||||
|
|
||||||
|
/* Remove a cleanup handler as pthread_cleanup_pop does, but also
|
||||||
|
restores the cancellation type that was in effect when the matching
|
||||||
|
pthread_cleanup_push_defer was called. */
|
||||||
|
|
||||||
|
#define pthread_cleanup_pop_restore_np(execute) \
|
||||||
|
_pthread_cleanup_pop_restore (&_buffer, (execute)); }
|
||||||
|
|
||||||
|
extern void _pthread_cleanup_pop_restore __P ((struct _pthread_cleanup_buffer *__buffer,
|
||||||
|
int __execute));
|
||||||
|
|
||||||
|
/* Functions for handling signals. */
|
||||||
|
|
||||||
|
/* Modify the signal mask for the calling thread. The arguments have
|
||||||
|
the same meaning as for sigprocmask(2). */
|
||||||
|
|
||||||
|
extern int pthread_sigmask __P ((int __how, __const sigset_t *__newmask,
|
||||||
|
sigset_t *__oldmask));
|
||||||
|
|
||||||
|
/* Send signal SIGNO to the given thread. */
|
||||||
|
|
||||||
|
extern int pthread_kill __P ((pthread_t __thread, int __signo));
|
||||||
|
|
||||||
|
|
||||||
|
/* Functions for handling process creation and process execution. */
|
||||||
|
|
||||||
|
/* Install handlers to be called when a new process is created with FORK.
|
||||||
|
The PREPARE handler is called in the parent process just before performing
|
||||||
|
FORK. The PARENT handler is called in the parent process just after FORK.
|
||||||
|
The CHILD handler is called in the child process. Each of the three
|
||||||
|
handlers can be NULL, meaning that no handler needs to be called at that
|
||||||
|
point.
|
||||||
|
PTHREAD_ATFORK can be called several times, in which case the PREPARE
|
||||||
|
handlers are called in LIFO order (last added with PTHREAD_ATFORK,
|
||||||
|
first called before FORK), and the PARENT and CHILD handlers are called
|
||||||
|
in FIFO (first added, first called). */
|
||||||
|
|
||||||
|
extern int __pthread_atfork __P ((void (*__prepare) (void),
|
||||||
|
void (*__parent) (void),
|
||||||
|
void (*__child) (void)));
|
||||||
|
extern int pthread_atfork __P ((void (*__prepare) (void),
|
||||||
|
void (*__parent) (void),
|
||||||
|
void (*__child) (void)));
|
||||||
|
|
||||||
|
/* Terminate all threads in the program except the calling process.
|
||||||
|
Should be called just before invoking one of the exec*() functions. */
|
||||||
|
|
||||||
|
extern void __pthread_kill_other_threads_np __P ((void));
|
||||||
|
extern void pthread_kill_other_threads_np __P ((void));
|
||||||
|
|
||||||
|
|
||||||
|
/* This function is called to initialize the pthread library. */
|
||||||
|
extern void __pthread_initialize __P ((void));
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif /* pthread.h */
|
1
linuxthreads/sysdeps/sparc/sparc32/Implies
Normal file
1
linuxthreads/sysdeps/sparc/sparc32/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/no-cmpxchg
|
55
linuxthreads/sysdeps/sparc/sparc32/pt-machine.h
Normal file
55
linuxthreads/sysdeps/sparc/sparc32/pt-machine.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
sparc version.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
static inline int testandset(int *spinlock)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("ldstub %1,%0"
|
||||||
|
: "=r"(ret), "=m"(*spinlock)
|
||||||
|
: "m"(*spinlock));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinlock release; default is just set to zero. */
|
||||||
|
#define RELEASE(spinlock) \
|
||||||
|
__asm__ __volatile__("stbar; stb %1,%0" : "=m"(*(spinlock)) : "r"(0));
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__("%sp");
|
||||||
|
|
||||||
|
|
||||||
|
/* Registers %g6 and %g7 are reserved by the ABI for "system use". It
|
||||||
|
happens that Solaris uses %g6 for the thread pointer -- we do the same. */
|
||||||
|
struct _pthread_descr_struct;
|
||||||
|
register struct _pthread_descr_struct *__thread_self __asm__("%g6");
|
||||||
|
|
||||||
|
/* Return the thread descriptor for the current thread. */
|
||||||
|
#define THREAD_SELF __thread_self
|
||||||
|
|
||||||
|
/* Initialize the thread-unique value. */
|
||||||
|
#define INIT_THREAD_SELF(descr) (__thread_self = (descr))
|
1
linuxthreads/sysdeps/sparc/sparc64/Implies
Normal file
1
linuxthreads/sysdeps/sparc/sparc64/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread/cmpxchg
|
67
linuxthreads/sysdeps/sparc/sparc64/pt-machine.h
Normal file
67
linuxthreads/sysdeps/sparc/sparc64/pt-machine.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* Machine-dependent pthreads configuration and inline functions.
|
||||||
|
Sparc v9 version.
|
||||||
|
Copyright (C) 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Richard Henderson <rth@tamu.edu>.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
||||||
|
not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinlock implementation; required. */
|
||||||
|
extern inline int
|
||||||
|
testandset (int *spinlock)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__asm__ __volatile__("ldstub %1,%0"
|
||||||
|
: "=r"(ret), "=m"(*spinlock) : "m"(*spinlock));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get some notion of the current stack. Need not be exactly the top
|
||||||
|
of the stack, just something somewhere in the current frame. */
|
||||||
|
#define CURRENT_STACK_FRAME stack_pointer
|
||||||
|
register char * stack_pointer __asm__ ("%sp");
|
||||||
|
|
||||||
|
|
||||||
|
/* Registers %g6 and %g7 are reserved by the ABI for "system use". It
|
||||||
|
happens that Solaris uses %g6 for the thread pointer -- we do the same. */
|
||||||
|
struct _pthread_descr_struct;
|
||||||
|
register struct _pthread_descr_struct *__thread_self __asm__("%g6");
|
||||||
|
|
||||||
|
/* Return the thread descriptor for the current thread. */
|
||||||
|
#define THREAD_SELF __thread_self
|
||||||
|
|
||||||
|
/* Initialize the thread-unique value. */
|
||||||
|
#define INIT_THREAD_SELF(descr) (__thread_self = (descr))
|
||||||
|
|
||||||
|
|
||||||
|
/* Compare-and-swap for semaphores. */
|
||||||
|
|
||||||
|
#define HAS_COMPARE_AND_SWAP
|
||||||
|
extern inline int
|
||||||
|
__compare_and_swap (long int *p, long int oldval, long int newval)
|
||||||
|
{
|
||||||
|
long int readval;
|
||||||
|
|
||||||
|
__asm__ __volatile__ ("cas %1, %2, %0"
|
||||||
|
: "=r"(readval), "=m"(*p)
|
||||||
|
: "r"(oldval), "m"(*p), "0"(newval));
|
||||||
|
|
||||||
|
return readval == newval;
|
||||||
|
}
|
1
linuxthreads/sysdeps/unix/sysv/linux/Implies
Normal file
1
linuxthreads/sysdeps/unix/sysv/linux/Implies
Normal file
@ -0,0 +1 @@
|
|||||||
|
pthread
|
40
linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h
Normal file
40
linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* Minimum guaranteed maximum values for system limits. Linux version.
|
||||||
|
Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
/* The kernel sources contain a file with all the needed information. */
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
/* The number of data keys per process. */
|
||||||
|
#define _POSIX_THREAD_KEYS_MAX 128
|
||||||
|
/* This is the value this implementation supports. */
|
||||||
|
#define PTHREAD_KEYS_MAX 1024
|
||||||
|
|
||||||
|
/* Controlling the iterations of destructors for thread-specific data. */
|
||||||
|
#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4
|
||||||
|
/* Number of iterations this implementation does. */
|
||||||
|
#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
|
||||||
|
|
||||||
|
/* The number of threads per process. */
|
||||||
|
#define _POSIX_THREAD_THREADS_MAX 64
|
||||||
|
/* This is the value this implementation supports. */
|
||||||
|
#define PTHREAD_THREADS_MAX 1024
|
||||||
|
|
||||||
|
/* Maximum amount by which a process can descrease its asynchronous I/O
|
||||||
|
priority level. */
|
||||||
|
#define AIO_PRIO_DELTA_MAX 20
|
94
linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h
Normal file
94
linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* Define POSIX options for Linux.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#ifndef _POSIX_OPT_H
|
||||||
|
#define _POSIX_OPT_H 1
|
||||||
|
|
||||||
|
/* Job control is supported. */
|
||||||
|
#define _POSIX_JOB_CONTROL 1
|
||||||
|
|
||||||
|
/* Processes have a saved set-user-ID and a saved set-group-ID. */
|
||||||
|
#define _POSIX_SAVED_IDS 1
|
||||||
|
|
||||||
|
/* Priority scheduling is supported. */
|
||||||
|
#define _POSIX_PRIORITY_SCHEDULING 1
|
||||||
|
|
||||||
|
/* Synchronizing file data is supported. */
|
||||||
|
#define _POSIX_SYNCHRONIZED_IO 1
|
||||||
|
|
||||||
|
/* The fsync function is present. */
|
||||||
|
#define _POSIX_FSYNC 1
|
||||||
|
|
||||||
|
/* Mapping of files to memory is supported. */
|
||||||
|
#define _POSIX_MAPPED_FILES 1
|
||||||
|
|
||||||
|
/* Locking of all memory is supported. */
|
||||||
|
#define _POSIX_MEMLOCK 1
|
||||||
|
|
||||||
|
/* Locking of ranges of memory is supported. */
|
||||||
|
#define _POSIX_MEMLOCK_RANGE 1
|
||||||
|
|
||||||
|
/* Setting of memory protections is supported. */
|
||||||
|
#define _POSIX_MEMORY_PROTECTION 1
|
||||||
|
|
||||||
|
/* Implementation supports `poll' function. */
|
||||||
|
#define _POSIX_POLL 1
|
||||||
|
|
||||||
|
/* Implementation supports `select' and `pselect' functions. */
|
||||||
|
#define _POSIX_SELECT 1
|
||||||
|
|
||||||
|
/* Only root can change owner of file. */
|
||||||
|
#define _POSIX_CHOWN_RESTRICTED 1
|
||||||
|
|
||||||
|
/* `c_cc' member of 'struct termios' structure can be disabled by
|
||||||
|
using the value _POSIX_VDISABLE. */
|
||||||
|
#define _POSIX_VDISABLE '\0'
|
||||||
|
|
||||||
|
/* Filenames are not silently truncated. */
|
||||||
|
#define _POSIX_NO_TRUNC 1
|
||||||
|
|
||||||
|
/* X/Open realtime support is available. */
|
||||||
|
#define _XOPEN_REALTIME 1
|
||||||
|
|
||||||
|
/* X/Open realtime thread support is available. */
|
||||||
|
#define _XOPEN_REALTIME_THREADS 1
|
||||||
|
|
||||||
|
/* XPG4.2 shared memory is supported. */
|
||||||
|
#define _XOPEN_SHM 1
|
||||||
|
|
||||||
|
/* Tell we have POSIX threads. */
|
||||||
|
#define _POSIX_THREADS 1
|
||||||
|
|
||||||
|
/* We have the reentrant functions described in POSIX. */
|
||||||
|
#define _POSIX_REENTRANT_FUNCTIONS 1
|
||||||
|
#define _POSIX_THREAD_SAFE_FUNCTIONS 1
|
||||||
|
|
||||||
|
/* We provide priority scheduling for threads. */
|
||||||
|
#define _POSIX_THREAD_PRIORITY_SCHEDULING 1
|
||||||
|
|
||||||
|
/* We support POSIX.1b semaphores, but only the non-shared form for now. */
|
||||||
|
/*#define _POSIX_SEMAPHORES 1 XXX We are not quite there now. */
|
||||||
|
|
||||||
|
/* Real-time signals are supported. */
|
||||||
|
#define _POSIX_REALTIME_SIGNALS 1
|
||||||
|
|
||||||
|
/* We support asynchronous I/O. */
|
||||||
|
#define _POSIX_ASYNCHRONOUS_IO 1
|
||||||
|
|
||||||
|
#endif /* posix_opt.h */
|
3
linuxthreads/sysdeps/unix/sysv/linux/configure
vendored
Normal file
3
linuxthreads/sysdeps/unix/sysv/linux/configure
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Local configure fragment for sysdeps/unix/sysv/linux.
|
||||||
|
|
||||||
|
DEFINES="$DEFINES -D_LIBC_REENTRANT"
|
87
linuxthreads/weaks.c
Normal file
87
linuxthreads/weaks.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* The weak pthread functions for Linux.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern int __pthread_return_0 __P ((void));
|
||||||
|
extern int __pthread_return_1 __P ((void));
|
||||||
|
extern void __pthread_return_void __P ((void));
|
||||||
|
|
||||||
|
/* Those are pthread functions which return 0 if successful. */
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_init)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_destroy)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_setdetachstate)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_getdetachstate)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_setschedparam)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_getschedparam)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_setschedpolicy)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_getschedpolicy)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_setinheritsched)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_getinheritsched)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_setscope)
|
||||||
|
weak_alias (__pthread_return_0, pthread_attr_getscope)
|
||||||
|
weak_alias (__pthread_return_0, pthread_mutex_init)
|
||||||
|
weak_alias (__pthread_return_0, pthread_mutex_destroy)
|
||||||
|
weak_alias (__pthread_return_0, pthread_mutex_lock)
|
||||||
|
weak_alias (__pthread_return_0, pthread_mutex_unlock)
|
||||||
|
weak_alias (__pthread_return_0, pthread_mutexattr_setkind_np)
|
||||||
|
weak_alias (__pthread_return_0, pthread_mutexattr_getkind_np)
|
||||||
|
weak_alias (__pthread_return_0, pthread_condattr_init)
|
||||||
|
weak_alias (__pthread_return_0, pthread_condattr_destroy)
|
||||||
|
weak_alias (__pthread_return_0, pthread_setschedparam)
|
||||||
|
weak_alias (__pthread_return_0, pthread_getschedparam)
|
||||||
|
weak_alias (__pthread_return_0, pthread_setcancelstate)
|
||||||
|
weak_alias (__pthread_return_0, pthread_setcanceltype)
|
||||||
|
weak_alias (__pthread_return_0, pthread_self)
|
||||||
|
weak_alias (__pthread_return_0, pthread_cond_init)
|
||||||
|
weak_alias (__pthread_return_0, pthread_cond_destroy)
|
||||||
|
weak_alias (__pthread_return_0, pthread_cond_wait)
|
||||||
|
weak_alias (__pthread_return_0, pthread_cond_signal)
|
||||||
|
weak_alias (__pthread_return_0, pthread_cond_broadcast)
|
||||||
|
|
||||||
|
|
||||||
|
/* Those are pthread functions which return 1 if successful. */
|
||||||
|
weak_alias (__pthread_return_1, pthread_equal)
|
||||||
|
|
||||||
|
/* pthread_exit () is a special case. */
|
||||||
|
void
|
||||||
|
weak_function
|
||||||
|
pthread_exit (void *retval)
|
||||||
|
{
|
||||||
|
exit (EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
__pthread_return_0 (void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
__pthread_return_1 (void)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
__pthread_return_void (void)
|
||||||
|
{
|
||||||
|
}
|
183
linuxthreads/wrapsyscall.c
Normal file
183
linuxthreads/wrapsyscall.c
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/* Wrapper arpund system calls to provide cancelation points.
|
||||||
|
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public
|
||||||
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||||
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef PIC
|
||||||
|
/* We need a hook to force this file to be linked in when static
|
||||||
|
libpthread is used. */
|
||||||
|
const int __pthread_provide_wrappers = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define CANCELABLE_SYSCALL(res_type, name, param_list, params) \
|
||||||
|
res_type __libc_##name param_list; \
|
||||||
|
res_type \
|
||||||
|
name param_list \
|
||||||
|
{ \
|
||||||
|
res_type result; \
|
||||||
|
int oldtype; \
|
||||||
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); \
|
||||||
|
result = __libc_##name params; \
|
||||||
|
pthread_setcanceltype (oldtype, NULL); \
|
||||||
|
return result; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CANCELABLE_SYSCALL_VA(res_type, name, param_list, params, last_arg) \
|
||||||
|
res_type __libc_##name param_list; \
|
||||||
|
res_type \
|
||||||
|
name param_list \
|
||||||
|
{ \
|
||||||
|
res_type result; \
|
||||||
|
int oldtype; \
|
||||||
|
va_list ap; \
|
||||||
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); \
|
||||||
|
va_start (ap, last_arg); \
|
||||||
|
result = __libc_##name params; \
|
||||||
|
va_end (ap); \
|
||||||
|
pthread_setcanceltype (oldtype, NULL); \
|
||||||
|
return result; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* close(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, close, (int fd), (fd))
|
||||||
|
strong_alias (close, __close)
|
||||||
|
|
||||||
|
|
||||||
|
/* fcntl(2). */
|
||||||
|
CANCELABLE_SYSCALL_VA (int, fcntl, (int fd, int cmd, ...),
|
||||||
|
(fd, cmd, va_arg (ap, long int)), cmd)
|
||||||
|
strong_alias (fcntl, __fcntl)
|
||||||
|
|
||||||
|
|
||||||
|
/* fsync(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, fsync, (int fd), (fd))
|
||||||
|
|
||||||
|
|
||||||
|
/* lseek(2). */
|
||||||
|
CANCELABLE_SYSCALL (off_t, lseek, (int fd, off_t offset, int whence),
|
||||||
|
(fd, offset, whence))
|
||||||
|
strong_alias (lseek, __lseek)
|
||||||
|
|
||||||
|
|
||||||
|
/* msync(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, msync, (const void *start, size_t length, int flags),
|
||||||
|
(start, length, flags))
|
||||||
|
|
||||||
|
|
||||||
|
/* nanosleep(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, nanosleep, (const struct timespec *requested_time,
|
||||||
|
struct timespec *remaining),
|
||||||
|
(requested_time, remaining))
|
||||||
|
|
||||||
|
|
||||||
|
/* open(2). */
|
||||||
|
CANCELABLE_SYSCALL_VA (int, open, (const char *pathname, int flags, ...),
|
||||||
|
(pathname, flags, va_arg (ap, mode_t)), flags)
|
||||||
|
strong_alias (open, __open)
|
||||||
|
|
||||||
|
|
||||||
|
/* pause(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, pause, (void), ())
|
||||||
|
|
||||||
|
|
||||||
|
/* read(2). */
|
||||||
|
CANCELABLE_SYSCALL (ssize_t, read, (int fd, void *buf, size_t count),
|
||||||
|
(fd, buf, count))
|
||||||
|
strong_alias (read, __read)
|
||||||
|
|
||||||
|
|
||||||
|
/* system(3). */
|
||||||
|
CANCELABLE_SYSCALL (int, system, (const char *line), (line))
|
||||||
|
|
||||||
|
|
||||||
|
/* tcdrain(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, tcdrain, (int fd), (fd))
|
||||||
|
|
||||||
|
|
||||||
|
/* wait(2). */
|
||||||
|
CANCELABLE_SYSCALL (__pid_t, wait, (__WAIT_STATUS_DEFN stat_loc), (stat_loc))
|
||||||
|
strong_alias (wait, __wait)
|
||||||
|
|
||||||
|
|
||||||
|
/* waitpid(2). */
|
||||||
|
CANCELABLE_SYSCALL (__pid_t, waitpid, (__pid_t pid, int *stat_loc,
|
||||||
|
int options),
|
||||||
|
(pid, stat_loc, options))
|
||||||
|
|
||||||
|
|
||||||
|
/* write(2). */
|
||||||
|
CANCELABLE_SYSCALL (ssize_t, write, (int fd, const void *buf, size_t n),
|
||||||
|
(fd, buf, n))
|
||||||
|
strong_alias (write, __write)
|
||||||
|
|
||||||
|
|
||||||
|
/* The following system calls are thread cancellation points specified
|
||||||
|
in XNS. */
|
||||||
|
|
||||||
|
/* accept(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, accept, (int fd, __SOCKADDR_ARG addr,
|
||||||
|
socklen_t *addr_len),
|
||||||
|
(fd, addr, addr_len))
|
||||||
|
|
||||||
|
/* connect(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, connect, (int fd, __CONST_SOCKADDR_ARG addr,
|
||||||
|
socklen_t len),
|
||||||
|
(fd, addr, len))
|
||||||
|
strong_alias (connect, __connect)
|
||||||
|
|
||||||
|
/* recv(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, recv, (int fd, __ptr_t buf, size_t n, int flags),
|
||||||
|
(fd, buf, n, flags))
|
||||||
|
|
||||||
|
/* recvfrom(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, recvfrom, (int fd, __ptr_t buf, size_t n, int flags,
|
||||||
|
__SOCKADDR_ARG addr, socklen_t *addr_len),
|
||||||
|
(fd, buf, n, flags, addr, addr_len))
|
||||||
|
|
||||||
|
/* recvmsg(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, recvmsg, (int fd, struct msghdr *message, int flags),
|
||||||
|
(fd, message, flags))
|
||||||
|
|
||||||
|
/* send(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, send, (int fd, const __ptr_t buf, size_t n,
|
||||||
|
int flags),
|
||||||
|
(fd, buf, n, flags))
|
||||||
|
strong_alias (send, __send)
|
||||||
|
|
||||||
|
/* sendmsg(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, sendmsg, (int fd, const struct msghdr *message,
|
||||||
|
int flags),
|
||||||
|
(fd, message, flags))
|
||||||
|
|
||||||
|
/* sendto(2). */
|
||||||
|
CANCELABLE_SYSCALL (int, sendto, (int fd, const __ptr_t buf, size_t n,
|
||||||
|
int flags, __CONST_SOCKADDR_ARG addr,
|
||||||
|
socklen_t addr_len),
|
||||||
|
(fd, buf, n, flags, addr, addr_len))
|
Reference in New Issue
Block a user