diff options
author | Ulrich Drepper <drepper@redhat.com> | 1998-03-11 12:42:25 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 1998-03-11 12:42:25 +0000 |
commit | 5afdca0087dad2994ad4fcdfe7f489f4dbcab7b3 (patch) | |
tree | c9c82db2ff92537b921361a02a284b0e18a594b8 /linuxthreads | |
parent | 3d76e7784700c20fc222920b2a22baba006daa15 (diff) | |
download | glibc-5afdca0087dad2994ad4fcdfe7f489f4dbcab7b3.tar glibc-5afdca0087dad2994ad4fcdfe7f489f4dbcab7b3.tar.gz glibc-5afdca0087dad2994ad4fcdfe7f489f4dbcab7b3.tar.bz2 glibc-5afdca0087dad2994ad4fcdfe7f489f4dbcab7b3.zip |
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.
Diffstat (limited to 'linuxthreads')
87 files changed, 10213 insertions, 0 deletions
diff --git a/linuxthreads/Banner b/linuxthreads/Banner new file mode 100644 index 0000000000..c1a3821f13 --- /dev/null +++ b/linuxthreads/Banner @@ -0,0 +1 @@ +linuxthreads-0.7 by Xavier Leroy diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog new file mode 100644 index 0000000000..696d15178d --- /dev/null +++ b/linuxthreads/ChangeLog @@ -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. diff --git a/linuxthreads/Changes b/linuxthreads/Changes new file mode 100644 index 0000000000..8ec26c9a67 --- /dev/null +++ b/linuxthreads/Changes @@ -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 diff --git a/linuxthreads/Examples/Makefile b/linuxthreads/Examples/Makefile new file mode 100644 index 0000000000..c68b3676a4 --- /dev/null +++ b/linuxthreads/Examples/Makefile @@ -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) diff --git a/linuxthreads/Examples/ex1.c b/linuxthreads/Examples/ex1.c new file mode 100644 index 0000000000..c399fab894 --- /dev/null +++ b/linuxthreads/Examples/ex1.c @@ -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; +} + diff --git a/linuxthreads/Examples/ex2.c b/linuxthreads/Examples/ex2.c new file mode 100644 index 0000000000..3f7f115fda --- /dev/null +++ b/linuxthreads/Examples/ex2.c @@ -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; +} + + + diff --git a/linuxthreads/Examples/ex3.c b/linuxthreads/Examples/ex3.c new file mode 100644 index 0000000000..002bc9042a --- /dev/null +++ b/linuxthreads/Examples/ex3.c @@ -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); +} + diff --git a/linuxthreads/Examples/ex4.c b/linuxthreads/Examples/ex4.c new file mode 100644 index 0000000000..83bc54c913 --- /dev/null +++ b/linuxthreads/Examples/ex4.c @@ -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); +} diff --git a/linuxthreads/Examples/ex5.c b/linuxthreads/Examples/ex5.c new file mode 100644 index 0000000000..366668eb8c --- /dev/null +++ b/linuxthreads/Examples/ex5.c @@ -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; +} diff --git a/linuxthreads/FAQ.html b/linuxthreads/FAQ.html new file mode 100644 index 0000000000..45d2387db2 --- /dev/null +++ b/linuxthreads/FAQ.html @@ -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> diff --git a/linuxthreads/LICENSE b/linuxthreads/LICENSE new file mode 100644 index 0000000000..7bcca60504 --- /dev/null +++ b/linuxthreads/LICENSE @@ -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! + diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile new file mode 100644 index 0000000000..c4eddef3a5 --- /dev/null +++ b/linuxthreads/Makefile @@ -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 diff --git a/linuxthreads/README b/linuxthreads/README new file mode 100644 index 0000000000..e824dd5b50 --- /dev/null +++ b/linuxthreads/README @@ -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. diff --git a/linuxthreads/README.Xfree3.2 b/linuxthreads/README.Xfree3.2 new file mode 100644 index 0000000000..ac08e15832 --- /dev/null +++ b/linuxthreads/README.Xfree3.2 @@ -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) */ +------------------------------------------------------------------------- diff --git a/linuxthreads/attr.c b/linuxthreads/attr.c new file mode 100644 index 0000000000..9622b5706b --- /dev/null +++ b/linuxthreads/attr.c @@ -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; +} diff --git a/linuxthreads/cancel.c b/linuxthreads/cancel.c new file mode 100644 index 0000000000..a6a0ecaa5a --- /dev/null +++ b/linuxthreads/cancel.c @@ -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 diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c new file mode 100644 index 0000000000..6807522edc --- /dev/null +++ b/linuxthreads/condvar.c @@ -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; + } + } +} diff --git a/linuxthreads/configure b/linuxthreads/configure new file mode 100755 index 0000000000..3eafc93f5b --- /dev/null +++ b/linuxthreads/configure @@ -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 diff --git a/linuxthreads/errno.c b/linuxthreads/errno.c new file mode 100644 index 0000000000..3619aa87b6 --- /dev/null +++ b/linuxthreads/errno.c @@ -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; +} diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h new file mode 100644 index 0000000000..7939605cfe --- /dev/null +++ b/linuxthreads/internals.h @@ -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); diff --git a/linuxthreads/join.c b/linuxthreads/join.c new file mode 100644 index 0000000000..2bdc189631 --- /dev/null +++ b/linuxthreads/join.c @@ -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; +} diff --git a/linuxthreads/libpthread.map b/linuxthreads/libpthread.map new file mode 100644 index 0000000000..36767af3ed --- /dev/null +++ b/linuxthreads/libpthread.map @@ -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; diff --git a/linuxthreads/lockfile.c b/linuxthreads/lockfile.c new file mode 100644 index 0000000000..b0cb2ca006 --- /dev/null +++ b/linuxthreads/lockfile.c @@ -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 +} diff --git a/linuxthreads/man/Makefile b/linuxthreads/man/Makefile new file mode 100644 index 0000000000..4875c3d765 --- /dev/null +++ b/linuxthreads/man/Makefile @@ -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" diff --git a/linuxthreads/man/pthread_atfork.man b/linuxthreads/man/pthread_atfork.man new file mode 100644 index 0000000000..4d06a56f8b --- /dev/null +++ b/linuxthreads/man/pthread_atfork.man @@ -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). diff --git a/linuxthreads/man/pthread_attr_init.man b/linuxthreads/man/pthread_attr_init.man new file mode 100644 index 0000000000..bd5a169242 --- /dev/null +++ b/linuxthreads/man/pthread_attr_init.man @@ -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). diff --git a/linuxthreads/man/pthread_cancel.man b/linuxthreads/man/pthread_cancel.man new file mode 100644 index 0000000000..202d5c9b26 --- /dev/null +++ b/linuxthreads/man/pthread_cancel.man @@ -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 diff --git a/linuxthreads/man/pthread_cleanup_push.man b/linuxthreads/man/pthread_cleanup_push.man new file mode 100644 index 0000000000..1591431c9c --- /dev/null +++ b/linuxthreads/man/pthread_cleanup_push.man @@ -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 + diff --git a/linuxthreads/man/pthread_cond_init.man b/linuxthreads/man/pthread_cond_init.man new file mode 100644 index 0000000000..b803f08361 --- /dev/null +++ b/linuxthreads/man/pthread_cond_init.man @@ -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 + diff --git a/linuxthreads/man/pthread_condattr_init.man b/linuxthreads/man/pthread_condattr_init.man new file mode 100644 index 0000000000..f491cbedbe --- /dev/null +++ b/linuxthreads/man/pthread_condattr_init.man @@ -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). diff --git a/linuxthreads/man/pthread_create.man b/linuxthreads/man/pthread_create.man new file mode 100644 index 0000000000..a94004767a --- /dev/null +++ b/linuxthreads/man/pthread_create.man @@ -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). diff --git a/linuxthreads/man/pthread_detach.man b/linuxthreads/man/pthread_detach.man new file mode 100644 index 0000000000..7b43f45faa --- /dev/null +++ b/linuxthreads/man/pthread_detach.man @@ -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).
\ No newline at end of file diff --git a/linuxthreads/man/pthread_equal.man b/linuxthreads/man/pthread_equal.man new file mode 100644 index 0000000000..1a0396515a --- /dev/null +++ b/linuxthreads/man/pthread_equal.man @@ -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). diff --git a/linuxthreads/man/pthread_exit.man b/linuxthreads/man/pthread_exit.man new file mode 100644 index 0000000000..54751e9d05 --- /dev/null +++ b/linuxthreads/man/pthread_exit.man @@ -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). diff --git a/linuxthreads/man/pthread_join.man b/linuxthreads/man/pthread_join.man new file mode 100644 index 0000000000..d587093841 --- /dev/null +++ b/linuxthreads/man/pthread_join.man @@ -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). diff --git a/linuxthreads/man/pthread_key_create.man b/linuxthreads/man/pthread_key_create.man new file mode 100644 index 0000000000..6823e304c9 --- /dev/null +++ b/linuxthreads/man/pthread_key_create.man @@ -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 diff --git a/linuxthreads/man/pthread_kill_other_threads_np.man b/linuxthreads/man/pthread_kill_other_threads_np.man new file mode 100644 index 0000000000..0de42d52d5 --- /dev/null +++ b/linuxthreads/man/pthread_kill_other_threads_np.man @@ -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. diff --git a/linuxthreads/man/pthread_mutex_init.man b/linuxthreads/man/pthread_mutex_init.man new file mode 100644 index 0000000000..bda4ec6c4d --- /dev/null +++ b/linuxthreads/man/pthread_mutex_init.man @@ -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 + + diff --git a/linuxthreads/man/pthread_mutexattr_init.man b/linuxthreads/man/pthread_mutexattr_init.man new file mode 100644 index 0000000000..5ceefdbb56 --- /dev/null +++ b/linuxthreads/man/pthread_mutexattr_init.man @@ -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). diff --git a/linuxthreads/man/pthread_once.man b/linuxthreads/man/pthread_once.man new file mode 100644 index 0000000000..e9d117b656 --- /dev/null +++ b/linuxthreads/man/pthread_once.man @@ -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> + diff --git a/linuxthreads/man/pthread_self.man b/linuxthreads/man/pthread_self.man new file mode 100644 index 0000000000..3aa4a0021e --- /dev/null +++ b/linuxthreads/man/pthread_self.man @@ -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). + diff --git a/linuxthreads/man/pthread_setschedparam.man b/linuxthreads/man/pthread_setschedparam.man new file mode 100644 index 0000000000..3992927837 --- /dev/null +++ b/linuxthreads/man/pthread_setschedparam.man @@ -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). diff --git a/linuxthreads/man/pthread_sigmask.man b/linuxthreads/man/pthread_sigmask.man new file mode 100644 index 0000000000..784161da2b --- /dev/null +++ b/linuxthreads/man/pthread_sigmask.man @@ -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). diff --git a/linuxthreads/man/sem_init.man b/linuxthreads/man/sem_init.man new file mode 100644 index 0000000000..e3a1a63e36 --- /dev/null +++ b/linuxthreads/man/sem_init.man @@ -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). + diff --git a/linuxthreads/man/troffprepro b/linuxthreads/man/troffprepro new file mode 100755 index 0000000000..ba564fefbe --- /dev/null +++ b/linuxthreads/man/troffprepro @@ -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" + } +} + + diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c new file mode 100644 index 0000000000..325955db4a --- /dev/null +++ b/linuxthreads/manager.c @@ -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; +} diff --git a/linuxthreads/mutex.c b/linuxthreads/mutex.c new file mode 100644 index 0000000000..d4ebcb827a --- /dev/null +++ b/linuxthreads/mutex.c @@ -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) diff --git a/linuxthreads/ptfork.c b/linuxthreads/ptfork.c new file mode 100644 index 0000000000..8a7b7972a3 --- /dev/null +++ b/linuxthreads/ptfork.c @@ -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; +} diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c new file mode 100644 index 0000000000..994233ebc7 --- /dev/null +++ b/linuxthreads/pthread.c @@ -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 diff --git a/linuxthreads/queue.h b/linuxthreads/queue.h new file mode 100644 index 0000000000..60039cce6e --- /dev/null +++ b/linuxthreads/queue.h @@ -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; +} diff --git a/linuxthreads/restart.h b/linuxthreads/restart.h new file mode 100644 index 0000000000..4b4a1d7937 --- /dev/null +++ b/linuxthreads/restart.h @@ -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); + } +} diff --git a/linuxthreads/rwlock.c b/linuxthreads/rwlock.c new file mode 100644 index 0000000000..c6b281551a --- /dev/null +++ b/linuxthreads/rwlock.c @@ -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; +} diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c new file mode 100644 index 0000000000..bd07439cde --- /dev/null +++ b/linuxthreads/semaphore.c @@ -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); + } +} diff --git a/linuxthreads/semaphore.h b/linuxthreads/semaphore.h new file mode 100644 index 0000000000..9f01a7f4fa --- /dev/null +++ b/linuxthreads/semaphore.h @@ -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 */ diff --git a/linuxthreads/shlib-versions b/linuxthreads/shlib-versions new file mode 100644 index 0000000000..99b0ef1ea2 --- /dev/null +++ b/linuxthreads/shlib-versions @@ -0,0 +1,2 @@ +# Xavier Leroy's Linux clone based thread library. +.*-.*-linux.* libpthread=0 diff --git a/linuxthreads/signals.c b/linuxthreads/signals.c new file mode 100644 index 0000000000..905e11e5fe --- /dev/null +++ b/linuxthreads/signals.c @@ -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; + } +} diff --git a/linuxthreads/specific.c b/linuxthreads/specific.c new file mode 100644 index 0000000000..71f1ede541 --- /dev/null +++ b/linuxthreads/specific.c @@ -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]; +} diff --git a/linuxthreads/spinlock.h b/linuxthreads/spinlock.h new file mode 100644 index 0000000000..d324abbc84 --- /dev/null +++ b/linuxthreads/spinlock.h @@ -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 +} diff --git a/linuxthreads/sysdeps/alpha/bits/semaphore.h b/linuxthreads/sysdeps/alpha/bits/semaphore.h new file mode 100644 index 0000000000..323fea159e --- /dev/null +++ b/linuxthreads/sysdeps/alpha/bits/semaphore.h @@ -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; diff --git a/linuxthreads/sysdeps/alpha/pt-machine.h b/linuxthreads/sysdeps/alpha/pt-machine.h new file mode 100644 index 0000000000..a0c7cc77e2 --- /dev/null +++ b/linuxthreads/sysdeps/alpha/pt-machine.h @@ -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; +} diff --git a/linuxthreads/sysdeps/arm/Implies b/linuxthreads/sysdeps/arm/Implies new file mode 100644 index 0000000000..7edcd7e757 --- /dev/null +++ b/linuxthreads/sysdeps/arm/Implies @@ -0,0 +1 @@ +pthread/no-cmpxchg diff --git a/linuxthreads/sysdeps/arm/pt-machine.h b/linuxthreads/sysdeps/arm/pt-machine.h new file mode 100644 index 0000000000..0b9bc01fc1 --- /dev/null +++ b/linuxthreads/sysdeps/arm/pt-machine.h @@ -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"); diff --git a/linuxthreads/sysdeps/i386/Implies b/linuxthreads/sysdeps/i386/Implies new file mode 100644 index 0000000000..7edcd7e757 --- /dev/null +++ b/linuxthreads/sysdeps/i386/Implies @@ -0,0 +1 @@ +pthread/no-cmpxchg diff --git a/linuxthreads/sysdeps/i386/pt-machine.h b/linuxthreads/sysdeps/i386/pt-machine.h new file mode 100644 index 0000000000..ef4df2a1a3 --- /dev/null +++ b/linuxthreads/sysdeps/i386/pt-machine.h @@ -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; +} diff --git a/linuxthreads/sysdeps/m68k/Implies b/linuxthreads/sysdeps/m68k/Implies new file mode 100644 index 0000000000..81e93666c4 --- /dev/null +++ b/linuxthreads/sysdeps/m68k/Implies @@ -0,0 +1 @@ +pthread/cmpxchg diff --git a/linuxthreads/sysdeps/m68k/pt-machine.h b/linuxthreads/sysdeps/m68k/pt-machine.h new file mode 100644 index 0000000000..c5c6cabe35 --- /dev/null +++ b/linuxthreads/sysdeps/m68k/pt-machine.h @@ -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; +} diff --git a/linuxthreads/sysdeps/mips/Implies b/linuxthreads/sysdeps/mips/Implies new file mode 100644 index 0000000000..81e93666c4 --- /dev/null +++ b/linuxthreads/sysdeps/mips/Implies @@ -0,0 +1 @@ +pthread/cmpxchg diff --git a/linuxthreads/sysdeps/mips/pt-machine.h b/linuxthreads/sysdeps/mips/pt-machine.h new file mode 100644 index 0000000000..d15da7535b --- /dev/null +++ b/linuxthreads/sysdeps/mips/pt-machine.h @@ -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; +} diff --git a/linuxthreads/sysdeps/powerpc/Implies b/linuxthreads/sysdeps/powerpc/Implies new file mode 100644 index 0000000000..81e93666c4 --- /dev/null +++ b/linuxthreads/sysdeps/powerpc/Implies @@ -0,0 +1 @@ +pthread/cmpxchg diff --git a/linuxthreads/sysdeps/powerpc/bits/semaphore.h b/linuxthreads/sysdeps/powerpc/bits/semaphore.h new file mode 100644 index 0000000000..3770eedf15 --- /dev/null +++ b/linuxthreads/sysdeps/powerpc/bits/semaphore.h @@ -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; diff --git a/linuxthreads/sysdeps/powerpc/pt-machine.h b/linuxthreads/sysdeps/powerpc/pt-machine.h new file mode 100644 index 0000000000..a08828b322 --- /dev/null +++ b/linuxthreads/sysdeps/powerpc/pt-machine.h @@ -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; +} diff --git a/linuxthreads/sysdeps/pthread/Makefile b/linuxthreads/sysdeps/pthread/Makefile new file mode 100644 index 0000000000..419650c6a6 --- /dev/null +++ b/linuxthreads/sysdeps/pthread/Makefile @@ -0,0 +1,3 @@ +ifeq ($(subdir),libio) +sysdep_headers += bits/stdio-lock.h +endif diff --git a/linuxthreads/sysdeps/pthread/bits/libc-lock.h b/linuxthreads/sysdeps/pthread/bits/libc-lock.h new file mode 100644 index 0000000000..530d48c6da --- /dev/null +++ b/linuxthreads/sysdeps/pthread/bits/libc-lock.h @@ -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 */ diff --git a/linuxthreads/sysdeps/pthread/bits/stdio-lock.h b/linuxthreads/sysdeps/pthread/bits/stdio-lock.h new file mode 100644 index 0000000000..23ebf407f9 --- /dev/null +++ b/linuxthreads/sysdeps/pthread/bits/stdio-lock.h @@ -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) diff --git a/linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h b/linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h new file mode 100644 index 0000000000..0cdbc05b90 --- /dev/null +++ b/linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h @@ -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; diff --git a/linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h b/linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h new file mode 100644 index 0000000000..4d801a26a6 --- /dev/null +++ b/linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h @@ -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; diff --git a/linuxthreads/sysdeps/pthread/pthread.h b/linuxthreads/sysdeps/pthread/pthread.h new file mode 100644 index 0000000000..b62706a811 --- /dev/null +++ b/linuxthreads/sysdeps/pthread/pthread.h @@ -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 */ diff --git a/linuxthreads/sysdeps/sparc/sparc32/Implies b/linuxthreads/sysdeps/sparc/sparc32/Implies new file mode 100644 index 0000000000..7edcd7e757 --- /dev/null +++ b/linuxthreads/sysdeps/sparc/sparc32/Implies @@ -0,0 +1 @@ +pthread/no-cmpxchg diff --git a/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h b/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h new file mode 100644 index 0000000000..13f78e319a --- /dev/null +++ b/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h @@ -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)) diff --git a/linuxthreads/sysdeps/sparc/sparc64/Implies b/linuxthreads/sysdeps/sparc/sparc64/Implies new file mode 100644 index 0000000000..81e93666c4 --- /dev/null +++ b/linuxthreads/sysdeps/sparc/sparc64/Implies @@ -0,0 +1 @@ +pthread/cmpxchg diff --git a/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h b/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h new file mode 100644 index 0000000000..5424860786 --- /dev/null +++ b/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h @@ -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; +} diff --git a/linuxthreads/sysdeps/unix/sysv/linux/Implies b/linuxthreads/sysdeps/unix/sysv/linux/Implies new file mode 100644 index 0000000000..f1b3e8939c --- /dev/null +++ b/linuxthreads/sysdeps/unix/sysv/linux/Implies @@ -0,0 +1 @@ +pthread diff --git a/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h b/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h new file mode 100644 index 0000000000..68635ba36f --- /dev/null +++ b/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h @@ -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 diff --git a/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h b/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h new file mode 100644 index 0000000000..feb4e10c6e --- /dev/null +++ b/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h @@ -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 */ diff --git a/linuxthreads/sysdeps/unix/sysv/linux/configure b/linuxthreads/sysdeps/unix/sysv/linux/configure new file mode 100644 index 0000000000..229414dd74 --- /dev/null +++ b/linuxthreads/sysdeps/unix/sysv/linux/configure @@ -0,0 +1,3 @@ +# Local configure fragment for sysdeps/unix/sysv/linux. + +DEFINES="$DEFINES -D_LIBC_REENTRANT" diff --git a/linuxthreads/weaks.c b/linuxthreads/weaks.c new file mode 100644 index 0000000000..da645c1aea --- /dev/null +++ b/linuxthreads/weaks.c @@ -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) +{ +} diff --git a/linuxthreads/wrapsyscall.c b/linuxthreads/wrapsyscall.c new file mode 100644 index 0000000000..4659692d56 --- /dev/null +++ b/linuxthreads/wrapsyscall.c @@ -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)) |