1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

LinuxThreads library.

1998-03-11 00:42  Wolfram Gloger  <wmglo@dent.med.uni-muenchen.de>
	* linuxthreads/manager.c: Enable resetting of the thread
	scheduling policy to SCHED_OTHER when the parent thread
	has a different one.
1998-02-01 13:51  Ulrich Drepper  <drepper@cygnus.com>
	* sysdeps/unix/sysv/linux/bits/posix_opt.h: Define
	_POSIX_ASYNCHRONOUS_IO.
	* sysdeps/pthread/pthread.h: Define bits for Unix98 variants of
	mutexes.
	* mutex.c: Implement new mutex types.
	* internals.h: Include <signal.h>.
	* libpthread.map: Add __erno_location and __h_errno_location.
	* errno.c: Return pointer to variable actually in use.  This might
	not be the one in the thread structure.
	* internals.h (struct _pthread_descr_struct): Add new fields p_errnop
	and p_h_errnop.
	* manager.c (__pthread_manager): Set p_errnop and p_h_errnop member
	of manager thread structure.
	(pthread_handle_create): Set p_errnop and p_h_errnop members for new
	thread.
	* pthread.c: Adapt initializer for thread structures.
	(__pthread_initial_thread): Set p_errnop and p_h_errnop member.
	(__pthread_reset_main_thread): Reset p_errnop and p_h_errnop of
	current thread to global variables.
1998-01-31 17:27  Ulrich Drepper  <drepper@cygnus.com>
	* rwlock.c: New file.
	* Makefile (libpthread-routines): Add rwlock.
	* sysdeps/pthread/pthread.h: Define data structures and declare
	functions.
	* libpthread.map: Add new functions.
1997-12-18 13:50  Philip Blundell  <pb@nexus.co.uk>
	* sysdeps/arm/pt-machine.h: New file; add ARM support.
	* sysdeps/arm/Implies: likewise.
	* README: Document it.
1997-12-13  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
	* signals.c: Remove unneeded initializer for sigwaited, saving a
1997-04-11 01:18  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
	* semaphore.c (sem_init): Set sem_spinlock only if available.
1997-12-04 01:48  Ulrich Drepper  <drepper@cygnus.com>
	* mutex.c: Implement PTHREAD_MUTEX_CHECKERROR.
	* sysdeps/pthread/pthread.h: Define PTHREAD_MUTEX_CHECKERROR.
	* Makefile: Update from LinuxThreads 0.7.
	* internals.h. Likewise.
	* manager.c: Likewise.
	* mutex.c: Likewise.
	* pthread.c: Likewise.
	* signals.c: Likewise.
	* specific.c: Likewise.
	* Examples/ex3.c: Likewise.
1997-11-20 18:13  Ulrich Drepper  <drepper@cygnus.com>
	* pthread.c (__pthread_reset_main_thread): Close pipe only if still
	open.
1997-10-29 05:38  Ulrich Drepper  <drepper@cygnus.com>

	* wrapsyscall.c: Add socket functions which are also cancelation
	points.

1997-10-19 21:40  Wolfram Gloger  <wg@wolfram.dent.med.uni-muenchen.de>

	* specific.c (__libc_internal_tsd_set, __libc_internal_tsd_get):
	New functions for fast thread specific data within libc.

	* internals.h: Add new array p_libc_specific to struct
	_pthread_descr_struct.

	* sysdeps/pthread/bits/libc-lock.h: Declare new functions.

1997-10-13 05:39  Ulrich Drepper  <drepper@cygnus.com>

	* semaphore.h: Add __BEGIN_DECLS/__END_DECLS.
	Reported by Ralf Corsepius <corsepiu@faw.uni-ulm.de>.

1997-08-29 03:05  Ulrich Drepper  <drepper@cygnus.com>

	* internals.h (struct _pthread_descr_struct): Add definitions for
	two-level specific key handling.
	* manager.c (pthread_handle_create): Initialize specific memory array.
	* specific.c: Implement two-level key handling.
	* weaks.c: Don't provide dummy key handling.
	* sysdeps/pthread/bits/libc-lock.h: Typedef __libc_lock_t (no #define).
	Add definition of __libc_key_t.
	* sysdeps/unix/sysv/linux/bits/local_lim.h: Define PTHREAD_KEYS_MAX
	as 1024.
	Add definition of _POSIX_THREAD_DESTRUCTOR_ITERATIONS and
	PTHREAD_DESTRUCTOR_ITERATIONS.

	* manager.c (pthread_handle_create): Compare mmap result with
	MAP_FAILED.

	* ptfork.c: Rename to __pthread_atfork and make old name a weak alias.
	* sysdeps/pthread/bits/pthread.h: Add prototype for __pthread_atfork.

1997-08-22 19:04  Richard Henderson  <rth@cygnus.com>

	sysdeps/sparc -> sysdeps/sparc/sparc32
	sysdeps/sparc64 -> sysdeps/sparc/sparc64

	* internals.h: Change definition of THREAD_SELF to be an expression,
	not a statement that did a return.
	* sysdeps/alpha/pt-machine.h (THREAD_SELF): Update accordingly.
	* sysdeps/sparc/sparc32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF):
	Follow Solaris and use a "system reserved" register (%g6) to hold
	the thread descriptor.
	* sysdeps/sparc/sparc64/pt-machine.h: Likewise.

1997-08-03 00:09  Ulrich Drepper  <drepper@cygnus.com>

	* mutex.c: Correct pthread_once.  Patch by Xavier Leroy.
	* sysdeps/pthread/pthread.h: Add prototype for __pthread_once.
	* sysdeps/pthread/bits/pthread.h: Add macros for __libc_once.

	* semaphore.c: Include spinlock.h only when needed.

	* specific.c (__pthread_setsepcific, __pthread_getspecific): Reject
	keys for entries not in use.

	* weaks.c: Implement key handling functions for real.

1997-06-29  01:04  Richard Henderson  <richard@gnu.ai.mit.edu>

	Initial sparc64-linux support:
	* linuxthreads/sysdeps/sparc64/Implies: New file.
	* linuxthreads/sysdeps/sparc64/pt-machine.h: Likewise.

1997-06-29 00:48  Ulrich Drepper  <drepper@cygnus.com>

	* semaphore.c: Include spinlock.h at correct place.
	Patch by HJ Lu.

1997-06-13 10:06  Richard Henderson  <rth@tamu.edu>

	The Great Bit File Move:
	* sysdeps/alpha/semaphorebits.h: -> .../bits/semaphore.h.
	* sysdeps/powerpc/semaphorebits.h: Likewise.
	* sysdeps/pthread/cmpxchg/semaphorebits.h: Likewise.
	* sysdeps/pthread/no-cmpxchg/semaphorebits.h: Likewise.
	* sysdeps/pthread/libc-lock.h: -> bits/
	* sysdeps/pthread/stdio-lock.h: Likewise.
	* sysdeps/unix/sysv/linux/local_lim.h: Likewise.
	* sysdeps/unix/sysv/linux/posix_opt.h: Likewise.
	* semaphore.h: Likewise.
	* sysdeps/pthread/pthread.h: Likewise.

	* lockfile.c: <foo.h> -> <bits/foo.h>.
	* semaphore.h: Likewise.

	* Makefile: (headers): foo.h -> bits/foo.h.
	* sysdeps/pthread/Makefile: Likewise.

1997-04-11 01:18  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>

	* semaphore.c (sem_init): Set sem_spinlock only if available.

	* sysdeps/m68k/pt-machine.h (testandset, __compare_and_swap): Fix
	asm constraints.

1997-04-09 03:00  Ulrich Drepper  <drepper@cygnus.com>

	Update from LinuxThreads 0.6.

	* attr.c (pthread_attr_getdetachstate): Use __sched_get_priority_max
	and __sched_get_priority_min instead of names without `__'.

	* manager.c: Rewrite large parts to implement opaque pthread_t.

	* cancel.c: Adapt for opaque pthread_t type.
	* condvar.c: Likewise.
	* errno.c: Likewise.
	* join.c: Likewise.
	* mutex.c: Likewise.
	* pthread.c: Likewise.
	* signals.c: Likewise.
	* specific.c: Likewise.
	* restart.h: Likewise.
	* queue.h: Likewise.
	* Examples/ex3.c: Likewise.
	* Examples/ex4.c: Likewise.
	* sysdeps/pthread/pthread.h: Likewise.

	* pthread.c: Accumulate time for all threads in thread manager.

	* semaphore.c: Implement fallback implementation for architectures
	sometimes missing compare-exchange operations.

	* cancel.c (pthread_cancel): Validate handle argument.
	* join.c (pthread_join): Likewise.
	(pthread_detach): Likewise.
	* signals.c (pthread_kill): Likewise.

	* spinlock.h (acquire): Use __sched_yield not sched_yield.

	* queue.h (enqueue): Enqueue thread according to priority.

	* internals.c (struct pthread_start_args): New struct for passing
	args to cloning function.
	(struct _pthread): Rename to _pthread_descr_struct and adapt for
	opaque pthread_t.

	* Examples/Makefile (clean): Pass -f option to rm.

	* sysdeps/i386/pt-machine.h: Add check for compare-exchange instruction
	and define TEST_FOR_COMPARE_AND_SWAP.
	* sysdeps/i386/i486/pt-machine.h: Removed.

	* sysdeps/unix/sysv/linux/local_lim.h (PTHREAD_THREADS_MAX): Increase
	to 1024.

1997-04-04 16:38  Ulrich Drepper  <drepper@cygnus.com>

	* restart.h (suspend): Clear p_signal before suspending.
	(suspend_with_cancellation): Likewise.
	Patch by Xavier Leroy <Xavier.Leroy@inria.fr>.

	* weaks.c: Make __pthread_key_create return 1.
	* sysdeps/pthread/libc-lock.h: Define __libc_key_create,
	__libc_getspecific, __libc_setspecific, and __libc_key_t.
	* sysdeps/pthread/stdio-lock.h: Don't care for implementation not
	using libio.

1997-03-19 15:13  Miguel de Icaza  <miguel@nuclecu.unam.mx>

	* sysdeps/sparc/pt-machine (RELEASE): Fix.

1997-03-01 07:55  Geoff Keating  <geoffk@ozemail.com.au>

	* sysdeps/powerpc/Implies: Added.
	* sysdeps/powerpc/pt-machine.h: Added.
	* sysdeps/powerpc/semaphorebits.h: Added.

1997-01-22 01:22  Ulrich Drepper  <drepper@cygnus.com>

	* linuxtheads/pthread.c (__pthread_initial_thread): Correct
	initializer.
	(__pthread_manager_thread): Likewise.
	Reported by Andreas Jaeger.

1997-01-18 22:15  Richard Henderson  <rth@tamu.edu>

	Since sigset_t no longer fits in a register, we can't pass in the
	thread's initial mask so easily.  Take this opportunity to simplify
	the clone implementation by only accepting a single void* argument.

	* linuxthreads/manager.c (__pthread_manager): Put thread vitals
	in the thread struct instead of as arguments through clone.
	(pthread_start_thread): Look for them there.
	* linuxthreads/internals.h (struct _pthread): Add p_initial_fn,
	p_initial_fn_arg, p_initial_mask.  Fix __pthread_manager proto.
	* linuxthreads/pthread.c (pthread_initialize_manager): Revise
	clone invocation.
This commit is contained in:
Ulrich Drepper
1998-03-11 12:42:25 +00:00
parent 3d76e77847
commit 5afdca0087
87 changed files with 10213 additions and 0 deletions

1
linuxthreads/Banner Normal file
View File

@ -0,0 +1 @@
linuxthreads-0.7 by Xavier Leroy

271
linuxthreads/ChangeLog Normal file
View File

@ -0,0 +1,271 @@
1998-03-11 00:42 Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>
* linuxthreads/manager.c: Enable resetting of the thread
scheduling policy to SCHED_OTHER when the parent thread
has a different one.
1998-02-01 13:51 Ulrich Drepper <drepper@cygnus.com>
* sysdeps/unix/sysv/linux/bits/posix_opt.h: Define
_POSIX_ASYNCHRONOUS_IO.
* sysdeps/pthread/pthread.h: Define bits for Unix98 variants of
mutexes.
* mutex.c: Implement new mutex types.
* internals.h: Include <signal.h>.
* libpthread.map: Add __erno_location and __h_errno_location.
* errno.c: Return pointer to variable actually in use. This might
not be the one in the thread structure.
* internals.h (struct _pthread_descr_struct): Add new fields p_errnop
and p_h_errnop.
* manager.c (__pthread_manager): Set p_errnop and p_h_errnop member
of manager thread structure.
(pthread_handle_create): Set p_errnop and p_h_errnop members for new
thread.
* pthread.c: Adapt initializer for thread structures.
(__pthread_initial_thread): Set p_errnop and p_h_errnop member.
(__pthread_reset_main_thread): Reset p_errnop and p_h_errnop of
current thread to global variables.
1998-01-31 17:27 Ulrich Drepper <drepper@cygnus.com>
* rwlock.c: New file.
* Makefile (libpthread-routines): Add rwlock.
* sysdeps/pthread/pthread.h: Define data structures and declare
functions.
* libpthread.map: Add new functions.
1997-12-18 13:50 Philip Blundell <pb@nexus.co.uk>
* sysdeps/arm/pt-machine.h: New file; add ARM support.
* sysdeps/arm/Implies: likewise.
* README: Document it.
1997-12-13 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* signals.c: Remove unneeded initializer for sigwaited, saving a
warning.
1997-04-11 01:18 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* semaphore.c (sem_init): Set sem_spinlock only if available.
1997-12-04 01:48 Ulrich Drepper <drepper@cygnus.com>
* mutex.c: Implement PTHREAD_MUTEX_CHECKERROR.
* sysdeps/pthread/pthread.h: Define PTHREAD_MUTEX_CHECKERROR.
* Makefile: Update from LinuxThreads 0.7.
* internals.h. Likewise.
* manager.c: Likewise.
* mutex.c: Likewise.
* pthread.c: Likewise.
* signals.c: Likewise.
* specific.c: Likewise.
* Examples/ex3.c: Likewise.
1997-11-20 18:13 Ulrich Drepper <drepper@cygnus.com>
* pthread.c (__pthread_reset_main_thread): Close pipe only if still
open.
1997-10-29 05:38 Ulrich Drepper <drepper@cygnus.com>
* wrapsyscall.c: Add socket functions which are also cancelation
points.
1997-10-19 21:40 Wolfram Gloger <wg@wolfram.dent.med.uni-muenchen.de>
* specific.c (__libc_internal_tsd_set, __libc_internal_tsd_get):
New functions for fast thread specific data within libc.
* internals.h: Add new array p_libc_specific to struct
_pthread_descr_struct.
* sysdeps/pthread/bits/libc-lock.h: Declare new functions.
1997-10-13 05:39 Ulrich Drepper <drepper@cygnus.com>
* semaphore.h: Add __BEGIN_DECLS/__END_DECLS.
Reported by Ralf Corsepius <corsepiu@faw.uni-ulm.de>.
1997-08-29 03:05 Ulrich Drepper <drepper@cygnus.com>
* internals.h (struct _pthread_descr_struct): Add definitions for
two-level specific key handling.
* manager.c (pthread_handle_create): Initialize specific memory array.
* specific.c: Implement two-level key handling.
* weaks.c: Don't provide dummy key handling.
* sysdeps/pthread/bits/libc-lock.h: Typedef __libc_lock_t (no #define).
Add definition of __libc_key_t.
* sysdeps/unix/sysv/linux/bits/local_lim.h: Define PTHREAD_KEYS_MAX
as 1024.
Add definition of _POSIX_THREAD_DESTRUCTOR_ITERATIONS and
PTHREAD_DESTRUCTOR_ITERATIONS.
* manager.c (pthread_handle_create): Compare mmap result with
MAP_FAILED.
* ptfork.c: Rename to __pthread_atfork and make old name a weak alias.
* sysdeps/pthread/bits/pthread.h: Add prototype for __pthread_atfork.
1997-08-22 19:04 Richard Henderson <rth@cygnus.com>
sysdeps/sparc -> sysdeps/sparc/sparc32
sysdeps/sparc64 -> sysdeps/sparc/sparc64
* internals.h: Change definition of THREAD_SELF to be an expression,
not a statement that did a return.
* sysdeps/alpha/pt-machine.h (THREAD_SELF): Update accordingly.
* sysdeps/sparc/sparc32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF):
Follow Solaris and use a "system reserved" register (%g6) to hold
the thread descriptor.
* sysdeps/sparc/sparc64/pt-machine.h: Likewise.
1997-08-03 00:09 Ulrich Drepper <drepper@cygnus.com>
* mutex.c: Correct pthread_once. Patch by Xavier Leroy.
* sysdeps/pthread/pthread.h: Add prototype for __pthread_once.
* sysdeps/pthread/bits/pthread.h: Add macros for __libc_once.
* semaphore.c: Include spinlock.h only when needed.
* specific.c (__pthread_setsepcific, __pthread_getspecific): Reject
keys for entries not in use.
* weaks.c: Implement key handling functions for real.
1997-06-29 01:04 Richard Henderson <richard@gnu.ai.mit.edu>
Initial sparc64-linux support:
* linuxthreads/sysdeps/sparc64/Implies: New file.
* linuxthreads/sysdeps/sparc64/pt-machine.h: Likewise.
1997-06-29 00:48 Ulrich Drepper <drepper@cygnus.com>
* semaphore.c: Include spinlock.h at correct place.
Patch by HJ Lu.
1997-06-13 10:06 Richard Henderson <rth@tamu.edu>
The Great Bit File Move:
* sysdeps/alpha/semaphorebits.h: -> .../bits/semaphore.h.
* sysdeps/powerpc/semaphorebits.h: Likewise.
* sysdeps/pthread/cmpxchg/semaphorebits.h: Likewise.
* sysdeps/pthread/no-cmpxchg/semaphorebits.h: Likewise.
* sysdeps/pthread/libc-lock.h: -> bits/
* sysdeps/pthread/stdio-lock.h: Likewise.
* sysdeps/unix/sysv/linux/local_lim.h: Likewise.
* sysdeps/unix/sysv/linux/posix_opt.h: Likewise.
* semaphore.h: Likewise.
* sysdeps/pthread/pthread.h: Likewise.
* lockfile.c: <foo.h> -> <bits/foo.h>.
* semaphore.h: Likewise.
* Makefile: (headers): foo.h -> bits/foo.h.
* sysdeps/pthread/Makefile: Likewise.
1997-04-11 01:18 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>
* semaphore.c (sem_init): Set sem_spinlock only if available.
* sysdeps/m68k/pt-machine.h (testandset, __compare_and_swap): Fix
asm constraints.
1997-04-09 03:00 Ulrich Drepper <drepper@cygnus.com>
Update from LinuxThreads 0.6.
* attr.c (pthread_attr_getdetachstate): Use __sched_get_priority_max
and __sched_get_priority_min instead of names without `__'.
* manager.c: Rewrite large parts to implement opaque pthread_t.
* cancel.c: Adapt for opaque pthread_t type.
* condvar.c: Likewise.
* errno.c: Likewise.
* join.c: Likewise.
* mutex.c: Likewise.
* pthread.c: Likewise.
* signals.c: Likewise.
* specific.c: Likewise.
* restart.h: Likewise.
* queue.h: Likewise.
* Examples/ex3.c: Likewise.
* Examples/ex4.c: Likewise.
* sysdeps/pthread/pthread.h: Likewise.
* pthread.c: Accumulate time for all threads in thread manager.
* semaphore.c: Implement fallback implementation for architectures
sometimes missing compare-exchange operations.
* cancel.c (pthread_cancel): Validate handle argument.
* join.c (pthread_join): Likewise.
(pthread_detach): Likewise.
* signals.c (pthread_kill): Likewise.
* spinlock.h (acquire): Use __sched_yield not sched_yield.
* queue.h (enqueue): Enqueue thread according to priority.
* internals.c (struct pthread_start_args): New struct for passing
args to cloning function.
(struct _pthread): Rename to _pthread_descr_struct and adapt for
opaque pthread_t.
* Examples/Makefile (clean): Pass -f option to rm.
* sysdeps/i386/pt-machine.h: Add check for compare-exchange instruction
and define TEST_FOR_COMPARE_AND_SWAP.
* sysdeps/i386/i486/pt-machine.h: Removed.
* sysdeps/unix/sysv/linux/local_lim.h (PTHREAD_THREADS_MAX): Increase
to 1024.
1997-04-04 16:38 Ulrich Drepper <drepper@cygnus.com>
* restart.h (suspend): Clear p_signal before suspending.
(suspend_with_cancellation): Likewise.
Patch by Xavier Leroy <Xavier.Leroy@inria.fr>.
* weaks.c: Make __pthread_key_create return 1.
* sysdeps/pthread/libc-lock.h: Define __libc_key_create,
__libc_getspecific, __libc_setspecific, and __libc_key_t.
* sysdeps/pthread/stdio-lock.h: Don't care for implementation not
using libio.
1997-03-19 15:13 Miguel de Icaza <miguel@nuclecu.unam.mx>
* sysdeps/sparc/pt-machine (RELEASE): Fix.
1997-03-01 07:55 Geoff Keating <geoffk@ozemail.com.au>
* sysdeps/powerpc/Implies: Added.
* sysdeps/powerpc/pt-machine.h: Added.
* sysdeps/powerpc/semaphorebits.h: Added.
1997-01-22 01:22 Ulrich Drepper <drepper@cygnus.com>
* linuxtheads/pthread.c (__pthread_initial_thread): Correct
initializer.
(__pthread_manager_thread): Likewise.
Reported by Andreas Jaeger.
1997-01-18 22:15 Richard Henderson <rth@tamu.edu>
Since sigset_t no longer fits in a register, we can't pass in the
thread's initial mask so easily. Take this opportunity to simplify
the clone implementation by only accepting a single void* argument.
* linuxthreads/manager.c (__pthread_manager): Put thread vitals
in the thread struct instead of as arguments through clone.
(pthread_start_thread): Look for them there.
* linuxthreads/internals.h (struct _pthread): Add p_initial_fn,
p_initial_fn_arg, p_initial_mask. Fix __pthread_manager proto.
* linuxthreads/pthread.c (pthread_initialize_manager): Revise
clone invocation.

73
linuxthreads/Changes Normal file
View File

@ -0,0 +1,73 @@
Release 0.7:
- Destructors for thread-specific data now conform to the POSIX semantics
(call destructors again if non-NULL TSD remains after a round of
destruction).
- Implemented thread-specific data as a sparse array, allows more TSD keys
and smaller thread descriptors (Ulrich Drepper).
- Added "error checking" mutexes.
- Protect against multiple sigwait() on the same signals.
- Simplified implementation of semaphores when compare_and_swap is
not available.
- Fixed bug in fork() where stdin was closed if fork() was called before
the first pthread_create().
- Fixed bug in the gethostby*_r functions (bad result if null bytes
in addresses).
- Typos in manual pages corrected.
- First cut at a PowerPC port (not working yet, runs into problems
with gcc and with the C library).
Release 0.6:
- Validation of thread identifiers: no more crashes when operating on
a thread that has exited (based on Pavel Krauz's ideas).
- Added fallback implementation of semaphores for the 386 and the
Sparc.
- Fixed a bug in signal handling causing false restarts of suspended
threads.
- Fixed a bug in realtime scheduling causing all threads to have
default scheduling on Ix86 with libc5.
- With realtime scheduling, unlocking a mutex now restarts the
highest priority thread waiting on the mutex, not the
first-suspended thread (Richard Neitzel).
- Timing a process now returns cumulative times for all threads, not
just times for the initial thread (suggested by Wolfram Gloger).
- Cleaned up name space (internal defs prefixed by __, weak aliases
for non-portable extensions).
- MIPS port (contributed by Ralf Baechle).
Release 0.5:
- Signal-safe semaphores a la POSIX 1003.1b added.
- Locking bug in pthread_mutex_trylock over recursive mutexes fixed.
- Race conditions in thread cancellation fixed.
- Sparc port (contributed by Miguel de Icaza).
- Support for getpwnam_r and getpwuid_r.
- Added pthread_kill_other_threads_np to be used in conjunction with
exec*().
Release 0.4:
- Manual pages for all functions.
- Synchronization bug causing accumulation of zombie processes fixed.
- Race condition in pthread_cond_timedwait fixed.
- Recursive mutexes are back by popular demand.
- Partial support for realtime scheduling (initiated by Richard Neitzel).
- pthread.h cleaned up a lot: now C++ compatible, added missing "const"
qualifiers, added short documentation, put to GNU libc standards
for name space pollution (Ulrich Drepper).
- Motorola 68k port (contributed by Andreas Schwab).
- Interaction with fork(2) cleaned up a lot.
Release 0.3:
- Thread creation and reclaimation now performed by a centralized
"thread manager" thread.
- Removed recursive mutexes to make regular mutexes more efficient.
- Now available as a shared library (contributed by Richard Henderson).
- Alpha port (contributed by Richard Henderson).
- Fixed many small discrepancies with Posix 1003.1c.
- Put under the LGPL instead of the GPL.
Release 0.2:
- Reentrant libc functions (adapted from libc 5.3.9 by Peeter Joot)
- pthread_cond_wait did not reacquire the mutex correctly on return
- More efficient pthread_cond_broadcast
Release 0.1:
- First public release

View File

@ -0,0 +1,15 @@
CC=gcc
CFLAGS=-g -O -Wall -I.. -D_REENTRANT
LIBPTHREAD=../libpthread.a
PROGS=ex1 ex2 ex3 ex4 ex5 proxy
all: $(PROGS)
.c:
$(CC) $(CFLAGS) -o $* $*.c $(LIBPTHREAD)
$(PROGS):
clean:
rm -f $(PROGS)

View File

@ -0,0 +1,36 @@
/* Creates two threads, one printing 10000 "a"s, the other printing
10000 "b"s.
Illustrates: thread creation, thread joining. */
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include "pthread.h"
void * process(void * arg)
{
int i;
fprintf(stderr, "Starting process %s\n", (char *) arg);
for (i = 0; i < 10000; i++) {
write(1, (char *) arg, 1);
}
return NULL;
}
int main()
{
int retcode;
pthread_t th_a, th_b;
void * retval;
retcode = pthread_create(&th_a, NULL, process, "a");
if (retcode != 0) fprintf(stderr, "create a failed %d\n", retcode);
retcode = pthread_create(&th_b, NULL, process, "b");
if (retcode != 0) fprintf(stderr, "create b failed %d\n", retcode);
retcode = pthread_join(th_a, &retval);
if (retcode != 0) fprintf(stderr, "join a failed %d\n", retcode);
retcode = pthread_join(th_b, &retval);
if (retcode != 0) fprintf(stderr, "join b failed %d\n", retcode);
return 0;
}

116
linuxthreads/Examples/ex2.c Normal file
View File

@ -0,0 +1,116 @@
/* The classic producer-consumer example.
Illustrates mutexes and conditions.
All integers between 0 and 9999 should be printed exactly twice,
once to the right of the arrow and once to the left. */
#include <stdio.h>
#include "pthread.h"
#define BUFFER_SIZE 16
/* Circular buffer of integers. */
struct prodcons {
int buffer[BUFFER_SIZE]; /* the actual data */
pthread_mutex_t lock; /* mutex ensuring exclusive access to buffer */
int readpos, writepos; /* positions for reading and writing */
pthread_cond_t notempty; /* signaled when buffer is not empty */
pthread_cond_t notfull; /* signaled when buffer is not full */
};
/* Initialize a buffer */
void init(struct prodcons * b)
{
pthread_mutex_init(&b->lock, NULL);
pthread_cond_init(&b->notempty, NULL);
pthread_cond_init(&b->notfull, NULL);
b->readpos = 0;
b->writepos = 0;
}
/* Store an integer in the buffer */
void put(struct prodcons * b, int data)
{
pthread_mutex_lock(&b->lock);
/* Wait until buffer is not full */
while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
pthread_cond_wait(&b->notfull, &b->lock);
/* pthread_cond_wait reacquired b->lock before returning */
}
/* Write the data and advance write pointer */
b->buffer[b->writepos] = data;
b->writepos++;
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
/* Signal that the buffer is now not empty */
pthread_cond_signal(&b->notempty);
pthread_mutex_unlock(&b->lock);
}
/* Read and remove an integer from the buffer */
int get(struct prodcons * b)
{
int data;
pthread_mutex_lock(&b->lock);
/* Wait until buffer is not empty */
while (b->writepos == b->readpos) {
pthread_cond_wait(&b->notempty, &b->lock);
}
/* Read the data and advance read pointer */
data = b->buffer[b->readpos];
b->readpos++;
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
/* Signal that the buffer is now not full */
pthread_cond_signal(&b->notfull);
pthread_mutex_unlock(&b->lock);
return data;
}
/* A test program: one thread inserts integers from 1 to 10000,
the other reads them and prints them. */
#define OVER (-1)
struct prodcons buffer;
void * producer(void * data)
{
int n;
for (n = 0; n < 10000; n++) {
printf("%d --->\n", n);
put(&buffer, n);
}
put(&buffer, OVER);
return NULL;
}
void * consumer(void * data)
{
int d;
while (1) {
d = get(&buffer);
if (d == OVER) break;
printf("---> %d\n", d);
}
return NULL;
}
int main()
{
pthread_t th_a, th_b;
void * retval;
init(&buffer);
/* Create the threads */
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
/* Wait until producer and consumer finish. */
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
return 0;
}

144
linuxthreads/Examples/ex3.c Normal file
View File

@ -0,0 +1,144 @@
/* Multi-thread searching.
Illustrates: thread cancellation, cleanup handlers. */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
/* Defines the number of searching threads */
#define NUM_THREADS 5
/* Function prototypes */
void *search(void *);
void print_it(void *);
/* Global variables */
pthread_t threads[NUM_THREADS];
pthread_mutex_t lock;
int tries;
int main(argc, argv)
int argc;
char ** argv;
{
int i;
int pid;
/* create a number to search for */
pid = getpid();
printf("Searching for the number = %d...\n", pid);
/* Initialize the mutex lock */
pthread_mutex_init(&lock, NULL);
/* Create the searching threads */
for (i=0; i<NUM_THREADS; i++)
pthread_create(&threads[i], NULL, search, (void *)pid);
/* Wait for (join) all the searching threads */
for (i=0; i<NUM_THREADS; i++)
pthread_join(threads[i], NULL);
printf("It took %d tries to find the number.\n", tries);
/* Exit the program */
return 0;
}
/* This is the cleanup function that is called
when the threads are cancelled */
void print_it(void *arg)
{
int *try = (int *) arg;
pthread_t tid;
/* Get the calling thread's ID */
tid = pthread_self();
/* Print where the thread was in its search when it was cancelled */
printf("Thread %lx was canceled on its %d try.\n", tid, *try);
}
/* This is the search routine that is executed in each thread */
void *search(void *arg)
{
int num = (int) arg;
int i, j, ntries;
pthread_t tid;
/* get the calling thread ID */
tid = pthread_self();
/* use the thread ID to set the seed for the random number generator */
/* Since srand and rand are not thread-safe, serialize with lock */
pthread_mutex_lock(&lock);
srand((int)tid);
i = rand() & 0xFFFFFF;
pthread_mutex_unlock(&lock);
ntries = 0;
/* Set the cancellation parameters --
- Enable thread cancellation
- Defer the action of the cancellation */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
/* Push the cleanup routine (print_it) onto the thread
cleanup stack. This routine will be called when the
thread is cancelled. Also note that the pthread_cleanup_push
call must have a matching pthread_cleanup_pop call. The
push and pop calls MUST be at the same lexical level
within the code */
/* Pass address of `ntries' since the current value of `ntries' is not
the one we want to use in the cleanup function */
pthread_cleanup_push(print_it, (void *)&ntries);
/* Loop forever */
while (1) {
i = (i + 1) & 0xFFFFFF;
ntries++;
/* Does the random number match the target number? */
if (num == i) {
/* Try to lock the mutex lock --
if locked, check to see if the thread has been cancelled
if not locked then continue */
while (pthread_mutex_trylock(&lock) == EBUSY)
pthread_testcancel();
/* Set the global variable for the number of tries */
tries = ntries;
printf("Thread %lx found the number!\n", tid);
/* Cancel all the other threads */
for (j=0; j<NUM_THREADS; j++)
if (threads[j] != tid) pthread_cancel(threads[j]);
/* Break out of the while loop */
break;
}
/* Every 100 tries check to see if the thread has been cancelled. */
if (ntries % 100 == 0) {
pthread_testcancel();
}
}
/* The only way we can get here is when the thread breaks out
of the while loop. In this case the thread that makes it here
has found the number we are looking for and does not need to run
the thread cleanup function. This is why the pthread_cleanup_pop
function is called with a 0 argument; this will pop the cleanup
function off the stack without executing it */
pthread_cleanup_pop(0);
return((void *)0);
}

107
linuxthreads/Examples/ex4.c Normal file
View File

@ -0,0 +1,107 @@
/* Making a library function that uses static variables thread-safe.
Illustrates: thread-specific data, pthread_once(). */
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
/* This is a typical example of a library function that uses
static variables to accumulate results between calls.
Here, it just returns the concatenation of all string arguments
that were given to it. */
#if 0
char * str_accumulate(char * s)
{
static char accu[1024] = { 0 };
strcat(accu, s);
return accu;
}
#endif
/* Of course, this cannot be used in a multi-threaded program
because all threads store "accu" at the same location.
So, we'll use thread-specific data to have a different "accu"
for each thread. */
/* Key identifying the thread-specific data */
static pthread_key_t str_key;
/* "Once" variable ensuring that the key for str_alloc will be allocated
exactly once. */
static pthread_once_t str_alloc_key_once = PTHREAD_ONCE_INIT;
/* Forward functions */
static void str_alloc_key(void);
static void str_alloc_destroy_accu(void * accu);
/* Thread-safe version of str_accumulate */
char * str_accumulate(char * s)
{
char * accu;
/* Make sure the key is allocated */
pthread_once(&str_alloc_key_once, str_alloc_key);
/* Get the thread-specific data associated with the key */
accu = (char *) pthread_getspecific(str_key);
/* It's initially NULL, meaning that we must allocate the buffer first. */
if (accu == NULL) {
accu = malloc(1024);
if (accu == NULL) return NULL;
accu[0] = 0;
/* Store the buffer pointer in the thread-specific data. */
pthread_setspecific(str_key, (void *) accu);
printf("Thread %lx: allocating buffer at %p\n", pthread_self(), accu);
}
/* Now we can use accu just as in the non thread-safe code. */
strcat(accu, s);
return accu;
}
/* Function to allocate the key for str_alloc thread-specific data. */
static void str_alloc_key(void)
{
pthread_key_create(&str_key, str_alloc_destroy_accu);
printf("Thread %lx: allocated key %d\n", pthread_self(), str_key);
}
/* Function to free the buffer when the thread exits. */
/* Called only when the thread-specific data is not NULL. */
static void str_alloc_destroy_accu(void * accu)
{
printf("Thread %lx: freeing buffer at %p\n", pthread_self(), accu);
free(accu);
}
/* Test program */
void * process(void * arg)
{
char * res;
res = str_accumulate("Result of ");
res = str_accumulate((char *) arg);
res = str_accumulate(" thread");
printf("Thread %lx: \"%s\"\n", pthread_self(), res);
return NULL;
}
int main(int argc, char ** argv)
{
char * res;
pthread_t th1, th2;
res = str_accumulate("Result of ");
pthread_create(&th1, NULL, process, "first");
pthread_create(&th2, NULL, process, "second");
res = str_accumulate("initial thread");
printf("Thread %lx: \"%s\"\n", pthread_self(), res);
pthread_join(th1, NULL);
pthread_join(th2, NULL);
pthread_exit(NULL);
}

102
linuxthreads/Examples/ex5.c Normal file
View File

@ -0,0 +1,102 @@
/* The classic producer-consumer example, implemented with semaphores.
All integers between 0 and 9999 should be printed exactly twice,
once to the right of the arrow and once to the left. */
#include <stdio.h>
#include "pthread.h"
#include "semaphore.h"
#define BUFFER_SIZE 16
/* Circular buffer of integers. */
struct prodcons {
int buffer[BUFFER_SIZE]; /* the actual data */
int readpos, writepos; /* positions for reading and writing */
sem_t sem_read; /* number of elements available for reading */
sem_t sem_write; /* number of locations available for writing */
};
/* Initialize a buffer */
void init(struct prodcons * b)
{
sem_init(&b->sem_write, 0, BUFFER_SIZE - 1);
sem_init(&b->sem_read, 0, 0);
b->readpos = 0;
b->writepos = 0;
}
/* Store an integer in the buffer */
void put(struct prodcons * b, int data)
{
/* Wait until buffer is not full */
sem_wait(&b->sem_write);
/* Write the data and advance write pointer */
b->buffer[b->writepos] = data;
b->writepos++;
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
/* Signal that the buffer contains one more element for reading */
sem_post(&b->sem_read);
}
/* Read and remove an integer from the buffer */
int get(struct prodcons * b)
{
int data;
/* Wait until buffer is not empty */
sem_wait(&b->sem_read);
/* Read the data and advance read pointer */
data = b->buffer[b->readpos];
b->readpos++;
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
/* Signal that the buffer has now one more location for writing */
sem_post(&b->sem_write);
return data;
}
/* A test program: one thread inserts integers from 1 to 10000,
the other reads them and prints them. */
#define OVER (-1)
struct prodcons buffer;
void * producer(void * data)
{
int n;
for (n = 0; n < 10000; n++) {
printf("%d --->\n", n);
put(&buffer, n);
}
put(&buffer, OVER);
return NULL;
}
void * consumer(void * data)
{
int d;
while (1) {
d = get(&buffer);
if (d == OVER) break;
printf("---> %d\n", d);
}
return NULL;
}
int main()
{
pthread_t th_a, th_b;
void * retval;
init(&buffer);
/* Create the threads */
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
/* Wait until producer and consumer finish. */
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
return 0;
}

986
linuxthreads/FAQ.html Normal file
View File

@ -0,0 +1,986 @@
<HTML>
<HEAD>
<TITLE>LinuxThreads Frequently Asked Questions</TITLE>
</HEAD>
<BODY>
<H1 ALIGN=center>LinuxThreads Frequently Asked Questions <BR>
(with answers)</H1>
<HR><P>
<A HREF="#A">A. The big picture</A><BR>
<A HREF="#B">B. Getting more information</A><BR>
<A HREF="#C">C. Issues related to the C library</A><BR>
<A HREF="#D">D. Problems, weird behaviors, potential bugs</A><BR>
<A HREF="#E">E. Missing functions, wrong types, etc</A><BR>
<A HREF="#F">F. C++ issues</A><BR>
<A HREF="#G">G. Debugging LinuxThreads programs</A><BR>
<A HREF="#H">H. Compiling multithreaded code; errno madness</A><BR>
<A HREF="#I">I. X-Windows and other libraries</A><BR>
<A HREF="#J">J. Signals and threads</A><BR>
<A HREF="#K">K. Internals of LinuxThreads</A><P>
<HR>
<P>
<H2><A NAME="A">A. The big picture</A></H2>
<H4><A NAME="A.1">A.1: What is LinuxThreads?</A></H4>
LinuxThreads is a Linux library for multi-threaded programming.
It implements the Posix 1003.1c API (Application Programming
Interface) for threads. It runs on any Linux system with kernel 2.0.0
or more recent, and a suitable C library (see section <A HREF="B">B</A>).
<P>
<H4><A NAME="A.2">A.2: What are threads?</A></H4>
A thread is a sequential flow of control through a program.
Multi-threaded programming is, thus, a form of parallel programming
where several threads of control are executing concurrently in the
program. All threads execute in the same memory space, and can
therefore work concurrently on shared data.<P>
Multi-threaded programming differs from Unix-style multi-processing in
that all threads share the same memory space (and a few other system
resources, such as file descriptors), instead of running in their own
memory space as is the case with Unix processes.<P>
Threads are useful for two reasons. First, they allow a program to
exploit multi-processor machines: the threads can run in parallel on
several processors, allowing a single program to divide its work
between several processors, thus running faster than a single-threaded
program, which runs on only one processor at a time. Second, some
programs are best expressed as several threads of control that
communicate together, rather than as one big monolithic sequential
program. Examples include server programs, overlapping asynchronous
I/O, and graphical user interfaces.<P>
<H4><A NAME="A.3">A.3: What is POSIX 1003.1c?</A></H4>
It's an API for multi-threaded programming standardized by IEEE as
part of the POSIX standards. Most Unix vendors have endorsed the
POSIX 1003.1c standard. Implementations of the 1003.1c API are
already available under Sun Solaris 2.5, Digital Unix 4.0,
Silicon Graphics IRIX 6, and should soon be available from other
vendors such as IBM and HP. More generally, the 1003.1c API is
replacing relatively quickly the proprietary threads library that were
developed previously under Unix, such as Mach cthreads, Solaris
threads, and IRIX sprocs. Thus, multithreaded programs using the
1003.1c API are likely to run unchanged on a wide variety of Unix
platforms.<P>
<H4><A NAME="A.4">A.4: What is the status of LinuxThreads?</A></H4>
In short, it's not completely finished (hence the version numbers in
0.<I>x</I>), but what is done is pretty mature.
LinuxThreads implements almost all of Posix 1003.1c, as well as a few
extensions. The only part of LinuxThreads that does not conform yet
to Posix is signal handling (see section <A HREF="#J">J</A>). Apart
from the signal stuff, all the Posix 1003.1c base functionality is
provided and conforms to the standard (to the best of my knowledge).
The signal stuff is hard to get right, at least without special kernel
support, and while I'm definitely looking at ways to implement the
Posix behavior for signals, this might take a long time before it's
completed.<P>
<H4><A NAME="A.5">A.5: How stable is LinuxThreads?</A></H4>
The basic functionality (thread creation and termination, mutexes,
conditions, semaphores) is very stable. Several industrial-strength
programs, such as the AOL multithreaded Web server, use LinuxThreads
and seem quite happy about it. There are some rough edges in
the LinuxThreads / C library interface, at least with libc 5, but most
of these rough edges are fixed in glibc 2, which should soon become
the standard C library for Linux distributions (see section <A
HREF="#C">C</A>). <P>
<HR>
<P>
<H2><A NAME="B">B. Getting more information</A></H2>
<H4><A NAME="B.1">B.1: What are good books and other sources of
information on POSIX threads?</A></H4>
The FAQ for comp.programming.threads lists several books:
<A HREF="http://www.serpentine.com/~bos/threads-faq/">http://www.serpentine.com/~bos/threads-faq/</A>.<P>
There are also some online tutorials. Follow the links from the
LinuxThreads web page:
<A HREF="http://pauillac.inria.fr/~xleroy/linuxthreads">http://pauillac.inria.fr/~xleroy/linuxthreads</A>.<P>
<H4><A NAME="B.2">B.2: I'd like to be informed of future developments on
LinuxThreads. Is there a mailing list for this purpose?</A></H4>
I post LinuxThreads-related announcements on the newsgroup
<A HREF="news:comp.os.linux.announce">comp.os.linux.announce</A>,
and also on the mailing list
<code>linux-threads@magenet.com</code>.
You can subscribe to the latter by writing
<A HREF="mailto:majordomo@magenet.com">majordomo@magenet.com</A>.<P>
<H4><A NAME="B.3">B.3: What are good places for discussing
LinuxThreads?</A></H4>
For questions about programming with POSIX threads in general, use
the newsgroup
<A HREF="news:comp.programming.threads">comp.programming.threads</A>.
Be sure you read the
<A HREF="http://www.serpentine.com/~bos/threads-faq/">FAQ</A>
for this group before you post.<P>
For Linux-specific questions, use
<A
HREF="news:comp.os.linux.development.apps">comp.os.linux.development.apps</A>
and <A
HREF="news:comp.os.linux.development.kernel">comp.os.linux.development.kernel</A>.
The latter is especially appropriate for questions relative to the
interface between the kernel and LinuxThreads.<P>
Very specific LinuxThreads questions, and in particular everything
that looks like a potential bug in LinuxThreads, should be mailed
directly to me (<code>Xavier.Leroy@inria.fr</code>). Before mailing
me, make sure that your question is not answered in this FAQ.<P>
<H4><A NAME="B.4">B.4: I'd like to read the POSIX 1003.1c standard. Is
it available online?</A></H4>
Unfortunately, no. POSIX standards are copyrighted by IEEE, and
IEEE does not distribute them freely. You can buy paper copies from
IEEE, but the price is fairly high ($120 or so). If you disagree with
this policy and you're an IEEE member, be sure to let them know.<P>
On the other hand, you probably don't want to read the standard. It's
very hard to read, written in standard-ese, and targeted to
implementors who already know threads inside-out. A good book on
POSIX threads provides the same information in a much more readable form.
I can personally recommend Dave Butenhof's book, <CITE>Programming
with POSIX threads</CITE> (Addison-Wesley). Butenhof was part of the
POSIX committee and also designed the Digital Unix implementations of
POSIX threads, and it shows.<P>
Another good source of information is the X/Open Group Single Unix
specification which is available both
<A HREF="http://www.rdg.opengroup.org/onlinepubs/7908799/index.html">on-line</A>
and as a
<A HREF="http://www.UNIX-systems.org/gosolo2/">book and CD/ROM</A>.
That specification includes pretty much all the POSIX standards,
including 1003.1c, with some extensions and clarifications.<P>
<HR>
<P>
<H2><A NAME="C">C. Issues related to the C library</A></H2>
<H4><A NAME="C.1">C.1: Which version of the C library should I use
with LinuxThreads?</A></H4>
Most current Linux distributions come with libc version 5, maintained
by H.J.Lu. For LinuxThreads to work properly, you must use either
libc 5.2.18 or libc 5.4.12 or later. Avoid 5.3.12 and 5.4.7: these
have problems with the per-thread errno variable.
<P>
Unfortunately, many popular Linux distributions (e.g. RedHat 4.2) come
with libc 5.3.12 preinstalled -- the one that does not work with
LinuxThreads. Fortunately, you can often find pre-packaged binaries
of more recent versions of libc for these distributions. In the case
of RedHat 4, there is a RPM package for libc-5.4 in the "contrib"
area of RedHat FTP sites.
<P>
<H4><A NAME="C.2">C.2: What about glibc 2, a.k.a. libc 6?</A></H4>
It's the next generation libc for Linux, developed by Ulrich
Drepper and other FSF collaborators. glibc 2 offers much better
support for threads than libc 5. Indeed, thread support was
planned from the very early stages of glibc 2, while it's a
last-minute addition to libc 5. glibc 2 actually comes with a
specially adapted version of LinuxThreads, which you can drop in the
glibc 2 sources as an add-on package.
<H4><A NAME="C.3">C.3: So, should I switch to glibc 2, or stay with a
recent libc 5?</A></H4>
Depends how you plan to do it. Switching an already installed
system from libc 5 to glibc 2 is not completely straightforward.
See the <A HREF="http://sunsite.unc.edu/LDP/HOWTO/Glibc2-HOWTO.html">Glibc2
HOWTO</A> for more information.
But (re-)installing a Linux distribution based on glibc 2 is easy.
One such distribution available now is RedHat 5.0. Debian and other
Linux distributors will also provide glibc 2-based distributions in the
near future.
<P>
<H4><A NAME="C.4">C.4: Where can I find glibc 2 and the version of
LinuxThreads that goes with it?</A></H4>
On <code>prep.ai.mit.edu</code> and its many, many mirrors around the world.
See <A
HREF="http://www.gnu.org/order/ftp.html">http://www.gnu.org/order/ftp.html</A>
for a list of mirrors.<P>
<HR>
<P>
<H2><A NAME="D">D. Problems, weird behaviors, potential bugs</A></H2>
<H4><A NAME="D.1">D.1: When I compile LinuxThreads, I run into problems in
file <code>libc_r/dirent.c</code></A></H4>
You probably mean:
<PRE>
libc_r/dirent.c:94: structure has no member named `dd_lock'
</PRE>
I haven't actually seen this problem, but several users reported it.
My understanding is that something is wrong in the include files of
your Linux installation (<code>/usr/include/*</code>). Make sure
you're using a supported version of the C library. (See section <A
HREF="#B">B</A>).<P>
<H4><A NAME="D.2">D.2: When I compile LinuxThreads, I run into problems with
<CODE>/usr/include/sched.h</CODE>: there are several occurrences of
<CODE>_p</CODE> that the C compiler does not understand</A></H4>
Yes, <CODE>/usr/include/sched.h</CODE> that comes with libc 5.3.12 is broken.
Replace it with the <code>sched.h</code> file contained in the
LinuxThreads distribution. But really you should not be using libc
5.3.12 with LinuxThreads! (See question <A HREF="#C.1">C.1</A>.)<P>
<H4><A NAME="D.3">D.3: My program does <CODE>fdopen()</CODE> on a file
descriptor opened on a pipe. When I link it with LinuxThreads,
<CODE>fdopen()</CODE> always returns NULL!</A></H4>
You're using one of the buggy versions of libc (5.3.12, 5.4.7., etc).
See question <A HREF="#C.1">C.1</A> above.<P>
<H4><A NAME="D.4">D.4: My program crashes the first time it calls
<CODE>pthread_create()</CODE> !</A></H4>
You wouldn't be using glibc 2.0, by any chance? That's a known bug
with glibc 2.0. Please upgrade to 2.0.1 or later.<P>
<H4><A NAME="D.5">D.5: When I'm running a program that creates N
threads, <code>top</code> or <code>ps</code>
display N+2 processes that are running my program. What do all these
processes correspond to?</A></H4>
Due to the general "one process per thread" model, there's one process
for the initial thread and N processes for the threads it created
using <CODE>pthread_create</CODE>. That leaves one process
unaccounted for. That extra process corresponds to the "thread
manager" thread, a thread created internally by LinuxThreads to handle
thread creation and thread termination. This extra thread is asleep
most of the time.
<H4><A NAME="D.6">D.6: Scheduling seems to be very unfair when there
is strong contention on a mutex: instead of giving the mutex to each
thread in turn, it seems that it's almost always the same thread that
gets the mutex. Isn't this completely broken behavior?</A></H4>
What happens is the following: when a thread unlocks a mutex, all
other threads that were waiting on the mutex are sent a signal which
makes them runnable. However, the kernel scheduler may or may not
restart them immediately. If the thread that unlocked the mutex
tries to lock it again immediately afterwards, it is likely that it
will succeed, because the threads haven't yet restarted. This results
in an apparently very unfair behavior, when the same thread repeatedly
locks and unlocks the mutex, while other threads can't lock the mutex.<P>
This is perfectly acceptable behavior with respect to the POSIX
standard: for the default scheduling policy, POSIX makes no guarantees
of fairness, such as "the thread waiting for the mutex for the longest
time always acquires it first". This allows implementations of
mutexes to remain simple and efficient. Properly written
multithreaded code avoids that kind of heavy contention on mutexes,
and does not run into fairness problems. If you need scheduling
guarantees, you should consider using the real-time scheduling
policies <code>SCHED_RR</code> and <code>SCHED_FIFO</code>, which have
precisely defined scheduling behaviors. <P>
<H4><A NAME="D.7">D.7: I have a simple test program with two threads
that do nothing but <CODE>printf()</CODE> in tight loops, and from the
printout it seems that only one thread is running, the other doesn't
print anything!</A></H4>
If you wait long enough, you should see the second thread kick in.
But still, you're right, one thread prevents the other one from
running for long periods of time. The reason is explained in
question <A HREF="#D.6">D.6</A> above: <CODE>printf()</CODE> performs
locking on <CODE>stdout</CODE>, and thus your two threads contend very
heavily for the mutex associated with <CODE>stdout</CODE>. But if you
do some real work between two calls to <CODE>printf()</CODE>, you'll
see that scheduling becomes much smoother. <P>
<H4><A NAME="D.8">D.8: I've looked at <code>&lt;pthread.h&gt;</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>&lt;pthread.h&gt;</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>&lt;stdio.h&gt;</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>&lt;errno.h&gt;</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>&lt;errno.h&gt;</code>:
<PRE>
#define errno (*(__errno_location()))
</PRE>
which causes each reference to errno to call the
<CODE>__errno_location()</CODE> function for obtaining the location
where error codes are stored. libc provides a default definition of
<CODE>__errno_location()</CODE> that always returns
<code>&errno</code> (the address of the global errno variable). Thus,
for programs not linked with LinuxThreads, defining
<CODE>_REENTRANT</CODE> makes no difference w.r.t. errno processing.
But LinuxThreads redefines <CODE>__errno_location()</CODE> to return a
location in the thread descriptor reserved for holding the current
value of errno for the calling thread. Thus, each thread operates on
a different errno location.
</UL>
<P>
<H4><A NAME="H.2">H.2: Why is it so important that each thread has its
own errno variable? </A></H4>
If all threads were to store error codes in the same, global errno
variable, then the value of errno after a system call or library
function returns would be unpredictable: between the time a system
call stores its error code in the global errno and your code inspects
errno to see which error occurred, another thread might have stored
another error code in the same errno location. <P>
<H4><A NAME="H.3">H.3: What happens if I link LinuxThreads with code
not compiled with <CODE>-D_REENTRANT</CODE>?</A></H4>
Lots of trouble. If the code uses <CODE>getc()</CODE> or
<CODE>putc()</CODE>, it will perform I/O without proper interlocking
of the stdio buffers; this can cause lost output, duplicate output, or
just crash other stdio functions. If the code consults errno, it will
get back the wrong error code. The following code fragment is a
typical example:
<PRE>
do {
r = read(fd, buf, n);
if (r == -1) {
if (errno == EINTR) /* an error we can handle */
continue;
else { /* other errors are fatal */
perror("read failed");
exit(100);
}
}
} while (...);
</PRE>
Assume this code is not compiled with <CODE>-D_REENTRANT</CODE>, and
linked with LinuxThreads. At run-time, <CODE>read()</CODE> is
interrupted. Since the C library was compiled with
<CODE>-D_REENTRANT</CODE>, <CODE>read()</CODE> stores its error code
in the location pointed to by <CODE>__errno_location()</CODE>, which
is the thread-local errno variable. Then, the code above sees that
<CODE>read()</CODE> returns -1 and looks up errno. Since
<CODE>_REENTRANT</CODE> is not defined, the reference to errno
accesses the global errno variable, which is most likely 0. Hence the
code concludes that it cannot handle the error and stops.<P>
<H4><A NAME="H.4">H.4: With LinuxThreads, I can no longer use the signals
<code>SIGUSR1</code> and <code>SIGUSR2</code> in my programs! Why? </A></H4>
LinuxThreads needs two signals for its internal operation.
One is used to suspend and restart threads blocked on mutex, condition
or semaphore operations. The other is used for thread cancellation.
Since the only two signals not reserved for the Linux kernel are
<code>SIGUSR1</code> and <code>SIGUSR2</code>, LinuxThreads has no
other choice than using them. I know this is unfortunate, and hope
this problem will be addressed in future Linux kernels, either by
freeing some of the regular signals (unlikely), or by providing more
than 32 signals (as per the POSIX 1003.1b realtime extensions).<P>
In the meantime, you can try to use kernel-reserved signals either in
your program or in LinuxThreads. For instance,
<code>SIGSTKFLT</code> and <code>SIGUNUSED</code> appear to be
unused in the current Linux kernels for the Intel x86 architecture.
To use these in LinuxThreads, the only file you need to change
is <code>internals.h</code>, more specifically the two lines:
<PRE>
#define PTHREAD_SIG_RESTART SIGUSR1
#define PTHREAD_SIG_CANCEL SIGUSR2
</PRE>
Replace them by e.g.
<PRE>
#define PTHREAD_SIG_RESTART SIGSTKFLT
#define PTHREAD_SIG_CANCEL SIGUNUSED
</PRE>
Warning: you're doing this at your own risks.<P>
<H4><A NAME="H.5">H.5: Is the stack of one thread visible from the
other threads? Can I pass a pointer into my stack to other threads?
</A></H4>
Yes, you can -- if you're very careful. The stacks are indeed visible
from all threads in the system. Some non-POSIX thread libraries seem
to map the stacks for all threads at the same virtual addresses and
change the memory mapping when they switch from one thread to
another. But this is not the case for LinuxThreads, as it would make
context switching between threads more expensive, and at any rate
might not conform to the POSIX standard.<P>
So, you can take the address of an "auto" variable and pass it to
other threads via shared data structures. However, you need to make
absolutely sure that the function doing this will not return as long
as other threads need to access this address. It's the usual mistake
of returning the address of an "auto" variable, only made much worse
because of concurrency. It's much, much safer to systematically
heap-allocate all shared data structures. <P>
<HR>
<P>
<H2><A NAME="I">I. X-Windows and other libraries</A></H2>
<H4><A NAME="I.1">I.1: My program uses both Xlib and LinuxThreads.
It stops very early with an "Xlib: unknown 0 error" message. What
does this mean? </A></H4>
That's a prime example of the errno problem described in question <A
HREF="#H.2">H.2</A>. The binaries for Xlib you're using have not been
compiled with <CODE>-D_REENTRANT</CODE>. It happens Xlib contains a
piece of code very much like the one in question <A
HREF="#H.2">H.2</A>. So, your Xlib fetches the error code from the
wrong errno location and concludes that an error it cannot handle
occurred.<P>
<H4><A NAME="I.2">I.2: So, what can I do to build a multithreaded X
Windows client? </A></H4>
The best solution is to recompile the X libraries with multithreading
options set. They contain optional support for multithreading; it's
just that all binary distributions for Linux were built without this
support. See the file <code>README.Xfree3.3</code> in the LinuxThreads
distribution for patches and info on how to compile thread-safe X
libraries from the Xfree3.3 distribution. The Xfree3.3 sources are
readily available in most Linux distributions, e.g. as a source RPM
for RedHat. Be warned, however, that X Windows is a huge system, and
recompiling even just the libraries takes a lot of time and disk
space.<P>
Another, less involving solution is to call X functions only from the
main thread of your program. Even if all threads have their own errno
location, the main thread uses the global errno variable for its errno
location. Thus, code not compiled with <code>-D_REENTRANT</code>
still "sees" the right error values if it executes in the main thread
only. <P>
<H4><A NAME="I.2">This is a lot of work. Don't you have precompiled
thread-safe X libraries that you could distribute?</A></H4>
No, I don't. Sorry. But you could approach the maintainers of
your Linux distribution to see if they would be willing to provide
thread-safe X libraries.<P>
<H4><A NAME="I.3">I.3: Can I use library FOO in a multithreaded
program?</A></H4>
Most libraries cannot be used "as is" in a multithreaded program.
For one thing, they are not necessarily thread-safe: calling
simultaneously two functions of the library from two threads might not
work, due to internal use of global variables and the like. Second,
the libraries must have been compiled with <CODE>-D_REENTRANT</CODE> to avoid
the errno problems explained in question <A HREF="#H.2">H.2</A>.
<P>
<H4><A NAME="I.4">I.4: What if I make sure that only one thread calls
functions in these libraries?</A></H4>
This avoids problems with the library not being thread-safe. But
you're still vulnerable to errno problems. At the very least, a
recompile of the library with <CODE>-D_REENTRANT</CODE> is needed.
<P>
<H4><A NAME="I.5">I.5: What if I make sure that only the main thread
calls functions in these libraries?</A></H4>
That might actually work. As explained in question <A HREF="#I.1">I.1</A>,
the main thread uses the global errno variable, and can therefore
execute code not compiled with <CODE>-D_REENTRANT</CODE>.<P>
<H4><A NAME="I.6">I.6: SVGAlib doesn't work with LinuxThreads. Why?
</A></H4>
Because both LinuxThreads and SVGAlib use the signals
<code>SIGUSR1</code> and <code>SIGUSR2</code>. One of the two should
be recompiled to use different signals. See question <A
HREF="#H.4">H.4</A>.
<P>
<HR>
<P>
<H2><A NAME="J">J. Signals and threads</A></H2>
<H4><A NAME="J.1">J.1: When it comes to signals, what is shared
between threads and what isn't?</A></H4>
Signal handlers are shared between all threads: when a thread calls
<CODE>sigaction()</CODE>, it sets how the signal is handled not only
for itself, but for all other threads in the program as well.<P>
On the other hand, signal masks are per-thread: each thread chooses
which signals it blocks independently of others. At thread creation
time, the newly created thread inherits the signal mask of the thread
calling <CODE>pthread_create()</CODE>. But afterwards, the new thread
can modify its signal mask independently of its creator thread.<P>
<H4><A NAME="J.2">J.2: When I send a <CODE>SIGKILL</CODE> to a
particular thread using <CODE>pthread_kill</CODE>, all my threads are
killed!</A></H4>
That's how it should be. The POSIX standard mandates that all threads
should terminate when the process (i.e. the collection of all threads
running the program) receives a signal whose effect is to
terminate the process (such as <CODE>SIGKILL</CODE> or <CODE>SIGINT</CODE>
when no handler is installed on that signal). This behavior makes a
lot of sense: when you type "ctrl-C" at the keyboard, or when a thread
crashes on a division by zero or a segmentation fault, you really want
all threads to stop immediately, not just the one that caused the
segmentation violation or that got the <CODE>SIGINT</CODE> signal.
(This assumes default behavior for those signals; see question
<A HREF="#J.3">J.3</A> if you install handlers for those signals.)<P>
If you're trying to terminate a thread without bringing the whole
process down, use <code>pthread_cancel()</code>.<P>
<H4><A NAME="J.3">J.3: I've installed a handler on a signal. Which
thread executes the handler when the signal is received?</A></H4>
If the signal is generated by a thread during its execution (e.g. a
thread executes a division by zero and thus generates a
<CODE>SIGFPE</CODE> signal), then the handler is executed by that
thread. This also applies to signals generated by
<CODE>raise()</CODE>.<P>
If the signal is sent to a particular thread using
<CODE>pthread_kill()</CODE>, then that thread executes the handler.<P>
If the signal is sent via <CODE>kill()</CODE> or the tty interface
(e.g. by pressing ctrl-C), then the POSIX specs say that the handler
is executed by any thread in the process that does not currently block
the signal. In other terms, POSIX considers that the signal is sent
to the process (the collection of all threads) as a whole, and any
thread that is not blocking this signal can then handle it.<P>
The latter case is where LinuxThreads departs from the POSIX specs.
In LinuxThreads, there is no real notion of ``the process as a whole'':
in the kernel, each thread is really a distinct process with a
distinct PID, and signals sent to the PID of a thread can only be
handled by that thread. As long as no thread is blocking the signal,
the behavior conforms to the standard: one (unspecified) thread of the
program handles the signal. But if the thread to which PID the signal
is sent blocks the signal, and some other thread does not block the
signal, then LinuxThreads will simply queue in
that thread and execute the handler only when that thread unblocks
the signal, instead of executing the handler immediately in the other
thread that does not block the signal.<P>
This is to be viewed as a LinuxThreads bug, but I currently don't see
any way to implement the POSIX behavior without kernel support.<P>
<H4><A NAME="J.3">J.3: How shall I go about mixing signals and threads
in my program? </A></H4>
The less you mix them, the better. Notice that all
<CODE>pthread_*</CODE> functions are not async-signal safe, meaning
that you should not call them from signal handlers. This
recommendation is not to be taken lightly: your program can deadlock
if you call a <CODE>pthread_*</CODE> function from a signal handler!
<P>
The only sensible things you can do from a signal handler is set a
global flag, or call <CODE>sem_post</CODE> on a semaphore, to record
the delivery of the signal. The remainder of the program can then
either poll the global flag, or use <CODE>sem_wait()</CODE> and
<CODE>sem_trywait()</CODE> on the semaphore.<P>
Another option is to do nothing in the signal handler, and dedicate
one thread (preferably the initial thread) to wait synchronously for
signals, using <CODE>sigwait()</CODE>, and send messages to the other
threads accordingly.
<H4><A NAME="J.4">J.4: When one thread is blocked in
<CODE>sigwait()</CODE>, other threads no longer receive the signals
<CODE>sigwait()</CODE> is waiting for! What happens? </A></H4>
It's an unfortunate consequence of how LinuxThreads implements
<CODE>sigwait()</CODE>. Basically, it installs signal handlers on all
signals waited for, in order to record which signal was received.
Since signal handlers are shared with the other threads, this
temporarily deactivates any signal handlers you might have previously
installed on these signals.<P>
Though surprising, this behavior actually seems to conform to the
POSIX standard. According to POSIX, <CODE>sigwait()</CODE> is
guaranteed to work as expected only if all other threads in the
program block the signals waited for (otherwise, the signals could be
delivered to other threads than the one doing <CODE>sigwait()</CODE>,
which would make <CODE>sigwait()</CODE> useless). In this particular
case, the problem described in this question does not appear.<P>
One day, <CODE>sigwait()</CODE> will be implemented in the kernel,
along with others POSIX 1003.1b extensions, and <CODE>sigwait()</CODE>
will have a more natural behavior (as well as better performances).<P>
<HR>
<P>
<H2><A NAME="K">K. Internals of LinuxThreads</A></H2>
<H4><A NAME="K.1">K.1: What is the implementation model for
LinuxThreads?</A></H4>
LinuxThreads follows the so-called "one-to-one" model: each thread is
actually a separate process in the kernel. The kernel scheduler takes
care of scheduling the threads, just like it schedules regular
processes. The threads are created with the Linux
<code>clone()</code> system call, which is a generalization of
<code>fork()</code> allowing the new process to share the memory
space, file descriptors, and signal handlers of the parent.<P>
Advantages of the "one-to-one" model include:
<UL>
<LI> minimal overhead on CPU-intensive multiprocessing (with
about one thread per processor);
<LI> minimal overhead on I/O operations;
<LI> a simple and robust implementation (the kernel scheduler does
most of the hard work for us).
</UL>
The main disadvantage is more expensive context switches on mutex and
condition operations, which must go through the kernel. This is
mitigated by the fact that context switches in the Linux kernel are
pretty efficient.<P>
<H4><A NAME="K.2">K.2: Have you considered other implementation
models?</A></H4>
There are basically two other models. The "many-to-one" model
relies on a user-level scheduler that context-switches between the
threads entirely in user code; viewed from the kernel, there is only
one process running. This model is completely out of the question for
me, since it does not take advantage of multiprocessors, and require
unholy magic to handle blocking I/O operations properly. There are
several user-level thread libraries available for Linux, but I found
all of them deficient in functionality, performance, and/or robustness.
<P>
The "many-to-many" model combines both kernel-level and user-level
scheduling: several kernel-level threads run concurrently, each
executing a user-level scheduler that selects between user threads.
Most commercial Unix systems (Solaris, Digital Unix, IRIX) implement
POSIX threads this way. This model combines the advantages of both
the "many-to-one" and the "one-to-one" model, and is attractive
because it avoids the worst-case behaviors of both models --
especially on kernels where context switches are expensive, such as
Digital Unix. Unfortunately, it is pretty complex to implement, and
requires kernel support which Linux does not provide. Linus Torvalds
and other Linux kernel developers have always been pushing the
"one-to-one" model in the name of overall simplicity, and are doing a
pretty good job of making kernel-level context switches between
threads efficient. LinuxThreads is just following the general
direction they set.<P>
<H4><A NAME="K.3">K.3: I looked at the LinuxThreads sources, and I saw
quite a lot of spinlocks and busy-waiting loops to acquire these
spinlocks. Isn't this a big waste of CPU time?</A></H4>
Look more carefully. Spinlocks are used internally to protect
LinuxThreads's data structures, but these locks are held for very
short periods of time: 10 instructions or so. The probability that a
thread has to loop busy-waiting on a taken spinlock for more than,
say, 100 cycles is very, very low. When a thread needs to wait on a
mutex, condition, or semaphore, it actually puts itself on a waiting
queue, then suspends on a signal, consuming no CPU time at all. The
thread will later be restarted by sending it a signal when the state
of the mutex, condition, or semaphore changes.<P>
<HR>
<ADDRESS>Xavier.Leroy@inria.fr</ADDRESS>
</BODY>
</HTML>

501
linuxthreads/LICENSE Normal file
View File

@ -0,0 +1,501 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
**********************************
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
59 Temple Place -- Suite 330, Boston, MA 02111-1307, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
========
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it in
new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License, which was designed for utility
programs. This license, the GNU Library General Public License,
applies to certain designated libraries. This license is quite
different from the ordinary one; be sure to read it in full, and don't
assume that anything in it is the same as in the ordinary license.
The reason we have a separate public license for some libraries is
that they blur the distinction we usually make between modifying or
adding to a program and simply using it. Linking a program with a
library, without changing the library, is in some sense simply using
the library, and is analogous to running a utility program or
application program. However, in a textual and legal sense, the linked
executable is a combined work, a derivative of the original library,
and the ordinary General Public License treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended
to permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to
achieve this as regards changes in header files, but we have achieved
it as regards changes in the actual functions of the Library.) The
hope is that this will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other
authorized party saying it may be distributed under the terms of
this Library General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or
translated straightforwardly into another language. (Hereinafter,
translation is included without limitation in the term
"modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code
means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the library.
Activities other than copying, distribution and modification are
not covered by this License; they are outside its scope. The act
of running a program using the Library is not restricted, and
output from such a program is covered only if its contents
constitute a work based on the Library (independent of the use of
the Library in a tool for writing it). Whether that is true
depends on what the Library does and what the program that uses
the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided
that you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep
intact all the notices that refer to this License and to the
absence of any warranty; and distribute a copy of this License
along with the Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange
for a fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a. The modified work must itself be a software library.
b. You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c. You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d. If a facility in the modified Library refers to a function or
a table of data to be supplied by an application program that
uses the facility, other than as an argument passed when the
facility is invoked, then you must make a good faith effort
to ensure that, in the event an application does not supply
such function or table, the facility still operates, and
performs whatever part of its purpose remains meaningful.
(For example, a function in a library to compute square roots
has a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function
must be optional: if the application does not supply it, the
square root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the
Library, and can be reasonably considered independent and separate
works in themselves, then this License, and its terms, do not
apply to those sections when you distribute them as separate
works. But when you distribute the same sections as part of a
whole which is a work based on the Library, the distribution of
the whole must be on the terms of this License, whose permissions
for other licensees extend to the entire whole, and thus to each
and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or
contest your rights to work written entirely by you; rather, the
intent is to exercise the right to control the distribution of
derivative or collective works based on the Library.
In addition, mere aggregation of another work not based on the
Library with the Library (or with a work based on the Library) on
a volume of a storage or distribution medium does not bring the
other work under the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.
To do this, you must alter all the notices that refer to this
License, so that they refer to the ordinary GNU General Public
License, version 2, instead of to this License. (If a newer
version than version 2 of the ordinary GNU General Public License
has appeared, then you can specify that version instead if you
wish.) Do not make any other change in these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to
all subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable
form under the terms of Sections 1 and 2 above provided that you
accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software
interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy
the source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being
compiled or linked with it, is called a "work that uses the
Library". Such a work, in isolation, is not a derivative work of
the Library, and therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because
it contains portions of the Library), rather than a "work that
uses the library". The executable is therefore covered by this
License. Section 6 states terms for distribution of such
executables.
When a "work that uses the Library" uses material from a header
file that is part of the Library, the object code for the work may
be a derivative work of the Library even though the source code is
not. Whether this is true is especially significant if the work
can be linked without the Library, or if the work is itself a
library. The threshold for this to be true is not precisely
defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a
derivative work. (Executables containing this object code plus
portions of the Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section
6. Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered
by this License. You must supply a copy of this License. If the
work during execution displays copyright notices, you must include
the copyright notice for the Library among them, as well as a
reference directing the user to the copy of this License. Also,
you must do one of these things:
a. Accompany the work with the complete corresponding
machine-readable source code for the Library including
whatever changes were used in the work (which must be
distributed under Sections 1 and 2 above); and, if the work
is an executable linked with the Library, with the complete
machine-readable "work that uses the Library", as object code
and/or source code, so that the user can modify the Library
and then relink to produce a modified executable containing
the modified Library. (It is understood that the user who
changes the contents of definitions files in the Library will
not necessarily be able to recompile the application to use
the modified definitions.)
b. Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
c. If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the
above specified materials from the same place.
d. Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special
exception, the source code distributed need not include anything
that is normally distributed (in either source or binary form)
with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that
component itself accompanies the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you
cannot use both them and the Library together in an executable
that you distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other
library facilities not covered by this License, and distribute
such a combined library, provided that the separate distribution
of the work based on the Library and of the other library
facilities is otherwise permitted, and provided that you do these
two things:
a. Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b. Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same
work.
8. You may not copy, modify, sublicense, link with, or distribute the
Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate
your rights under this License. However, parties who have
received copies, or rights, from you under this License will not
have their licenses terminated so long as such parties remain in
full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify
or distribute the Library or its derivative works. These actions
are prohibited by law if you do not accept this License.
Therefore, by modifying or distributing the Library (or any work
based on the Library), you indicate your acceptance of this
License to do so, and all its terms and conditions for copying,
distributing or modifying the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the
Library subject to these terms and conditions. You may not impose
any further restrictions on the recipients' exercise of the rights
granted herein. You are not responsible for enforcing compliance
by third parties to this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent
issues), conditions are imposed on you (whether by court order,
agreement or otherwise) that contradict the conditions of this
License, they do not excuse you from the conditions of this
License. If you cannot distribute so as to satisfy simultaneously
your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the
Library at all. For example, if a patent license would not permit
royalty-free redistribution of the Library by all those who
receive copies directly or indirectly through you, then the only
way you could satisfy both it and this License would be to refrain
entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable
under any particular circumstance, the balance of the section is
intended to apply, and the section as a whole is intended to apply
in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of
any such claims; this section has the sole purpose of protecting
the integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is
willing to distribute software through any other system and a
licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed
to be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces,
the original copyright holder who places the Library under this
License may add an explicit geographical distribution limitation
excluding those countries, so that distribution is permitted only
in or among countries not thus excluded. In such case, this
License incorporates the limitation as if written in the body of
this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library specifies a version number of this License which applies
to it and "any later version", you have the option of following
the terms and conditions either of that version or of any later
version published by the Free Software Foundation. If the Library
does not specify a license version number, you may choose any
version ever published by the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free
status of all derivatives of our free software and of promoting
the sharing and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE
LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU
OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY
OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
==============================================
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of
the ordinary General Public License).
To apply these terms, attach the following notices to the library.
It is safest to attach them to the start of each source file to most
effectively convey the exclusion of warranty; and each file should have
at least the "copyright" line and a pointer to where the full notice is
found.
ONE LINE TO GIVE THE LIBRARY'S NAME AND AN IDEA OF WHAT IT DOES.
Copyright (C) YEAR NAME OF AUTHOR
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
Also add information on how to contact you by electronic and paper
mail.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the library,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the library
`Frob' (a library for tweaking knobs) written by James Random Hacker.
SIGNATURE OF TY COON, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

44
linuxthreads/Makefile Normal file
View File

@ -0,0 +1,44 @@
# Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
# You should have received a copy of the GNU Library General Public
# License along with the GNU C Library; see the file COPYING.LIB. If not,
# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
# Sub-makefile for linuxthreads portion of the library.
#
subdir := linuxthreads
linuxthreads-version=0.7
headers := pthread.h semaphore.h bits/semaphore.h
distribute := internals.h queue.h restart.h spinlock.h
routines := weaks
extra-libs := libpthread
extra-libs-others := $(extra-libs)
libpthread-routines := attr cancel condvar join manager mutex ptfork \
pthread signals specific errno lockfile \
semaphore wrapsyscall rwlock
libpthread-map := libpthread.map
include ../Rules
# Depend on libc.so so a DT_NEEDED is generated in the shared objects.
# This ensures they will load libc.so for needed symbols if loaded by
# a statically-linked program that hasn't already loaded it.
$(objpfx)libpthread.so: $(common-objpfx)libc.so

163
linuxthreads/README Normal file
View File

@ -0,0 +1,163 @@
Linuxthreads - POSIX 1003.1c kernel threads for Linux
Copyright 1996, 1997 Xavier Leroy (Xavier.Leroy@inria.fr)
DESCRIPTION:
This is release 0.7 (late beta) of LinuxThreads, a BiCapitalized
implementation of the Posix 1003.1c "pthread" interface for Linux.
LinuxThreads provides kernel-level threads: each thread is a separate
Unix process, sharing its address space with the other threads through
the new system call clone(). Scheduling between threads is handled by
the kernel scheduler, just like scheduling between Unix processes.
REQUIREMENTS:
- Linux version 2.0 and up (requires the new clone() system call
and the new realtime scheduler).
- For Intel platforms: libc 5.2.18 or later is required.
5.2.18 or 5.4.12 or later are recommended;
5.3.12 and 5.4.7 have problems (see the FAQ.html file for more info).
- Also supports glibc 2 (a.k.a. libc 6), which actually comes with
a specially-adapted version of this library.
- Currently supports Intel, Alpha, Sparc, Motorola 68k, ARM and MIPS
platforms.
- Multiprocessors are supported.
INSTALLATION:
- Edit the Makefile, set the variables in the "Configuration" section.
- Do "make".
- Do "make install".
USING LINUXTHREADS:
gcc -D_REENTRANT ... -lpthread
A complete set of manual pages is included. Also see the subdirectory
Examples/ for some sample programs.
STATUS:
- All functions in the Posix 1003.1c base interface implemented.
Also supports priority scheduling.
- For users of libc 5 (H.J.Lu's libc), a number of C library functions
are reimplemented or wrapped to make them thread-safe, including:
* malloc functions
* stdio functions (define _REENTRANT before including <stdio.h>)
* per-thread errno variable (define _REENTRANT before including <errno.h>)
* directory reading functions (opendir(), etc)
* sleep()
* gmtime(), localtime()
New library functions provided:
* flockfile(), funlockfile(), ftrylockfile()
* reentrant versions of network database functions (gethostbyname_r(), etc)
and password functions (getpwnam_r(), etc).
- libc 6 (glibc 2) provides much better thread support than libc 5,
and comes with a specially-adapted version of LinuxThreads.
For serious multithreaded programming, you should consider switching
to glibc 2. It is available from prep.ai.mit.edu:/pub/gnu and its mirrors.
WARNING:
Many existing libraries are not compatible with LinuxThreads,
either because they are not inherently thread-safe, or because they
have not been compiled with the -D_REENTRANT. For more info, see the
FAQ.html file in this directory.
A prime example of the latter is Xlib. If you link it with
LinuxThreads, you'll probably get an "unknown 0 error" very
early. This is just a consequence of the Xlib binaries using the
global variable "errno" to fetch error codes, while LinuxThreads and
the C library use the per-thread "errno" location.
See the file README.Xfree3.3 for info on how to compile the Xfree 3.3
libraries to make them compatible with LinuxThreads.
KNOWN BUGS AND LIMITATIONS:
- Threads share pretty much everything they should share according
to the standard: memory space, file descriptors, signal handlers,
current working directory, etc. One thing that they do not share
is their pid's and parent pid's. According to the standard, they
should have the same, but that's one thing we cannot achieve
in this implementation (until the CLONE_PID flag to clone() becomes
usable).
- The current implementation uses the two signals SIGUSR1 and SIGUSR2,
so user-level code cannot employ them. Ideally, there should be two
signals reserved for this library. One signal is used for restarting
threads blocked on mutexes or conditions; the other is for thread
cancellation.
- The stacks for the threads are allocated high in the memory space,
below the stack of the initial process, and spaced 2M apart.
Stacks are allocated with the "grow on demand" flag, so they don't
use much virtual space initially (4k, currently), but can grow
up to 2M if needed.
Reserving such a large address space for each thread means that,
on a 32-bit architecture, no more than about 1000 threads can
coexist (assuming a 2Gb address space for user processes),
but this is reasonable, since each thread uses up one entry in the
kernel's process table, which is usually limited to 512 processes.
Another potential problem of the "grow on demand" scheme is that
nothing prevents the user from mmap'ing something in the 2M address
window reserved for a thread stack, possibly causing later extensions of
that stack to fail. Mapping at fixed addresses should be avoided
when using this library.
- Signal handling does not fully conform to the Posix standard,
due to the fact that threads are here distinct processes that can be
sent signals individually, so there's no notion of sending a signal
to "the" process (the collection of all threads).
More precisely, here is a summary of the standard requirements
and how they are met by the implementation:
1- Synchronous signals (generated by the thread execution, e.g. SIGFPE)
are delivered to the thread that raised them.
(OK.)
2- A fatal asynchronous signal terminates all threads in the process.
(OK. The thread manager notices when a thread dies on a signal
and kills all other threads with the same signal.)
3- An asynchronous signal will be delivered to one of the threads
of the program which does not block the signal (it is unspecified
which).
(No, the signal is delivered to the thread it's been sent to,
based on the pid of the thread. If that thread is currently
blocking the signal, the signal remains pending.)
4- The signal will be delivered to at most one thread.
(OK, except for signals generated from the terminal or sent to
the process group, which will be delivered to all threads.)
- The current implementation of the MIPS support assumes a MIPS ISA II
processor or better. These processors support atomic operations by
ll/sc instructions. Older R2000/R3000 series processors are not
supported yet; support for these will have higher overhead.
- The current implementation of the ARM support assumes that the SWP
(atomic swap register with memory) instruction is available. This is
the case for all processors except for the ARM1 and ARM2. On StrongARM,
the SWP instruction does not bypass the cache, so multi-processor support
will be more troublesome.

View File

@ -0,0 +1,352 @@
This file describes how to make a threaded X11R6.
You need the source-code of XFree-3.2. I used the sources of X11R6.1
(files: xc-1.tar.gz xc-2.tar.gz xc-3.tar.gz) and the patches to
XFree-3.2 (files: README.X11.patch R6.1pl1-3.2.diff.gz cfont32.tgz).
Untar the xc-?.tar.gz files in a directory called XF3.2 and apply
the XFree-3.2 patches as described in README.X11.patch or use the
whole XFree86 source.
Now apply the thread patch with
patch -p0 < XF3.2.xc.diff
Go to the XF3.2/xc directory and make the whole thing:
nice make World >& world.log &
tail -f world.log
Wait a few hours or interrupt the process after the shared libs
are made. The shared libs are:
XF3.2/xc/lib/ICE/libICE.so.6.0*
XF3.2/xc/lib/PEX5/libPEX5.so.6.0*
XF3.2/xc/lib/SM/libSM.so.6.0*
XF3.2/xc/lib/X11/libX11.so.6.1*
XF3.2/xc/lib/XIE/libXIE.so.6.0*
XF3.2/xc/lib/XThrStub/libXThrStub.so.6.0*
XF3.2/xc/lib/Xaw/libXaw.so.6.1*
XF3.2/xc/lib/Xext/libXext.so.6.1*
XF3.2/xc/lib/Xi/libXi.so.6.0*
XF3.2/xc/lib/Xmu/libXmu.so.6.0*
XF3.2/xc/lib/Xt/libXt.so.6.0*
XF3.2/xc/lib/Xtst/libXtst.so.6.1*
(The Program dga didn't compile, but I have not check out why.)
Now you can copy the resulting libs
cp XF3.2/xc/lib/*/*.so.?.? /usr/X11R6/lib/
and create some links
cd /usr/X11R6/lib/
ln -s libXThrStub.so.6.0 libXThrStub.so.6
ln -s libXThrStub.so.6 libXThrStub.so
or use make install (not tested, and needs new configuration).
It is possible with the libXThrSub to compile X11 programs without linking
libpthread to them and not necessary to recompile already installed
unthreaded X11 programs, because libXThrSub keeps the dynamic linker quit.
On the other hand you can link libpthread to a X11 program to use threads.
I used linux 2.0.23 and libc 5.4.7 .
Hans-Helmut B<>hmann hans@expmech.ing.tu-bs.de
----------------------------------------------------------------------------
XF3.2.xc.diff:
-----------------------------------------------------------------------------
diff -u --recursive XF3.2.orig/xc/config/cf/linux.cf XF3.2/xc/config/cf/linux.cf
--- XF3.2.orig/xc/config/cf/linux.cf Sun Nov 10 17:05:30 1996
+++ XF3.2/xc/config/cf/linux.cf Sun Nov 10 16:30:55 1996
@@ -61,6 +61,14 @@
#define HasSnprintf YES
#endif
+#define HasPosixThreads YES
+#define ThreadedX YES
+#define BuildThreadStubLibrary YES
+#define NeedUIThrStubs YES
+#define HasThreadSafeAPI NO
+#define SystemMTDefines -D_REENTRANT
+#define ThreadsLibraries -lpthread
+
#define AvoidNullMakeCommand YES
#define StripInstalledPrograms YES
#define CompressAllFonts YES
@@ -158,7 +166,7 @@
#define LdPostLib /* Never needed */
#ifdef i386Architecture
-#define OptimizedCDebugFlags DefaultGcc2i386Opt -m486
+#define OptimizedCDebugFlags DefaultGcc2i386Opt -m486 -pipe
#define StandardDefines -Dlinux -D__i386__ -D_POSIX_SOURCE \
-D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE
#define XawI18nDefines -DUSE_XWCHAR_STRING -DUSE_XMBTOWC
diff -u --recursive XF3.2.orig/xc/config/cf/lnxLib.tmpl XF3.2/xc/config/cf/lnxLib.tmpl
--- XF3.2.orig/xc/config/cf/lnxLib.tmpl Sun Nov 10 17:05:30 1996
+++ XF3.2/xc/config/cf/lnxLib.tmpl Sat Nov 9 14:52:39 1996
@@ -19,7 +19,7 @@
#define CplusplusLibC
-#define SharedX11Reqs
+#define SharedX11Reqs -L$(BUILDLIBDIR) -lXThrStub
#define SharedOldXReqs $(LDPRELIB) $(XLIBONLY)
#define SharedXtReqs $(LDPRELIB) $(XLIBONLY) $(SMLIB) $(ICELIB)
#define SharedXawReqs $(LDPRELIB) $(XMULIB) $(XTOOLLIB) $(XLIB)
diff -u --recursive XF3.2.orig/xc/include/Xthreads.h XF3.2/xc/include/Xthreads.h
--- XF3.2.orig/xc/include/Xthreads.h Thu Dec 7 02:19:09 1995
+++ XF3.2/xc/include/Xthreads.h Sat Nov 9 01:04:55 1996
@@ -229,12 +229,12 @@
#define xcondition_wait(c,m) pthread_cond_wait(c,m)
#define xcondition_signal(c) pthread_cond_signal(c)
#define xcondition_broadcast(c) pthread_cond_broadcast(c)
-#ifdef _DECTHREADS_
+#if defined(_DECTHREADS_) || defined(linux)
static xthread_t _X_no_thread_id;
#define xthread_have_id(id) !pthread_equal(id, _X_no_thread_id)
#define xthread_clear_id(id) id = _X_no_thread_id
#define xthread_equal(id1,id2) pthread_equal(id1, id2)
-#endif /* _DECTHREADS_ */
+#endif /* _DECTHREADS_ || linux */
#if _CMA_VENDOR_ == _CMA__IBM
#ifdef DEBUG /* too much of a hack to enable normally */
/* see also cma__obj_set_name() */
diff -u --recursive XF3.2.orig/xc/lib/X11/util/makekeys.c XF3.2/xc/lib/X11/util/makekeys.c
--- XF3.2.orig/xc/lib/X11/util/makekeys.c Mon Apr 18 02:22:22 1994
+++ XF3.2/xc/lib/X11/util/makekeys.c Sat Nov 9 00:44:14 1996
@@ -73,7 +73,7 @@
register char c;
int first;
int best_max_rehash;
- int best_z;
+ int best_z = 0;
int num_found;
KeySym val;
diff -u --recursive XF3.2.orig/xc/lib/XThrStub/Imakefile XF3.2/xc/lib/XThrStub/Imakefile
--- XF3.2.orig/xc/lib/XThrStub/Imakefile Sun Nov 10 17:08:12 1996
+++ XF3.2/xc/lib/XThrStub/Imakefile Sat Nov 9 19:04:51 1996
@@ -25,7 +25,7 @@
DEFINES = $(ALLOC_DEFINES)
INCLUDES =
SRCS = $(STUBSRCS)
- OBJS = $(STUBOBJS
+ OBJS = $(STUBOBJS)
LINTLIBS = $(LINTXLIB)
#include <Library.tmpl>
diff -u --recursive XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c XF3.2/xc/lib/XThrStub/UIThrStubs.c
--- XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c Sun Nov 10 17:08:12 1996
+++ XF3.2/xc/lib/XThrStub/UIThrStubs.c Sun Nov 10 15:14:55 1996
@@ -37,16 +37,43 @@
* specificies the thread library on the link line.
*/
+#if defined(linux)
+#include <pthread.h>
+#else
#include <thread.h>
#include <synch.h>
+#endif
+#if defined(linux)
+static pthread_t no_thread_id;
+#endif /* defined(linux) */
+
+#if defined(linux)
+#pragma weak pthread_self = _Xthr_self_stub_
+pthread_t
+_Xthr_self_stub_()
+{
+ return(no_thread_id);
+}
+#else /* defined(linux) */
#pragma weak thr_self = _Xthr_self_stub_
thread_t
_Xthr_self_stub_()
{
return((thread_t)0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_mutex_init = _Xmutex_init_stub_
+int
+_Xmutex_init_stub_(m, a)
+ pthread_mutex_t *m;
+ __const pthread_mutexattr_t *a;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak mutex_init = _Xmutex_init_stub_
int
_Xmutex_init_stub_(m, t, a)
@@ -56,7 +83,17 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_mutex_destroy = _Xmutex_destroy_stub_
+int
+_Xmutex_destroy_stub_(m)
+ pthread_mutex_t *m;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak mutex_destroy = _Xmutex_destroy_stub_
int
_Xmutex_destroy_stub_(m)
@@ -64,7 +101,17 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_mutex_lock = _Xmutex_lock_stub_
+int
+_Xmutex_lock_stub_(m)
+ pthread_mutex_t *m;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak mutex_lock = _Xmutex_lock_stub_
int
_Xmutex_lock_stub_(m)
@@ -72,7 +119,17 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_mutex_unlock = _Xmutex_unlock_stub_
+int
+_Xmutex_unlock_stub_(m)
+ pthread_mutex_t *m;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak mutex_unlock = _Xmutex_unlock_stub_
int
_Xmutex_unlock_stub_(m)
@@ -80,7 +137,18 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_cond_init = _Xcond_init_stub_
+int
+_Xcond_init_stub_(c, a)
+ pthread_cond_t *c;
+ __const pthread_condattr_t *a;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak cond_init = _Xcond_init_stub_
int
_Xcond_init_stub_(c, t, a)
@@ -90,7 +158,17 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_cond_destroy = _Xcond_destroy_stub_
+int
+_Xcond_destroy_stub_(c)
+ pthread_cond_t *c;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak cond_destroy = _Xcond_destroy_stub_
int
_Xcond_destroy_stub_(c)
@@ -98,7 +176,18 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_cond_wait = _Xcond_wait_stub_
+int
+_Xcond_wait_stub_(c,m)
+ pthread_cond_t *c;
+ pthread_mutex_t *m;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak cond_wait = _Xcond_wait_stub_
int
_Xcond_wait_stub_(c,m)
@@ -107,7 +196,17 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_cond_signal = _Xcond_signal_stub_
+int
+_Xcond_signal_stub_(c)
+ pthread_cond_t *c;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak cond_signal = _Xcond_signal_stub_
int
_Xcond_signal_stub_(c)
@@ -115,7 +214,17 @@
{
return(0);
}
+#endif /* defined(linux) */
+#if defined(linux)
+#pragma weak pthread_cond_broadcast = _Xcond_broadcast_stub_
+int
+_Xcond_broadcast_stub_(c)
+ pthread_cond_t *c;
+{
+ return(0);
+}
+#else /* defined(linux) */
#pragma weak cond_broadcast = _Xcond_broadcast_stub_
int
_Xcond_broadcast_stub_(c)
@@ -123,3 +232,15 @@
{
return(0);
}
+#endif /* defined(linux) */
+
+#if defined(linux)
+#pragma weak pthread_equal = _Xthr_equal_stub_
+int
+_Xthr_equal_stub_(t1, t2)
+ pthread_t t1;
+ pthread_t t2;
+{
+ return(1);
+}
+#endif /* defined(linux) */
-------------------------------------------------------------------------

117
linuxthreads/attr.c Normal file
View File

@ -0,0 +1,117 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Handling of thread attributes */
#include <unistd.h>
#include "pthread.h"
#include "internals.h"
int pthread_attr_init(pthread_attr_t *attr)
{
attr->detachstate = PTHREAD_CREATE_JOINABLE;
attr->schedpolicy = SCHED_OTHER;
attr->schedparam.sched_priority = 0;
attr->inheritsched = PTHREAD_EXPLICIT_SCHED;
attr->scope = PTHREAD_SCOPE_SYSTEM;
return 0;
}
int pthread_attr_destroy(pthread_attr_t *attr)
{
return 0;
}
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
{
if (detachstate < PTHREAD_CREATE_JOINABLE ||
detachstate > PTHREAD_CREATE_DETACHED)
return EINVAL;
attr->detachstate = detachstate;
return 0;
}
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
{
*detachstate = attr->detachstate;
return 0;
}
int pthread_attr_setschedparam(pthread_attr_t *attr,
const struct sched_param *param)
{
int max_prio = __sched_get_priority_max(attr->schedpolicy);
int min_prio = __sched_get_priority_min(attr->schedpolicy);
if (param->sched_priority < min_prio || param->sched_priority > max_prio)
return EINVAL;
attr->schedparam = *param;
return 0;
}
int pthread_attr_getschedparam(const pthread_attr_t *attr,
struct sched_param *param)
{
*param = attr->schedparam;
return 0;
}
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
{
if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR)
return EINVAL;
if (policy != SCHED_OTHER && geteuid() != 0)
return ENOTSUP;
attr->schedpolicy = policy;
return 0;
}
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
{
*policy = attr->schedpolicy;
return 0;
}
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit)
{
if (inherit != PTHREAD_INHERIT_SCHED && inherit != PTHREAD_EXPLICIT_SCHED)
return EINVAL;
attr->inheritsched = inherit;
return 0;
}
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit)
{
*inherit = attr->inheritsched;
return 0;
}
int pthread_attr_setscope(pthread_attr_t *attr, int scope)
{
switch (scope) {
case PTHREAD_SCOPE_SYSTEM:
attr->scope = scope;
return 0;
case PTHREAD_SCOPE_PROCESS:
return ENOTSUP;
default:
return EINVAL;
}
}
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope)
{
*scope = attr->scope;
return 0;
}

131
linuxthreads/cancel.c Normal file
View File

@ -0,0 +1,131 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Thread cancellation */
#include <errno.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
int pthread_setcancelstate(int state, int * oldstate)
{
pthread_descr self = thread_self();
if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
return EINVAL;
if (oldstate != NULL) *oldstate = self->p_cancelstate;
self->p_cancelstate = state;
if (self->p_canceled &&
self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_exit(PTHREAD_CANCELED);
return 0;
}
int pthread_setcanceltype(int type, int * oldtype)
{
pthread_descr self = thread_self();
if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
return EINVAL;
if (oldtype != NULL) *oldtype = self->p_canceltype;
self->p_canceltype = type;
if (self->p_canceled &&
self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_exit(PTHREAD_CANCELED);
return 0;
}
int pthread_cancel(pthread_t thread)
{
pthread_handle handle = thread_handle(thread);
int pid;
acquire(&handle->h_spinlock);
if (invalid_handle(handle, thread)) {
release(&handle->h_spinlock);
return ESRCH;
}
handle->h_descr->p_canceled = 1;
pid = handle->h_descr->p_pid;
release(&handle->h_spinlock);
kill(pid, PTHREAD_SIG_CANCEL);
return 0;
}
void pthread_testcancel(void)
{
pthread_descr self = thread_self();
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)
pthread_exit(PTHREAD_CANCELED);
}
void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
void (*routine)(void *), void * arg)
{
pthread_descr self = thread_self();
buffer->routine = routine;
buffer->arg = arg;
buffer->prev = self->p_cleanup;
self->p_cleanup = buffer;
}
void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
int execute)
{
pthread_descr self = thread_self();
if (execute) buffer->routine(buffer->arg);
self->p_cleanup = buffer->prev;
}
void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
void (*routine)(void *), void * arg)
{
pthread_descr self = thread_self();
buffer->routine = routine;
buffer->arg = arg;
buffer->canceltype = self->p_canceltype;
buffer->prev = self->p_cleanup;
self->p_canceltype = PTHREAD_CANCEL_DEFERRED;
self->p_cleanup = buffer;
}
void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
int execute)
{
pthread_descr self = thread_self();
if (execute) buffer->routine(buffer->arg);
self->p_cleanup = buffer->prev;
self->p_canceltype = buffer->canceltype;
if (self->p_canceled &&
self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_exit(PTHREAD_CANCELED);
}
void __pthread_perform_cleanup(void)
{
pthread_descr self = thread_self();
struct _pthread_cleanup_buffer * c;
for (c = self->p_cleanup; c != NULL; c = c->prev) c->routine(c->arg);
}
#ifndef PIC
/* We need a hook to force the cancelation wrappers to be linked in when
static libpthread is used. */
extern const int __pthread_provide_wrappers;
static const int * const __pthread_require_wrappers =
&__pthread_provide_wrappers;
#endif

207
linuxthreads/condvar.c Normal file
View File

@ -0,0 +1,207 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* and Pavel Krauz (krauz@fsid.cvut.cz). */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Condition variables */
#include <errno.h>
#include <sched.h>
#include <stddef.h>
#include <sys/time.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "queue.h"
#include "restart.h"
static void remove_from_queue(pthread_queue * q, pthread_descr th);
int pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *cond_attr)
{
cond->c_spinlock = 0;
queue_init(&cond->c_waiting);
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
pthread_descr head;
acquire(&cond->c_spinlock);
head = cond->c_waiting.head;
release(&cond->c_spinlock);
if (head != NULL) return EBUSY;
return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
volatile pthread_descr self = thread_self();
acquire(&cond->c_spinlock);
enqueue(&cond->c_waiting, self);
release(&cond->c_spinlock);
pthread_mutex_unlock(mutex);
suspend_with_cancellation(self);
pthread_mutex_lock(mutex);
/* This is a cancellation point */
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
/* Remove ourselves from the waiting queue if we're still on it */
acquire(&cond->c_spinlock);
remove_from_queue(&cond->c_waiting, self);
release(&cond->c_spinlock);
pthread_exit(PTHREAD_CANCELED);
}
return 0;
}
static inline int
pthread_cond_timedwait_relative(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec * reltime)
{
volatile pthread_descr self = thread_self();
sigset_t unblock, initial_mask;
int retsleep;
sigjmp_buf jmpbuf;
/* Wait on the condition */
acquire(&cond->c_spinlock);
enqueue(&cond->c_waiting, self);
release(&cond->c_spinlock);
pthread_mutex_unlock(mutex);
/* Set up a longjmp handler for the restart signal */
/* No need to save the signal mask, since PTHREAD_SIG_RESTART will be
blocked when doing the siglongjmp, and we'll just leave it blocked. */
if (sigsetjmp(jmpbuf, 0) == 0) {
self->p_signal_jmp = &jmpbuf;
self->p_signal = 0;
/* Check for cancellation */
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
retsleep = -1;
} else {
/* Unblock the restart signal */
sigemptyset(&unblock);
sigaddset(&unblock, PTHREAD_SIG_RESTART);
sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
/* Sleep for the required duration */
retsleep = __libc_nanosleep(reltime, NULL);
/* Block the restart signal again */
sigprocmask(SIG_SETMASK, &initial_mask, NULL);
}
} else {
retsleep = -1;
}
self->p_signal_jmp = NULL;
/* Here, either the condition was signaled (self->p_signal != 0)
or we got canceled (self->p_canceled != 0)
or the timeout occurred (retsleep == 0)
or another interrupt occurred (retsleep == -1) */
/* Re-acquire the spinlock */
acquire(&cond->c_spinlock);
/* This is a cancellation point */
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
remove_from_queue(&cond->c_waiting, self);
release(&cond->c_spinlock);
pthread_mutex_lock(mutex);
pthread_exit(PTHREAD_CANCELED);
}
/* If not signaled: also remove ourselves and return an error code */
if (self->p_signal == 0) {
remove_from_queue(&cond->c_waiting, self);
release(&cond->c_spinlock);
pthread_mutex_lock(mutex);
return retsleep == 0 ? ETIMEDOUT : EINTR;
}
/* Otherwise, return normally */
release(&cond->c_spinlock);
pthread_mutex_lock(mutex);
return 0;
}
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec * abstime)
{
struct timeval now;
struct timespec reltime;
/* Compute a time offset relative to now */
__gettimeofday(&now, NULL);
reltime.tv_sec = abstime->tv_sec - now.tv_sec;
reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
if (reltime.tv_nsec < 0) {
reltime.tv_nsec += 1000000000;
reltime.tv_sec -= 1;
}
if (reltime.tv_sec < 0) return ETIMEDOUT;
return pthread_cond_timedwait_relative(cond, mutex, &reltime);
}
int pthread_cond_signal(pthread_cond_t *cond)
{
pthread_descr th;
acquire(&cond->c_spinlock);
th = dequeue(&cond->c_waiting);
release(&cond->c_spinlock);
if (th != NULL) restart(th);
return 0;
}
int pthread_cond_broadcast(pthread_cond_t *cond)
{
pthread_queue tosignal;
pthread_descr th;
acquire(&cond->c_spinlock);
/* Copy the current state of the waiting queue and empty it */
tosignal = cond->c_waiting;
queue_init(&cond->c_waiting);
release(&cond->c_spinlock);
/* Now signal each process in the queue */
while ((th = dequeue(&tosignal)) != NULL) restart(th);
return 0;
}
int pthread_condattr_init(pthread_condattr_t *attr)
{
return 0;
}
int pthread_condattr_destroy(pthread_condattr_t *attr)
{
return 0;
}
/* Auxiliary function on queues */
static void remove_from_queue(pthread_queue * q, pthread_descr th)
{
pthread_descr t;
if (q->head == NULL) return;
if (q->head == th) {
q->head = th->p_nextwaiting;
if (q->head == NULL) q->tail = NULL;
th->p_nextwaiting = NULL;
return;
}
for (t = q->head; t->p_nextwaiting != NULL; t = t->p_nextwaiting) {
if (t->p_nextwaiting == th) {
t->p_nextwaiting = th->p_nextwaiting;
if (th->p_nextwaiting == NULL) q->tail = t;
th->p_nextwaiting = NULL;
return;
}
}
}

5
linuxthreads/configure vendored Executable file
View File

@ -0,0 +1,5 @@
# This is only to keep the GNU C library configure mechanism happy.
#
# Perhaps some day we need a real configuration script for different
# kernel versions or so.
exit 0

32
linuxthreads/errno.c Normal file
View File

@ -0,0 +1,32 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Define the location of errno for the remainder of the C library */
#include <errno.h>
#include <netdb.h>
#include "pthread.h"
#include "internals.h"
int * __errno_location()
{
pthread_descr self = thread_self();
return self->p_errnop;
}
int * __h_errno_location()
{
pthread_descr self = thread_self();
return self->p_h_errnop;
}

280
linuxthreads/internals.h Normal file
View File

@ -0,0 +1,280 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Internal data structures */
/* Includes */
#include <bits/libc-lock.h> /* for _LIBC_TSD_KEY_N */
#include <setjmp.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include "pt-machine.h"
/* Arguments passed to thread creation routine */
struct pthread_start_args {
void * (*start_routine)(void *); /* function to run */
void * arg; /* its argument */
sigset_t mask; /* initial signal mask for thread */
int schedpolicy; /* initial scheduling policy (if any) */
struct sched_param schedparam; /* initial scheduling parameters (if any) */
};
/* We keep thread specific data in a special data structure, a two-level
array. The top-level array contains pointers to dynamically allocated
arrays of a certain number of data pointers. So we can implement a
sparse array. Each dynamic second-level array has
PTHREAD_KEY_2NDLEVEL_SIZE
entries. This value shouldn't be too large. */
#define PTHREAD_KEY_2NDLEVEL_SIZE 32
/* We need to address PTHREAD_KEYS_MAX key with PTHREAD_KEY_2NDLEVEL_SIZE
keys in each subarray. */
#define PTHREAD_KEY_1STLEVEL_SIZE \
((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1) \
/ PTHREAD_KEY_2NDLEVEL_SIZE)
#define PTHREAD_START_ARGS_INITIALIZER { NULL, NULL, {{0, }}, 0, { 0 } }
/* The type of thread descriptors */
typedef struct _pthread_descr_struct * pthread_descr;
struct _pthread_descr_struct {
pthread_descr p_nextlive, p_prevlive;
/* Double chaining of active threads */
pthread_descr p_nextwaiting; /* Next element in the queue holding the thr */
pthread_t p_tid; /* Thread identifier */
int p_pid; /* PID of Unix process */
int p_priority; /* Thread priority (== 0 if not realtime) */
int * p_spinlock; /* Spinlock for synchronized accesses */
int p_signal; /* last signal received */
sigjmp_buf * p_signal_jmp; /* where to siglongjmp on a signal or NULL */
sigjmp_buf * p_cancel_jmp; /* where to siglongjmp on a cancel or NULL */
char p_terminated; /* true if terminated e.g. by pthread_exit */
char p_detached; /* true if detached */
char p_exited; /* true if the assoc. process terminated */
void * p_retval; /* placeholder for return value */
int p_retcode; /* placeholder for return code */
pthread_descr p_joining; /* thread joining on that thread or NULL */
struct _pthread_cleanup_buffer * p_cleanup; /* cleanup functions */
char p_cancelstate; /* cancellation state */
char p_canceltype; /* cancellation type (deferred/async) */
char p_canceled; /* cancellation request pending */
int * p_errnop; /* pointer to used errno variable */
int p_errno; /* error returned by last system call */
int * p_h_errnop; /* pointer to used h_errno variable */
int p_h_errno; /* error returned by last netdb function */
struct pthread_start_args p_start_args; /* arguments for thread creation */
void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE]; /* thread-specific data */
void * p_libc_specific[_LIBC_TSD_KEY_N]; /* thread-specific data for libc */
};
/* The type of thread handles. */
typedef struct pthread_handle_struct * pthread_handle;
struct pthread_handle_struct {
int h_spinlock; /* Spinlock for sychronized access */
pthread_descr h_descr; /* Thread descriptor or NULL if invalid */
};
/* The type of messages sent to the thread manager thread */
struct pthread_request {
pthread_descr req_thread; /* Thread doing the request */
enum { /* Request kind */
REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT
} req_kind;
union { /* Arguments for request */
struct { /* For REQ_CREATE: */
const pthread_attr_t * attr; /* thread attributes */
void * (*fn)(void *); /* start function */
void * arg; /* argument to start function */
sigset_t mask; /* signal mask */
} create;
struct { /* For REQ_FREE: */
pthread_descr thread; /* descriptor of thread to free */
} free;
struct { /* For REQ_PROCESS_EXIT: */
int code; /* exit status */
} exit;
} req_args;
};
/* Signals used for suspend/restart and for cancellation notification. */
#ifdef SIGRTMIN
/* The have real-time signals. */
extern int __pthread_sig_restart;
extern int __pthread_sig_cancel;
# define PTHREAD_SIG_RESTART __pthread_sig_restart
# define PTHREAD_SIG_CANCEL __pthread_sig_cancel
#else
# define PTHREAD_SIG_RESTART SIGUSR1
# define PTHREAD_SIG_CANCEL SIGUSR2
#endif
/* Global array of thread handles, used for validating a thread id
and retrieving the corresponding thread descriptor. Also used for
mapping the available stack segments. */
extern struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX];
/* Descriptor of the initial thread */
extern struct _pthread_descr_struct __pthread_initial_thread;
/* Descriptor of the manager thread */
extern struct _pthread_descr_struct __pthread_manager_thread;
/* Descriptor of the main thread */
extern pthread_descr __pthread_main_thread;
/* Limit between the stack of the initial thread (above) and the
stacks of other threads (below). Aligned on a STACK_SIZE boundary.
Initially 0, meaning that the current thread is (by definition)
the initial thread. */
extern char *__pthread_initial_thread_bos;
/* File descriptor for sending requests to the thread manager.
Initially -1, meaning that pthread_initialize must be called. */
extern int __pthread_manager_request;
/* Other end of the pipe for sending requests to the thread manager. */
extern int __pthread_manager_reader;
/* Limits of the thread manager stack. */
extern char *__pthread_manager_thread_bos;
extern char *__pthread_manager_thread_tos;
/* Pending request for a process-wide exit */
extern int __pthread_exit_requested, __pthread_exit_code;
/* Return the handle corresponding to a thread id */
static inline pthread_handle thread_handle(pthread_t id)
{
return &__pthread_handles[id % PTHREAD_THREADS_MAX];
}
/* Validate a thread handle. Must have acquired h->h_spinlock before. */
static inline int invalid_handle(pthread_handle h, pthread_t id)
{
return h->h_descr == NULL || h->h_descr->p_tid != id;
}
/* Fill in defaults left unspecified by pt-machine.h. */
/* The page size we can get from the system. This should likely not be
changed by the machine file but, you never know. */
#ifndef PAGE_SIZE
#define PAGE_SIZE (sysconf (_SC_PAGE_SIZE))
#endif
/* The max size of the thread stack segments. If the default
THREAD_SELF implementation is used, this must be a power of two and
a multiple of PAGE_SIZE. */
#ifndef STACK_SIZE
#define STACK_SIZE (2 * 1024 * 1024)
#endif
/* The initial size of the thread stack. Must be a multiple of PAGE_SIZE. */
#ifndef INITIAL_STACK_SIZE
#define INITIAL_STACK_SIZE (4 * PAGE_SIZE)
#endif
/* Size of the thread manager stack. The "- 32" avoids wasting space
with some malloc() implementations. */
#ifndef THREAD_MANAGER_STACK_SIZE
#define THREAD_MANAGER_STACK_SIZE (2 * PAGE_SIZE - 32)
#endif
/* The base of the "array" of thread stacks. The array will grow down from
here. Defaults to the calculated bottom of the initial application
stack. */
#ifndef THREAD_STACK_START_ADDRESS
#define THREAD_STACK_START_ADDRESS __pthread_initial_thread_bos
#endif
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#ifndef CURRENT_STACK_FRAME
#define CURRENT_STACK_FRAME ({ char __csf; &__csf; })
#endif
/* Recover thread descriptor for the current thread */
static inline pthread_descr thread_self (void) __attribute__ ((const));
static inline pthread_descr thread_self (void)
{
#ifdef THREAD_SELF
return THREAD_SELF;
#else
char *sp = CURRENT_STACK_FRAME;
if (sp >= __pthread_initial_thread_bos)
return &__pthread_initial_thread;
else if (sp >= __pthread_manager_thread_bos
&& sp < __pthread_manager_thread_tos)
return &__pthread_manager_thread;
else
return (pthread_descr)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1;
#endif
}
/* Debugging */
#ifdef DEBUG
#include <assert.h>
#define ASSERT assert
#define MSG __pthread_message
#else
#define ASSERT(x)
#define MSG(msg,arg...)
#endif
/* Internal global functions */
void __pthread_destroy_specifics(void);
void __pthread_perform_cleanup(void);
void __pthread_sighandler(int sig);
void __pthread_message(char * fmt, long arg, ...);
int __pthread_manager(void *reqfd);
void __pthread_manager_sighandler(int sig);
void __pthread_reset_main_thread(void);
void __fresetlockfiles(void);
/* Prototypes for the function without cancelation support when the
normal version has it. */
extern int __libc_close (int fd);
extern int __libc_nanosleep (const struct timespec *requested_time,
struct timespec *remaining);
extern int __libc_read (int fd, void *buf, size_t count);
extern pid_t __libc_waitpid (pid_t pid, int *stat_loc, int options);
extern int __libc_write (int fd, const void *buf, size_t count);

145
linuxthreads/join.c Normal file
View File

@ -0,0 +1,145 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Thread termination and joining */
#include <errno.h>
#include <sched.h>
#include <unistd.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
void pthread_exit(void * retval)
{
pthread_descr self = thread_self();
pthread_descr joining;
struct pthread_request request;
/* Reset the cancellation flag to avoid looping if the cleanup handlers
contain cancellation points */
self->p_canceled = 0;
/* Call cleanup functions and destroy the thread-specific data */
__pthread_perform_cleanup();
__pthread_destroy_specifics();
/* Store return value */
acquire(self->p_spinlock);
self->p_retval = retval;
/* Say that we've terminated */
self->p_terminated = 1;
/* See if someone is joining on us */
joining = self->p_joining;
release(self->p_spinlock);
/* Restart joining thread if any */
if (joining != NULL) restart(joining);
/* If this is the initial thread, block until all threads have terminated.
If another thread calls exit, we'll be terminated from our signal
handler. */
if (self == __pthread_main_thread && __pthread_manager_request >= 0) {
request.req_thread = self;
request.req_kind = REQ_MAIN_THREAD_EXIT;
__libc_write(__pthread_manager_request, (char *)&request, sizeof(request));
suspend(self);
}
/* Exit the process (but don't flush stdio streams, and don't run
atexit functions). */
_exit(0);
}
int pthread_join(pthread_t thread_id, void ** thread_return)
{
volatile pthread_descr self = thread_self();
struct pthread_request request;
pthread_handle handle = thread_handle(thread_id);
pthread_descr th;
acquire(&handle->h_spinlock);
if (invalid_handle(handle, thread_id)) {
release(&handle->h_spinlock);
return ESRCH;
}
th = handle->h_descr;
if (th == self) {
release(&handle->h_spinlock);
return EDEADLK;
}
/* If detached or already joined, error */
if (th->p_detached || th->p_joining != NULL) {
release(&handle->h_spinlock);
return EINVAL;
}
/* If not terminated yet, suspend ourselves. */
if (! th->p_terminated) {
th->p_joining = self;
release(&handle->h_spinlock);
suspend_with_cancellation(self);
/* This is a cancellation point */
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
th->p_joining = NULL;
pthread_exit(PTHREAD_CANCELED);
}
acquire(&handle->h_spinlock);
}
/* Get return value */
if (thread_return != NULL) *thread_return = th->p_retval;
release(&handle->h_spinlock);
/* Send notification to thread manager */
if (__pthread_manager_request >= 0) {
request.req_thread = self;
request.req_kind = REQ_FREE;
request.req_args.free.thread = th;
__libc_write(__pthread_manager_request,
(char *) &request, sizeof(request));
}
return 0;
}
int pthread_detach(pthread_t thread_id)
{
int terminated;
struct pthread_request request;
pthread_handle handle = thread_handle(thread_id);
pthread_descr th;
acquire(&handle->h_spinlock);
if (invalid_handle(handle, thread_id)) {
release(&handle->h_spinlock);
return ESRCH;
}
th = handle->h_descr;
/* If already detached, error */
if (th->p_detached) {
release(&handle->h_spinlock);
return EINVAL;
}
/* If already joining, don't do anything. */
if (th->p_joining != NULL) {
release(&handle->h_spinlock);
return 0;
}
/* Mark as detached */
th->p_detached = 1;
terminated = th->p_terminated;
release(&handle->h_spinlock);
/* If already terminated, notify thread manager to reclaim resources */
if (terminated && __pthread_manager_request >= 0) {
request.req_thread = thread_self();
request.req_kind = REQ_FREE;
request.req_args.free.thread = th;
__libc_write(__pthread_manager_request,
(char *) &request, sizeof(request));
}
return 0;
}

View File

@ -0,0 +1,62 @@
GLIBC_2.0 {
global:
# Hidden entry point (through macros).
_pthread_cleanup_pop; _pthread_cleanup_pop_restore; _pthread_cleanup_push;
_pthread_cleanup_push_defer;
# Overwritten libc functions.
close; fcntl; fork; fsync; lseek; msync; nanosleep; open; pause; raise;
read; system; tcdrain; wait; waitpid; write;
# POSIX.1c extensions to libc.
flockfile; funlockfile; ftrylockfile;
# Non-standard POSIX1.x functions.
pthread_kill_other_threads_np; pthread_mutexattr_getkind_np;
pthread_mutexattr_setkind_np;
# Real POSIX.1c functions.
pthread_atfork; pthread_attr_destroy; pthread_attr_getdetachstate;
pthread_attr_getinheritsched; pthread_attr_getschedparam;
pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_init;
pthread_attr_setdetachstate; pthread_attr_setinheritsched;
pthread_attr_setschedparam; pthread_attr_setschedpolicy;
pthread_attr_setscope; pthread_cancel; pthread_cond_broadcast;
pthread_cond_destroy; pthread_cond_init; pthread_cond_signal;
pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy;
pthread_condattr_init; pthread_create; pthread_detach; pthread_equal;
pthread_exit; pthread_getschedparam; pthread_getspecific; pthread_join;
pthread_key_create; pthread_key_delete; pthread_kill;
pthread_mutex_destroy; pthread_mutex_init; pthread_mutex_lock;
pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy;
pthread_mutexattr_init; pthread_once; pthread_self; pthread_setcancelstate;
pthread_setcanceltype; pthread_setschedparam; pthread_setspecific;
pthread_sigmask; pthread_testcancel;
sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait;
sigwait;
# Protected names for functions used in other shared objects.
__pthread_getspecific; __pthread_initialize; __pthread_mutex_destroy;
__pthread_mutex_init; __pthread_mutex_lock; __pthread_mutex_trylock;
__pthread_mutex_unlock; __pthread_mutexattr_destroy;
__pthread_mutexattr_init; __pthread_mutexattr_setkind_np;
__pthread_setspecific;
# The error functions.
__errno_location; __h_errno_location;
local:
*;
};
GLIBC_2.1 {
global:
# Unix98 extensions.
pthread_rwlock_init; pthread_rwlock_destroy; pthread_rwlock_rdlock;
pthread_rwlock_tryrdlock; pthread_rwlock_wrlock; pthread_rwlock_trywrlock;
pthread_rwlock_unlock; pthread_rwlockattr_init; pthread_rwlockattr_destroy;
pthread_rwlockattr_getpshared; pthread_rwlockattr_setpshared;
pthread_rwlockattr_getkind_np; pthread_rwlockattr_setkind_np;
} GLIBC_2.0;

87
linuxthreads/lockfile.c Normal file
View File

@ -0,0 +1,87 @@
/* lockfile - Handle locking and unlocking of stream.
Copyright (C) 1996 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <bits/libc-lock.h>
#include <stdio.h>
#include <pthread.h>
#ifdef USE_IN_LIBIO
#include "../libio/libioP.h"
#endif
void
__flockfile (FILE *stream)
{
#ifdef USE_IN_LIBIO
__pthread_mutex_lock (stream->_lock);
#else
#endif
}
#ifdef USE_IN_LIBIO
#undef _IO_flockfile
strong_alias (__flockfile, _IO_flockfile)
#endif
weak_alias (__flockfile, flockfile);
void
__funlockfile (FILE *stream)
{
#ifdef USE_IN_LIBIO
__pthread_mutex_unlock (stream->_lock);
#else
#endif
}
#ifdef USE_IN_LIBIO
#undef _IO_funlockfile
strong_alias (__funlockfile, _IO_funlockfile)
#endif
weak_alias (__funlockfile, funlockfile);
int
__ftrylockfile (FILE *stream)
{
#ifdef USE_IN_LIBIO
return __pthread_mutex_trylock (stream->_lock);
#else
#endif
}
#ifdef USE_IN_LIBIO
strong_alias (__ftrylockfile, _IO_ftrylockfile)
#endif
weak_alias (__ftrylockfile, ftrylockfile);
void
__fresetlockfiles (void)
{
#ifdef USE_IN_LIBIO
_IO_FILE *fp;
pthread_mutexattr_t attr;
__pthread_mutexattr_init (&attr);
__pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
for (fp = _IO_list_all; fp != NULL; fp = fp->_chain)
__pthread_mutex_init (fp->_lock, &attr);
__pthread_mutexattr_destroy (&attr);
#endif
}

31
linuxthreads/man/Makefile Normal file
View File

@ -0,0 +1,31 @@
SOURCES=pthread_atfork.man pthread_attr_init.man pthread_cancel.man \
pthread_cleanup_push.man pthread_cond_init.man \
pthread_condattr_init.man pthread_create.man pthread_detach.man \
pthread_equal.man pthread_exit.man pthread_join.man \
pthread_key_create.man pthread_mutex_init.man \
pthread_mutexattr_init.man pthread_once.man pthread_self.man \
pthread_setschedparam.man pthread_sigmask.man sem_init.man \
pthread_kill_other_threads_np.man
MANPAGES=$(SOURCES:.man=.3thr)
PREPRO=perl troffprepro
MANDIR=/usr/man/man3
all: $(MANPAGES)
.SUFFIXES: .man .3thr
.man.3thr:
$(PREPRO) $*.man $*.3thr
$(MANPAGES): troffprepro
clean:
rm -f *.3thr
rm -f *~
install:
install *.3thr $(MANDIR)
@echo "*** Remember to run /usr/sbin/makewhatis `dirname $(MANDIR)` at some point"

View File

@ -0,0 +1,58 @@
.TH PTHREAD_ATFORK 3 LinuxThreads
.SH NAME
pthread_atfork \- register handlers to be called at fork(2) time
.SH SYNOPSIS
#include <pthread.h>
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
.SH DESCRIPTION
!pthread_atfork! registers handler functions to be called just before
and just after a new process is created with !fork!(2). The |prepare|
handler will be called from the parent process, just before the new
process is created. The |parent| handler will be called from the parent
process, just before !fork!(2) returns. The |child| handler will be
called from the child process, just before !fork!(2) returns.
One or several of the three handlers |prepare|, |parent| and |child|
can be given as !NULL!, meaning that no handler needs to be called at
the corresponding point.
!pthread_atfork! can be called several times to install several sets
of handlers. At !fork!(2) time, the |prepare| handlers are called in
LIFO order (last added with !pthread_atfork!, first called before !fork!),
while the |parent| and |child| handlers are called in FIFO order
(first added, first called).
To understand the purpose of !pthread_atfork!, recall that !fork!(2)
duplicates the whole memory space, including mutexes in their current
locking state, but only the calling thread: other threads are not
running in the child process. Thus, if a mutex is locked by a thread
other than the thread calling !fork!, that mutex will remain locked
forever in the child process, possibly blocking the execution of the
child process. To avoid this, install handlers with !pthread_atfork!
as follows: the |prepare| handler locks the global mutexes (in locking
order), and the |parent| and |child| handlers unlock them (in
reverse order). Alternatively, |prepare| and |parent| can be set to
!NULL! and |child| to a function that calls !pthread_mutex_init! on
the global mutexes.
.SH "RETURN VALUE"
!pthread_atfork! returns 0 on success and a non-zero error code on error.
.SH ERRORS
.TP
!ENOMEM!
insufficient memory available to register the handlers.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!fork!(2),
!pthread_mutex_lock!(3),
!pthread_mutex_unlock!(3).

View File

@ -0,0 +1,221 @@
.TH PTHREAD_ATTR_INIT 3 LinuxThreads
.XREF pthread_attr_destroy
.XREF pthread_attr_setdetachstate
.XREF pthread_attr_getdetachstate
.XREF pthread_attr_setschedparam
.XREF pthread_attr_getschedparam
.XREF pthread_attr_setschedpolicy
.XREF pthread_attr_getschedpolicy
.XREF pthread_attr_setinheritsched
.XREF pthread_attr_getinheritsched
.XREF pthread_attr_setscope
.XREF pthread_attr_getscope
.SH NAME
pthread_attr_init, pthread_attr_destroy, pthread_attr_setdetachstate, pthread_attr_getdetachstate, pthread_attr_setschedparam, pthread_attr_getschedparam, pthread_attr_setschedpolicy, pthread_attr_getschedpolicy, pthread_attr_setinheritsched, pthread_attr_getinheritsched, pthread_attr_setscope, pthread_attr_getscope \- thread creation attributes
.SH SYNOPSIS
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
.SH DESCRIPTION
Setting attributes for threads is achieved by filling a
thread attribute object |attr| of type !pthread_attr_t!, then passing it as
second argument to !pthread_create!(3). Passing !NULL! is equivalent to
passing a thread attribute object with all attributes set to their
default values.
!pthread_attr_init! initializes the thread attribute object |attr| and
fills it with default values for the attributes. (The default values
are listed below for each attribute.)
Each attribute |attrname| (see below for a list of all attributes) can
be individually set using the function !pthread_attr_set!|attrname|
and retrieved using the function !pthread_attr_get!|attrname|.
!pthread_attr_destroy! destroys a thread attribute object, which
must not be reused until it is reinitialized. !pthread_attr_destroy!
does nothing in the LinuxThreads implementation.
Attribute objects are consulted only when creating a new thread. The
same attribute object can be used for creating several
threads. Modifying an attribute object after a call to
!pthread_create! does not change the attributes of the thread
previously created.
The following thread attributes are supported:
.SS detachstate
Control whether the thread is created in the joinable state (value
!PTHREAD_CREATE_JOINABLE!) or in the detached state
(!PTHREAD_CREATE_DETACHED!).
Default value: !PTHREAD_CREATE_JOINABLE!.
In the joinable state, another thread can synchronize on the thread
termination and recover its termination code using !pthread_join!(3),
but some of the thread resources are kept allocated after the thread
terminates, and reclaimed only when another thread performs
!pthread_join!(3) on that thread.
In the detached state, the thread resources are immediately freed when
it terminates, but !pthread_join!(3) cannot be used to synchronize on
the thread termination.
A thread created in the joinable state can later be put in the
detached thread using !pthread_detach!(3).
.SS schedpolicy
Select the scheduling policy for the thread: one of
!SCHED_OTHER! (regular, non-realtime scheduling),
!SCHED_RR! (realtime, round-robin) or
!SCHED_FIFO! (realtime, first-in first-out). See
!sched_setpolicy!(2) for more information on scheduling policies.
Default value: !SCHED_OTHER!.
The realtime scheduling policies !SCHED_RR! and !SCHED_FIFO! are
available only to processes with superuser privileges.
The scheduling policy of a thread can be changed after creation with
!pthread_setschedparam!(3).
.SS schedparam
Contain the scheduling parameters (essentially, the scheduling
priority) for the thread. See !sched_setparam!(2) for more information
on scheduling parameters.
Default value: priority is 0.
This attribute is not significant if the scheduling policy is !SCHED_OTHER!;
it only matters for the realtime policies !SCHED_RR! and !SCHED_FIFO!.
The scheduling priority of a thread can be changed after creation with
!pthread_setschedparam!(3).
.SS inheritsched
Indicate whether the scheduling policy and scheduling parameters for
the newly created thread are determined by the values of the
|schedpolicy| and |schedparam| attributes (value
!PTHREAD_EXPLICIT_SCHED!) or are inherited from the parent thread
(value !PTHREAD_INHERIT_SCHED!).
Default value: !PTHREAD_EXPLICIT_SCHED!.
.SS scope
Define the scheduling contention scope for the created thread. The
only value supported in the LinuxThreads implementation is
!PTHREAD_SCOPE_SYSTEM!, meaning that the threads contend for CPU time
with all processes running on the machine. In particular, thread
priorities are interpreted relative to the priorities of all other
processes on the machine. The other value specified by the standard,
!PTHREAD_SCOPE_PROCESS!, means that scheduling contention occurs only
between the threads of the running process: thread priorities are
interpreted relative to the priorities of the other threads of the
process, regardless of the priorities of other processes.
!PTHREAD_SCOPE_PROCESS! is not supported in LinuxThreads.
Default value: !PTHREAD_SCOPE_SYSTEM!.
.SH "RETURN VALUE"
All functions return 0 on success and a non-zero error code on error.
On success, the !pthread_attr_get!|attrname| functions also store the
current value of the attribute |attrname| in the location pointed to
by their second argument.
.SH ERRORS
The !pthread_attr_setdetachstate! function returns the following error
codes on error:
.RS
.TP
!EINVAL!
the specified |detachstate| is not one of !PTHREAD_CREATE_JOINABLE! or
!PTHREAD_CREATE_DETACHED!.
.RE
The !pthread_attr_setschedparam! function returns the following error
codes on error:
.RS
.TP
!EINVAL!
the priority specified in |param| is outside the range of allowed
priorities for the scheduling policy currently in |attr|
(1 to 99 for !SCHED_FIFO! and !SCHED_RR!; 0 for !SCHED_OTHER!).
.RE
The !pthread_attr_setschedpolicy! function returns the following error
codes on error:
.RS
.TP
!EINVAL!
the specified |policy| is not one of !SCHED_OTHER!, !SCHED_FIFO!, or
!SCHED_RR!.
.TP
!ENOTSUP!
|policy| is !SCHED_FIFO! or !SCHED_RR!, and the effective user of the
calling process is not super-user.
.RE
The !pthread_attr_setinheritsched! function returns the following error
codes on error:
.RS
.TP
!EINVAL!
the specified |inherit| is not one of !PTHREAD_INHERIT_SCHED! or
!PTHREAD_EXPLICIT_SCHED!.
.RE
The !pthread_attr_setscope! function returns the following error
codes on error:
.RS
.TP
!EINVAL!
the specified |scope| is not one of !PTHREAD_SCOPE_SYSTEM! or
!PTHREAD_SCOPE_PROCESS!.
.TP
!ENOTSUP!
the specified |scope| is !PTHREAD_SCOPE_PROCESS! (not supported).
.RE
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_create!(3),
!pthread_join!(3),
!pthread_detach!(3),
!pthread_setschedparam!(3).

View File

@ -0,0 +1,155 @@
.TH PTHREAD_CANCEL 3 LinuxThreads
.XREF pthread_setcancelstate
.XREF pthread_setcanceltype
.XREF pthread_testcancel
.SH NAME
pthread_cancel, pthread_setcancelstate, pthread_setcanceltype, pthread_testcancel \- thread cancellation
.SH SYNOPSIS
#include <pthread.h>
int pthread_cancel(pthread_t thread);
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);
void pthread_testcancel(void);
.SH DESCRIPTION
Cancellation is the mechanism by which a thread can terminate the
execution of another thread. More precisely, a thread can send a
cancellation request to another thread. Depending on its settings, the
target thread can then either ignore the request, honor it
immediately, or defer it till it reaches a cancellation point.
When a thread eventually honors a cancellation request, it performs as
if !pthread_exit(PTHREAD_CANCELED)! has been called at that point:
all cleanup handlers are executed in reverse order, finalization
functions for thread-specific data are called, and finally the thread
stops executing with the return value !PTHREAD_CANCELED!. See
!pthread_exit!(3) for more information.
!pthread_cancel! sends a cancellation request to the thread denoted
by the |thread| argument.
!pthread_setcancelstate! changes the cancellation state for the
calling thread -- that is, whether cancellation requests are ignored
or not. The |state| argument is the new cancellation state: either
!PTHREAD_CANCEL_ENABLE! to enable cancellation, or
!PTHREAD_CANCEL_DISABLE! to disable cancellation (cancellation
requests are ignored). If |oldstate| is not !NULL!, the previous
cancellation state is stored in the location pointed to by |oldstate|,
and can thus be restored later by another call to
!pthread_setcancelstate!.
!pthread_setcanceltype! changes the type of responses to cancellation
requests for the calling thread: asynchronous (immediate) or deferred.
The |type| argument is the new cancellation type: either
!PTHREAD_CANCEL_ASYNCHRONOUS! to cancel the calling thread as soon as
the cancellation request is received, or !PTHREAD_CANCEL_DEFERRED! to
keep the cancellation request pending until the next cancellation
point. If |oldtype| is not !NULL!, the previous
cancellation state is stored in the location pointed to by |oldtype|,
and can thus be restored later by another call to
!pthread_setcanceltype!.
Threads are always created by !pthread_create!(3) with cancellation
enabled and deferred. That is, the initial cancellation state is
!PTHREAD_CANCEL_ENABLE! and the initial type is
!PTHREAD_CANCEL_DEFERRED!.
Cancellation points are those points in the program execution where a
test for pending cancellation requests is performed and cancellation
is executed if positive. The following POSIX threads functions
are cancellation points:
!pthread_join!(3)
.br
!pthread_cond_wait!(3)
.br
!pthread_cond_timedwait!(3)
.br
!pthread_testcancel!(3)
.br
!sem_wait!(3)
.br
!sigwait!(3)
All other POSIX threads functions are guaranteed not to be
cancellation points. That is, they never perform cancellation in
deferred cancellation mode.
!pthread_testcancel! does nothing except testing for pending
cancellation and executing it. Its purpose is to introduce explicit
checks for cancellation in long sequences of code that do not call
cancellation point functions otherwise.
.SH "RETURN VALUE"
!pthread_cancel!, !pthread_setcancelstate! and
!pthread_setcanceltype! return 0 on success and a non-zero error code
on error.
.SH ERRORS
!pthread_cancel! returns the following error code on error:
.RS
.TP
!ESRCH!
no thread could be found corresponding to that specified by the |thread| ID.
.RE
!pthread_setcancelstate! returns the following error code on error:
.RS
.TP
!EINVAL!
the |state| argument is not !PTHREAD_CANCEL_ENABLE! nor
!PTHREAD_CANCEL_DISABLE!
.RE
!pthread_setcanceltype! returns the following error code on error:
.RS
.TP
!EINVAL!
the |type| argument is not !PTHREAD_CANCEL_DEFERRED! nor
!PTHREAD_CANCEL_ASYNCHRONOUS!
.RE
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_exit!(3),
!pthread_cleanup_push!(3),
!pthread_cleanup_pop!(3).
.SH BUGS
POSIX specifies that a number of system calls (basically, all
system calls that may block, such as !read!(2), !write!(2), !wait!(2),
etc.) and library functions that may call these system calls (e.g.
!fprintf!(3)) are cancellation points. LinuxThreads is not yet
integrated enough with the C library to implement this, and thus none
of the C library functions is a cancellation point.
For system calls at least, there is a workaround. Cancellation
requests are transmitted to the target thread by sending it a
signal. That signal will interrupt all blocking system calls, causing
them to return immediately with the !EINTR! error. So, checking for
cancellation during a !read! system call, for instance, can be
achieved as follows:
.RS
.ft 3
.nf
.sp
pthread_testcancel();
retcode = read(fd, buffer, length);
pthread_testcancel();
.ft
.LP
.RE
.fi

View File

@ -0,0 +1,194 @@
.TH PTHREAD_CLEANUP 3 LinuxThreads
.XREF pthread_cleanup_pop
.XREF pthread_cleanup_push_defer_np
.XREF pthread_cleanup_pop_restore_np
.SH NAME
pthread_cleanup_push, pthread_cleanup_pop, pthread_cleanup_push_defer_np, pthread_cleanup_pop_restore_np \- install and remove cleanup handlers
.SH SYNOPSIS
#include <pthread.h>
void pthread_cleanup_push(void (*routine) (void *), void *arg);
void pthread_cleanup_pop(int execute);
void pthread_cleanup_push_defer_np(void (*routine) (void *), void *arg);
void pthread_cleanup_pop_restore_np(int execute);
.SH DESCRIPTION
Cleanup handlers are functions that get called when a thread
terminates, either by calling !pthread_exit!(3) or because of
cancellation. Cleanup handlers are installed and removed following a
stack-like discipline.
The purpose of cleanup handlers is to free the resources that a thread
may hold at the time it terminates. In particular, if a thread
exits or is cancelled while it owns a locked mutex, the mutex will
remain locked forever and prevent other threads from executing
normally. The best way to avoid this is, just before locking the
mutex, to install a cleanup handler whose effect is to unlock the
mutex. Cleanup handlers can be used similarly to free blocks allocated
with !malloc!(3) or close file descriptors on thread termination.
!pthread_cleanup_push! installs the |routine| function with argument
|arg| as a cleanup handler. From this point on to the matching
!pthread_cleanup_pop!, the function |routine| will be called with
arguments |arg| when the thread terminates, either through !pthread_exit!(3)
or by cancellation. If several cleanup handlers are active at that
point, they are called in LIFO order: the most recently installed
handler is called first.
!pthread_cleanup_pop! removes the most recently installed cleanup
handler. If the |execute| argument is not 0, it also executes the
handler, by calling the |routine| function with arguments |arg|. If
the |execute| argument is 0, the handler is only removed but not
executed.
Matching pairs of !pthread_cleanup_push! and !pthread_cleanup_pop!
must occur in the same function, at the same level of block nesting.
Actually, !pthread_cleanup_push! and !pthread_cleanup_pop! are macros,
and the expansion of !pthread_cleanup_push! introduces an open brace !{!
with the matching closing brace !}! being introduced by the expansion
of the matching !pthread_cleanup_pop!.
!pthread_cleanup_push_defer_np! is a non-portable extension that
combines !pthread_cleanup_push! and !pthread_setcanceltype!(3).
It pushes a cleanup handler just as !pthread_cleanup_push! does, but
also saves the current cancellation type and sets it to deferred
cancellation. This ensures that the cleanup mechanism is effective
even if the thread was initially in asynchronous cancellation mode.
!pthread_cleanup_pop_restore_np! pops a cleanup handler introduced by
!pthread_cleanup_push_defer_np!, and restores the cancellation type to
its value at the time !pthread_cleanup_push_defer_np! was called.
!pthread_cleanup_push_defer_np! and !pthread_cleanup_pop_restore_np!
must occur in matching pairs, at the same level of block nesting.
The following sequence
.RS
.ft 3
.nf
.sp
pthread_cleanup_push_defer_np(routine, arg);
...
pthread_cleanup_pop_defer_np(execute);
.ft
.LP
.RE
.fi
is functionally equivalent to (but more compact and more efficient than)
.RS
.ft 3
.nf
.sp
{ int oldtype;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
pthread_cleanup_push(routine, arg);
...
pthread_cleanup_pop(execute);
pthread_setcanceltype(oldtype, NULL);
}
.ft
.LP
.RE
.fi
.SH "RETURN VALUE"
None.
.SH ERRORS
None.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_exit!(3),
!pthread_cancel!(3),
!pthread_setcanceltype!(3).
.SH EXAMPLE
Here is how to lock a mutex |mut| in such a way that it will be
unlocked if the thread is canceled while |mut| is locked:
.RS
.ft 3
.nf
.sp
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);
.ft
.LP
.RE
.fi
Equivalently, the last two lines can be replaced by
.RS
.ft 3
.nf
.sp
pthread_cleanup_pop(1);
.ft
.LP
.RE
.fi
Notice that the code above is safe only in deferred cancellation mode
(see !pthread_setcanceltype!(3)). In asynchronous cancellation mode,
a cancellation can occur between !pthread_cleanup_push! and
!pthread_mutex_lock!, or between !pthread_mutex_unlock! and
!pthread_cleanup_pop!, resulting in both cases in the thread trying to
unlock a mutex not locked by the current thread. This is the main
reason why asynchronous cancellation is difficult to use.
If the code above must also work in asynchronous cancellation mode,
then it must switch to deferred mode for locking and unlocking the
mutex:
.RS
.ft 3
.nf
.sp
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_cleanup_pop(1);
pthread_setcanceltype(oldtype, NULL);
.ft
.LP
.RE
.fi
The code above can be rewritten in a more compact and more
efficient way, using the non-portable functions
!pthread_cleanup_push_defer_np! and !pthread_cleanup_pop_restore_np!:
.RS
.ft 3
.nf
.sp
pthread_cleanup_push_restore_np(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_cleanup_pop_restore_np(1);
.ft
.LP
.RE
.fi

View File

@ -0,0 +1,235 @@
.TH PTHREAD_COND 3 LinuxThreads
.XREF pthread_cond_signal
.XREF pthread_cond_broadcast
.XREF pthread_cond_wait
.XREF pthread_cond_timedwait
.XREF pthread_cond_destroy
.SH NAME
pthread_cond_init, pthread_cond_destroy, pthread_cond_signal, pthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait \- operations on conditions
.SH SYNOPSIS
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
.SH DESCRIPTION
A condition (short for ``condition variable'') is a synchronization
device that allows threads to suspend execution and relinquish the
processors until some predicate on shared data is satisfied. The basic
operations on conditions are: signal the condition (when the
predicate becomes true), and wait for the condition, suspending the
thread execution until another thread signals the condition.
A condition variable must always be associated with a mutex, to avoid
the race condition where a thread prepares to wait on a condition
variable and another thread signals the condition just before the
first thread actually waits on it.
!pthread_cond_init! initializes the condition variable |cond|, using the
condition attributes specified in |cond_attr|, or default attributes
if |cond_attr| is !NULL!. The LinuxThreads implementation supports no
attributes for conditions, hence the |cond_attr| parameter is actually
ignored.
Variables of type !pthread_cond_t! can also be initialized
statically, using the constant !PTHREAD_COND_INITIALIZER!.
!pthread_cond_signal! restarts one of the threads that are waiting on
the condition variable |cond|. If no threads are waiting on |cond|,
nothing happens. If several threads are waiting on |cond|, exactly one
is restarted, but it is not specified which.
!pthread_cond_broadcast! restarts all the threads that are waiting on
the condition variable |cond|. Nothing happens if no threads are
waiting on |cond|.
!pthread_cond_wait! atomically unlocks the |mutex| (as per
!pthread_unlock_mutex!) and waits for the condition variable |cond| to
be signaled. The thread execution is suspended and does not consume
any CPU time until the condition variable is signaled. The |mutex|
must be locked by the calling thread on entrance to
!pthread_cond_wait!. Before returning to the calling thread,
!pthread_cond_wait! re-acquires |mutex| (as per !pthread_lock_mutex!).
Unlocking the mutex and suspending on the condition variable is done
atomically. Thus, if all threads always acquire the mutex before
signaling the condition, this guarantees that the condition cannot be
signaled (and thus ignored) between the time a thread locks the mutex
and the time it waits on the condition variable.
!pthread_cond_timedwait! atomically unlocks |mutex| and waits on
|cond|, as !pthread_cond_wait! does, but it also bounds the duration
of the wait. If |cond| has not been signaled within the amount of time
specified by |abstime|, the mutex |mutex| is re-acquired and
!pthread_cond_timedwait! returns the error !ETIMEDOUT!.
The |abstime| parameter specifies an absolute time, with the same
origin as !time!(2) and !gettimeofday!(2): an |abstime| of 0
corresponds to 00:00:00 GMT, January 1, 1970.
!pthread_cond_destroy! destroys a condition variable, freeing the
resources it might hold. No threads must be waiting on the condition
variable on entrance to !pthread_cond_destroy!. In the LinuxThreads
implementation, no resources are associated with condition variables,
thus !pthread_cond_destroy! actually does nothing except checking that
the condition has no waiting threads.
.SH CANCELLATION
!pthread_cond_wait! and !pthread_cond_timedwait! are cancellation
points. If a thread is cancelled while suspended in one of these
functions, the thread immediately resumes execution, then locks again
the |mutex| argument to !pthread_cond_wait! and
!pthread_cond_timedwait!, and finally executes the cancellation.
Consequently, cleanup handlers are assured that |mutex| is locked when
they are called.
.SH "ASYNC-SIGNAL SAFETY"
The condition functions are not async-signal safe, and should not be
called from a signal handler. In particular, calling
!pthread_cond_signal! or !pthread_cond_broadcast! from a signal
handler may deadlock the calling thread.
.SH "RETURN VALUE"
All condition variable functions return 0 on success and a non-zero
error code on error.
.SH ERRORS
!pthread_cond_init!, !pthread_cond_signal!, !pthread_cond_broadcast!,
and !pthread_cond_wait! never return an error code.
The !pthread_cond_timedwait! function returns the following error codes
on error:
.RS
.TP
!ETIMEDOUT!
the condition variable was not signaled until the timeout specified by
|abstime|
.TP
!EINTR!
!pthread_cond_timedwait! was interrupted by a signal
.RE
The !pthread_cond_destroy! function returns the following error code
on error:
.RS
.TP
!EBUSY!
some threads are currently waiting on |cond|.
.RE
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_condattr_init!(3),
!pthread_mutex_lock!(3),
!pthread_mutex_unlock!(3),
!gettimeofday!(2),
!nanosleep!(2).
.SH EXAMPLE
Consider two shared variables |x| and |y|, protected by the mutex |mut|,
and a condition variable |cond| that is to be signaled whenever |x|
becomes greater than |y|.
.RS
.ft 3
.nf
.sp
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
.ft
.LP
.RE
.fi
Waiting until |x| is greater than |y| is performed as follows:
.RS
.ft 3
.nf
.sp
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);
.ft
.LP
.RE
.fi
Modifications on |x| and |y| that may cause |x| to become greater than
|y| should signal the condition if needed:
.RS
.ft 3
.nf
.sp
pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_mutex_broadcast(&cond);
pthread_mutex_unlock(&mut);
.ft
.LP
.RE
.fi
If it can be proved that at most one waiting thread needs to be waken
up (for instance, if there are only two threads communicating through
|x| and |y|), !pthread_cond_signal! can be used as a slightly more
efficient alternative to !pthread_cond_broadcast!. In doubt, use
!pthread_cond_broadcast!.
To wait for |x| to becomes greater than |y| with a timeout of 5
seconds, do:
.RS
.ft 3
.nf
.sp
struct timeval now;
struct timespec timeout;
int retcode;
pthread_mutex_lock(&mut);
gettimeofday(&now);
timeout.tv_sec = now.tv_sec + 5;
timeout.tv_nsec = now.tv_usec * 1000;
retcode = 0;
while (x <= y && retcode != ETIMEDOUT) {
retcode = pthread_cond_timedwait(&cond, &mut, &timeout);
}
if (retcode == ETIMEDOUT) {
/* timeout occurred */
} else {
/* operate on x and y */
}
pthread_mutex_unlock(&mut);
.ft
.LP
.RE
.fi

View File

@ -0,0 +1,39 @@
.TH PTHREAD_CONDATTR 3 LinuxThreads
.XREF pthread_condattr_destroy
.SH NAME
pthread_condattr_init, pthread_condattr_destroy \- condition creation attributes
.SH SYNOPSIS
#include <pthread.h>
int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_destroy(pthread_condattr_t *attr);
.SH DESCRIPTION
Condition attributes can be specified at condition creation time, by passing a
condition attribute object as second argument to !pthread_cond_init!(3).
Passing !NULL! is equivalent to passing a condition attribute object with
all attributes set to their default values.
The LinuxThreads implementation supports no attributes for
conditions. The functions on condition attributes are included only
for compliance with the POSIX standard.
!pthread_condattr_init! initializes the condition attribute object
|attr| and fills it with default values for the attributes.
!pthread_condattr_destroy! destroys a condition attribute object,
which must not be reused until it is reinitialized. Both functions do
nothing in the LinuxThreads implementation.
.SH "RETURN VALUE"
!pthread_condattr_init! and !pthread_condattr_destroy! always return 0.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_cond_init!(3).

View File

@ -0,0 +1,46 @@
.TH PTHREAD_CREATE 3 LinuxThreads
.SH NAME
pthread_create \- create a new thread
.SH SYNOPSIS
#include <pthread.h>
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
.SH DESCRIPTION
!pthread_create! creates a new thread of control that executes
concurrently with the calling thread. The new thread applies the
function |start_routine| passing it |arg| as first argument. The new
thread terminates either explicitly, by calling !pthread_exit!(3),
or implicitly, by returning from the |start_routine| function. The
latter case is equivalent to calling !pthread_exit!(3) with the result
returned by |start_routine| as exit code.
The |attr| argument specifies thread attributes to be applied to the
new thread. See !pthread_attr_init!(3) for a complete list of thread
attributes. The |attr| argument can also be !NULL!, in which case
default attributes are used: the created thread is joinable (not
detached) and has default (non real-time) scheduling policy.
.SH "RETURN VALUE"
On success, the identifier of the newly created thread is stored in
the location pointed by the |thread| argument, and a 0 is returned. On
error, a non-zero error code is returned.
.SH ERRORS
.TP
!EAGAIN!
not enough system resources to create a process for the new thread.
.TP
!EAGAIN!
more than !PTHREAD_THREADS_MAX! threads are already active.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_exit!(3),
!pthread_join!(3),
!pthread_detach!(3),
!pthread_attr_init!(3).

View File

@ -0,0 +1,44 @@
.TH PTHREAD_DETACH 3 LinuxThreads
.SH NAME
pthread_detach \- put a running thread in the detached state
.SH SYNOPSIS
#include <pthread.h>
int pthread_detach(pthread_t th);
.SH DESCRIPTION
!pthread_detach! put the thread |th| in the detached state. This
guarantees that the memory resources consumed by |th| will be freed
immediately when |th| terminates. However, this prevents other threads
from synchronizing on the termination of |th| using !pthread_join!.
A thread can be created initially in the detached state, using the
!detachstate! attribute to !pthread_create!(3). In contrast,
!pthread_detach! applies to threads created in the joinable state, and
which need to be put in the detached state later.
After !pthread_detach! completes, subsequent attempts to perform
!pthread_join! on |th| will fail. If another thread is already joining
the thread |th| at the time !pthread_detach! is called,
!pthread_detach! does nothing and leaves |th| in the joinable state.
.SH "RETURN VALUE"
On success, 0 is returned. On error, a non-zero error code is returned.
.SH ERRORS
.TP
!ESRCH!
No thread could be found corresponding to that specified by |th|
.TP
!EINVAL!
the thread |th| is already in the detached state
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_create!(3),
!pthread_join!(3),
!pthread_attr_setdetachstate!(3).

View File

@ -0,0 +1,23 @@
.TH PTHREAD_EQUAL 3 LinuxThreads
.SH NAME
pthread_equal \- compare two thread identifiers
.SH SYNOPSIS
#include <pthread.h>
int pthread_equal(pthread_t thread1, pthread_t thread2);
.SH DESCRIPTION
!pthread_equal! determines if two thread identifiers refer to the same
thread.
.SH "RETURN VALUE"
A non-zero value is returned if |thread1| and |thread2| refer to the
same thread. Otherwise, 0 is returned.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_self!(3).

View File

@ -0,0 +1,32 @@
.TH PTHREAD_EXIT 3 LinuxThreads
.SH NAME
pthread_exit \- terminate the calling thread
.SH SYNOPSIS
#include <pthread.h>
void pthread_exit(void *retval);
.SH DESCRIPTION
!pthread_exit! terminates the execution of the calling thread.
All cleanup handlers that have been set for the calling thread with
!pthread_cleanup_push!(3) are executed in reverse order (the most
recently pushed handler is executed first). Finalization functions for
thread-specific data are then called for all keys that have non-!NULL!
values associated with them in the calling thread (see
!pthread_key_create!(3)). Finally, execution of the calling thread is
stopped.
The |retval| argument is the return value of the thread. It can be
consulted from another thread using !pthread_join!(3).
.SH "RETURN VALUE"
The !pthread_exit! function never returns.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_create!(3),
!pthread_join!(3).

View File

@ -0,0 +1,70 @@
.TH PTHREAD_JOIN 3 LinuxThreads
.SH NAME
pthread_join \- wait for termination of another thread
.SH SYNOPSIS
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
.SH DESCRIPTION
!pthread_join! suspends the execution of the calling thread until the
thread identified by |th| terminates, either by calling !pthread_exit!(3)
or by being cancelled.
If |thread_return| is not !NULL!, the return value of |th| is stored
in the location pointed to by |thread_return|. The return value of
|th| is either the argument it gave to !pthread_exit!(3), or
!PTHREAD_CANCELED! if |th| was cancelled.
The joined thread !th! must be in the joinable state: it must not have
been detached using !pthread_detach!(3) or the
!PTHREAD_CREATE_DETACHED! attribute to !pthread_create!(3).
When a joinable thread terminates, its memory resources (thread
descriptor and stack) are not deallocated until another thread
performs !pthread_join! on it. Therefore, !pthread_join! must be
called once for each joinable thread created to avoid memory leaks.
At most one thread can wait for the termination of a given
thread. Calling !pthread_join! on a thread |th| on which another
thread is already waiting for termination returns an error.
.SH CANCELLATION
!pthread_join! is a cancellation point. If a thread is canceled while
suspended in !pthread_join!, the thread execution resumes immediately
and the cancellation is executed without waiting for the |th| thread
to terminate. If cancellation occurs during !pthread_join!, the |th|
thread remains not joined.
.SH "RETURN VALUE"
On success, the return value of |th| is stored in the location pointed
to by |thread_return|, and 0 is returned. On error, a non-zero error
code is returned.
.SH ERRORS
.TP
!ESRCH!
No thread could be found corresponding to that specified by |th|.
.TP
!EINVAL!
The |th| thread has been detached.
.TP
!EINVAL!
Another thread is already waiting on termination of |th|.
.TP
!EDEADLK!
The |th| argument refers to the calling thread.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_exit!(3),
!pthread_detach!(3),
!pthread_create!(3),
!pthread_attr_setdetachstate!(3),
!pthread_cleanup_push!(3),
!pthread_key_create!(3).

View File

@ -0,0 +1,151 @@
.TH PTHREAD_SPECIFIC 3 LinuxThreads
.SH NAME
pthread_key_create, pthread_key_delete, pthread_setspecific, pthread_getspecific \- management of thread-specific data
.SH SYNOPSIS
#include <pthread.h>
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
int pthread_key_delete(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *pointer);
void * pthread_getspecific(pthread_key_t key);
.SH DESCRIPTION
Programs often need global or static variables that have different
values in different threads. Since threads share one memory space,
this cannot be achieved with regular variables. Thread-specific data
is the POSIX threads answer to this need.
Each thread possesses a private memory block, the thread-specific data
area, or TSD area for short. This area is indexed by TSD keys. The TSD
area associates values of type !void *! to TSD keys. TSD keys are
common to all threads, but the value associated with a given TSD key
can be different in each thread.
For concreteness, the TSD areas can be viewed as arrays of !void *!
pointers, TSD keys as integer indices into these arrays, and the value
of a TSD key as the value of the corresponding array element in the
calling thread.
When a thread is created, its TSD area initially associates !NULL!
with all keys.
!pthread_key_create! allocates a new TSD key. The key is stored in the
location pointed to by |key|. There is a limit of !PTHREAD_KEYS_MAX!
on the number of keys allocated at a given time. The value initially
associated with the returned key is !NULL! in all currently executing
threads.
The |destr_function| argument, if not !NULL!, specifies a destructor
function associated with the key. When a thread terminates via
!pthread_exit! or by cancellation, |destr_function| is called with
arguments the value associated with the key in that thread. The
|destr_function| is not called if that value is !NULL!. The order in
which destructor functions are called at thread termination time is
unspecified.
Before the destructor function is called, the !NULL! value is
associated with the key in the current thread. A destructor function
might, however, re-associate non-!NULL! values to that key or some
other key. To deal with this, if after all the destructors have been
called for all non-!NULL! values, there are still some non-!NULL!
values with associated destructors, then the process is repeated. The
LinuxThreads implementation stops the process after
!PTHREAD_DESTRUCTOR_ITERATIONS! iterations, even if some non-!NULL!
values with associated descriptors remain. Other implementations may
loop indefinitely.
!pthread_key_delete! deallocates a TSD key. It does not check whether
non-!NULL! values are associated with that key in the currently
executing threads, nor call the destructor function associated with
the key.
!pthread_setspecific! changes the value associated with |key| in the
calling thread, storing the given |pointer| instead.
!pthread_getspecific! returns the value currently associated with
|key| in the calling thread.
.SH "RETURN VALUE"
!pthread_key_create!, !pthread_key_delete!, and !pthread_setspecific!
return 0 on success and a non-zero error code on failure. If
successful, !pthread_key_create! stores the newly allocated key in the
location pointed to by its |key| argument.
!pthread_getspecific! returns the value associated with |key| on
success, and !NULL! on error.
.SH ERRORS
!pthread_key_create! returns the following error code on error:
.RS
.TP
!EAGAIN!
!PTHREAD_KEYS_MAX! keys are already allocated
.RE
!pthread_key_delete! and !pthread_setspecific! return the following
error code on error:
.RS
.TP
!EINVAL!
|key| is not a valid, allocated TSD key
.RE
!pthread_getspecific! returns !NULL! if |key| is not a valid,
allocated TSD key.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
pthread_create(3), pthread_exit(3), pthread_testcancel(3).
.SH EXAMPLE
The following code fragment allocates a thread-specific array of 100
characters, with automatic reclaimation at thread exit:
.RS
.ft 3
.nf
.sp
/* Key for the thread-specific buffer */
static pthread_key_t buffer_key;
/* Once-only initialisation of the key */
static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;
/* Allocate the thread-specific buffer */
void buffer_alloc(void)
{
pthread_once(&buffer_key_once, buffer_key_alloc);
pthread_setspecific(buffer_key, malloc(100));
}
/* Return the thread-specific buffer */
char * get_buffer(void)
{
return (char *) pthread_getspecific(buffer_key);
}
/* Allocate the key */
static void buffer_key_alloc()
{
pthread_key_create(&buffer_key, buffer_destroy);
}
/* Free the thread-specific buffer */
static void buffer_destroy(void * buf)
{
free(buf);
}
.ft
.LP
.RE
.fi

View File

@ -0,0 +1,40 @@
.TH PTHREAD_KILL_OTHER_THREADS_NP 3 LinuxThreads
.SH NAME
pthread_kill_other_threads_np \- terminate all threads in program except calling thread
.SH SYNOPSIS
#include <pthread.h>
void pthread_kill_other_threads_np(void);
.SH DESCRIPTION
!pthread_kill_other_threads_np! is a non-portable LinuxThreads extension.
It causes all threads in the program to terminate immediately, except
the calling thread which proceeds normally. It is intended to be
called just before a thread calls one of the !exec! functions,
e.g. !execve!(2).
Termination of the other threads is not performed through
!pthread_cancel!(3) and completely bypasses the cancellation
mechanism. Hence, the current settings for cancellation state and
cancellation type are ignored, and the cleanup handlers are not
executed in the terminated threads.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!execve!(2),
!pthread_setcancelstate!(3),
!pthread_setcanceltype!(3),
!pthread_cancel!(3).
.SH BUGS
According to POSIX 1003.1c, a successful !exec*! in one of the threads
should terminate automatically all other threads in the program.
This behavior is not yet implemented in LinuxThreads.
Calling !pthread_kill_other_threads_np! before !exec*! achieves much
of the same behavior, except that if !exec*! ultimately fails, then
all other threads are already killed.

View File

@ -0,0 +1,213 @@
.TH PTHREAD_MUTEX 3 LinuxThreads
.XREF pthread_mutex_lock
.XREF pthread_mutex_unlock
.XREF pthread_mutex_trylock
.XREF pthread_mutex_destroy
.SH NAME
pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destroy \- operations on mutexes
.SH SYNOPSIS
#include <pthread.h>
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex));
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
.SH DESCRIPTION
A mutex is a MUTual EXclusion device, and is useful for protecting
shared data structures from concurrent modifications, and implementing
critical sections and monitors.
A mutex has two possible states: unlocked (not owned by any thread),
and locked (owned by one thread). A mutex can never be owned by two
different threads simultaneously. A thread attempting to lock a mutex
that is already locked by another thread is suspended until the owning
thread unlocks the mutex first.
!pthread_mutex_init! initializes the mutex object pointed to by
|mutex| according to the mutex attributes specified in |mutexattr|.
If |mutexattr| is !NULL!, default attributes are used instead.
The LinuxThreads implementation supports only one mutex attributes,
the |mutex kind|, which is either ``fast'', ``recursive'', or
``error checking''. The kind of a mutex determines whether
it can be locked again by a thread that already owns it.
The default kind is ``fast''. See !pthread_mutexattr_init!(3) for more
information on mutex attributes.
Variables of type !pthread_mutex_t! can also be initialized
statically, using the constants !PTHREAD_MUTEX_INITIALIZER! (for fast
mutexes), !PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP! (for recursive
mutexes), and !PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP! (for error checking
mutexes).
!pthread_mutex_lock! locks the given mutex. If the mutex is currently
unlocked, it becomes locked and owned by the calling thread, and
!pthread_mutex_lock! returns immediately. If the mutex is already
locked by another thread, !pthread_mutex_lock! suspends the calling
thread until the mutex is unlocked.
If the mutex is already locked by the calling thread, the behavior of
!pthread_mutex_lock! depends on the kind of the mutex. If the mutex is
of the ``fast'' kind, the calling thread is suspended until the mutex
is unlocked, thus effectively causing the calling thread to
deadlock. If the mutex is of the ``error checking'' kind,
!pthread_mutex_lock! returns immediately with the error code !EDEADLK!.
If the mutex is of the ``recursive'' kind, !pthread_mutex_lock!
succeeds and returns immediately, recording the number of times the
calling thread has locked the mutex. An equal number of
!pthread_mutex_unlock! operations must be performed before the mutex
returns to the unlocked state.
!pthread_mutex_trylock! behaves identically to !pthread_mutex_lock!,
except that it does not block the calling thread if the mutex is
already locked by another thread (or by the calling thread in the case
of a ``fast'' mutex). Instead, !pthread_mutex_trylock! returns
immediately with the error code !EBUSY!.
!pthread_mutex_unlock! unlocks the given mutex. The mutex is assumed
to be locked and owned by the calling thread on entrance to
!pthread_mutex_unlock!. If the mutex is of the ``fast'' kind,
!pthread_mutex_unlock! always returns it to the unlocked state. If it
is of the ``recursive'' kind, it decrements the locking count of the
mutex (number of !pthread_mutex_lock! operations performed on it by
the calling thread), and only when this count reaches zero is the
mutex actually unlocked.
On ``error checking'' mutexes, !pthread_mutex_unlock! actually checks
at run-time that the mutex is locked on entrance, and that it was
locked by the same thread that is now calling !pthread_mutex_unlock!.
If these conditions are not met, an error code is returned and the
mutex remains unchanged. ``Fast'' and ``recursive'' mutexes perform
no such checks, thus allowing a locked mutex to be unlocked by a
thread other than its owner. This is non-portable behavior and must
not be relied upon.
!pthread_mutex_destroy! destroys a mutex object, freeing the resources
it might hold. The mutex must be unlocked on entrance. In the
LinuxThreads implementation, no resources are associated with mutex
objects, thus !pthread_mutex_destroy! actually does nothing except
checking that the mutex is unlocked.
.SH CANCELLATION
None of the mutex functions is a cancellation point, not even
!pthread_mutex_lock!, in spite of the fact that it can suspend a
thread for arbitrary durations. This way, the status of mutexes at
cancellation points is predictable, allowing cancellation handlers to
unlock precisely those mutexes that need to be unlocked before the
thread stops executing. Consequently, threads using deferred
cancellation should never hold a mutex for extended periods of time.
.SH "ASYNC-SIGNAL SAFETY"
The mutex functions are not async-signal safe. What this means is that
they should not be called from a signal handler. In particular,
calling !pthread_mutex_lock! or !pthread_mutex_unlock! from a signal
handler may deadlock the calling thread.
.SH "RETURN VALUE"
!pthread_mutex_init! always returns 0. The other mutex functions
return 0 on success and a non-zero error code on error.
.SH ERRORS
The !pthread_mutex_lock! function returns the following error code
on error:
.RS
.TP
!EINVAL!
the mutex has not been properly initialized.
.TP
!EDEADLK!
the mutex is already locked by the calling thread
(``error checking'' mutexes only).
.RE
The !pthread_mutex_trylock! function returns the following error codes
on error:
.RS
.TP
!EBUSY!
the mutex could not be acquired because it was currently locked.
.TP
!EINVAL!
the mutex has not been properly initialized.
.RE
The !pthread_mutex_unlock! function returns the following error code
on error:
.RS
.TP
!EINVAL!
the mutex has not been properly initialized.
.TP
!EPERM!
the calling thread does not own the mutex (``error checking'' mutexes only).
.RE
The !pthread_mutex_destroy! function returns the following error code
on error:
.RS
.TP
!EBUSY!
the mutex is currently locked.
.RE
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_mutexattr_init!(3),
!pthread_mutexattr_setkind_np!(3),
!pthread_cancel!(3).
.SH EXAMPLE
A shared global variable |x| can be protected by a mutex as follows:
.RS
.ft 3
.nf
.sp
int x;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
.ft
.LP
.RE
.fi
All accesses and modifications to |x| should be bracketed by calls to
!pthread_mutex_lock! and !pthread_mutex_unlock! as follows:
.RS
.ft 3
.nf
.sp
pthread_mutex_lock(&mut);
/* operate on x */
pthread_mutex_unlock(&mut);
.ft
.LP
.RE
.fi

View File

@ -0,0 +1,84 @@
.TH PTHREAD_MUTEXATTR 3 LinuxThreads
.XREF pthread_mutexattr_destroy
.XREF pthread_mutexattr_setkind_np
.XREF pthread_mutexattr_getkind_np
.SH NAME
pthread_mutexattr_init, pthread_mutexattr_destroy, pthread_mutexattr_setkind_np, pthread_mutexattr_getkind_np \- mutex creation attributes
.SH SYNOPSIS
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind);
.SH DESCRIPTION
Mutex attributes can be specified at mutex creation time, by passing a
mutex attribute object as second argument to !pthread_mutex_init!(3).
Passing !NULL! is equivalent to passing a mutex attribute object with
all attributes set to their default values.
!pthread_mutexattr_init! initializes the mutex attribute object |attr|
and fills it with default values for the attributes.
!pthread_mutexattr_destroy! destroys a mutex attribute object, which
must not be reused until it is reinitialized. !pthread_mutexattr_destroy!
does nothing in the LinuxThreads implementation.
LinuxThreads supports only one mutex attribute: the mutex kind, which
is either !PTHREAD_MUTEX_FAST_NP! for ``fast'' mutexes,
!PTHREAD_MUTEX_RECURSIVE_NP! for ``recursive'' mutexes,
or !PTHREAD_MUTEX_ERRORCHECK_NP! for ``error checking'' mutexes.
As the !NP! suffix indicates, this is a non-portable extension to the
POSIX standard and should not be employed in portable programs.
The mutex kind determines what happens if a thread attempts to lock a
mutex it already owns with !pthread_mutex_lock!(3). If the mutex is of
the ``fast'' kind, !pthread_mutex_lock!(3) simply suspends the calling
thread forever. If the mutex is of the ``error checking'' kind,
!pthread_mutex_lock!(3) returns immediately with the error code
!EDEADLK!. If the mutex is of the ``recursive'' kind, the call to
!pthread_mutex_lock!(3) returns immediately with a success return
code. The number of times the thread owning the mutex has locked it is
recorded in the mutex. The owning thread must call
!pthread_mutex_unlock!(3) the same number of times before the mutex
returns to the unlocked state.
The default mutex kind is ``fast'', that is, !PTHREAD_MUTEX_FAST_NP!.
!pthread_mutexattr_setkind_np! sets the mutex kind attribute in |attr|
to the value specified by |kind|.
!pthread_mutexattr_getkind_np! retrieves the current value of the
mutex kind attribute in |attr| and stores it in the location pointed
to by |kind|.
.SH "RETURN VALUE"
!pthread_mutexattr_init!, !pthread_mutexattr_destroy! and
!pthread_mutexattr_getkind_np! always return 0.
!pthread_mutexattr_setkind_np! returns 0 on success and a non-zero
error code on error.
.SH ERRORS
On error, !pthread_mutexattr_setkind_np! returns the following error code:
.TP
!EINVAL!
|kind| is neither !PTHREAD_MUTEX_FAST_NP! nor !PTHREAD_MUTEX_RECURSIVE_NP!
nor !PTHREAD_MUTEX_ERRORCHECK_NP!
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_mutex_init!(3),
!pthread_mutex_lock!(3),
!pthread_mutex_unlock!(3).

View File

@ -0,0 +1,34 @@
.TH PTHREAD_ONCE 3 LinuxThreads
.SH NAME
pthread_once \- once-only initialization
.SH SYNOPSIS
#include <pthread.h>
pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
.SH DESCRIPTION
The purpose of !pthread_once! is to ensure that a piece of
initialization code is executed at most once. The |once_control|
argument points to a static or extern variable statically initialized
to !PTHREAD_ONCE_INIT!.
The first time !pthread_once! is called with a given |once_control|
argument, it calls |init_routine| with no argument and changes the
value of the |once_control| variable to record that initialization has
been performed. Subsequent calls to !pthread_once! with the same
!once_control! argument do nothing.
.SH "RETURN VALUE"
!pthread_once! always returns 0.
.SH ERRORS
None.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>

View File

@ -0,0 +1,23 @@
.TH PTHREAD_SELF 3 LinuxThreads
.SH NAME
pthread_self \- return identifier of current thread
.SH SYNOPSIS
#include <pthread.h>
pthread_t pthread_self(void);
.SH DESCRIPTION
!pthread_self! return the thread identifier for the calling thread.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_equal!(3),
!pthread_join!(3),
!pthread_detach!(3),
!pthread_setschedparam!(3),
!pthread_getschedparam!(3).

View File

@ -0,0 +1,79 @@
.TH PTHREAD_SETSCHEDPARAM 3 LinuxThreads
.XREF pthread_getschedparam
.SH NAME
pthread_setschedparam, pthread_getschedparam \- control thread scheduling parameters
.SH SYNOPSIS
#include <pthread.h>
int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param);
int pthread_getschedparam(pthread_t target_thread, int *policy, struct sched_param *param);
.SH DESCRIPTION
!pthread_setschedparam! sets the scheduling parameters for the thread
|target_thread| as indicated by |policy| and |param|. |policy| can be
either !SCHED_OTHER! (regular, non-realtime scheduling), !SCHED_RR!
(realtime, round-robin) or !SCHED_FIFO! (realtime, first-in
first-out). |param| specifies the scheduling priority for the two
realtime policies. See !sched_setpolicy!(2) for more information on
scheduling policies.
The realtime scheduling policies !SCHED_RR! and !SCHED_FIFO! are
available only to processes with superuser privileges.
!pthread_getschedparam! retrieves the scheduling policy and scheduling
parameters for the thread |target_thread| and store them in the
locations pointed to by |policy| and |param|, respectively.
.SH "RETURN VALUE"
!pthread_setschedparam! and !pthread_getschedparam! return 0 on
success and a non-zero error code on error.
.SH ERRORS
On error, !pthread_setschedparam! returns the following error codes:
.RS
.TP
!EINVAL!
|policy| is not one of !SCHED_OTHER!, !SCHED_RR!, !SCHED_FIFO!
.TP
!EINVAL!
the priority value specified by |param| is not valid for the specified policy
.TP
!EPERM!
the calling process does not have superuser permissions
.TP
!ESRCH!
the |target_thread| is invalid or has already terminated
.TP
!EFAULT!
|param| points outside the process memory space
.RE
On error, !pthread_getschedparam! returns the following error codes:
.RS
.TP
!ESRCH!
the |target_thread| is invalid or has already terminated
.TP
!EFAULT!
|policy| or |param| point outside the process memory space
.RE
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!sched_setscheduler!(2),
!sched_getscheduler!(2),
!sched_getparam!(2),
!pthread_attr_setschedpolicy!(3),
!pthread_attr_setschedparam!(3).

View File

@ -0,0 +1,123 @@
.TH PTHREAD_SIGNAL 3 LinuxThreads
.XREF pthread_kill
.XREF sigwait
.SH NAME
pthread_sigmask, pthread_kill, sigwait \- handling of signals in threads
.SH SYNOPSIS
#include <pthread.h>
.br
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask);
int pthread_kill(pthread_t thread, int signo);
int sigwait(const sigset_t *set, int *sig);
.SH DESCRIPTION
!pthread_sigmask! changes the signal mask for the calling thread as
described by the |how| and |newmask| arguments. If |oldmask| is not
!NULL!, the previous signal mask is stored in the location pointed to
by |oldmask|.
The meaning of the |how| and |newmask| arguments is the same as for
!sigprocmask!(2). If |how| is !SIG_SETMASK!, the signal mask is set to
|newmask|. If |how| is !SIG_BLOCK!, the signals specified to |newmask|
are added to the current signal mask. If |how| is !SIG_UNBLOCK!, the
signals specified to |newmask| are removed from the current signal
mask.
Recall that signal masks are set on a per-thread basis, but signal
actions and signal handlers, as set with !sigaction!(2), are shared
between all threads.
!pthread_kill! send signal number |signo| to the thread
|thread|. The signal is delivered and handled as described in
!kill!(2).
!sigwait! suspends the calling thread until one of the signals
in |set| is delivered to the calling thread. It then stores the number
of the signal received in the location pointed to by |sig| and
returns. The signals in |set| must be blocked and not ignored on
entrance to !sigwait!. If the delivered signal has a signal handler
function attached, that function is |not| called.
.SH CANCELLATION
!sigwait! is a cancellation point.
.SH "RETURN VALUE"
On success, 0 is returned. On failure, a non-zero error code is returned.
.SH ERRORS
The !pthread_sigmask! function returns the following error codes
on error:
.RS
.TP
!EINVAL!
|how| is not one of !SIG_SETMASK!, !SIG_BLOCK!, or !SIG_UNBLOCK!
.TP
!EFAULT!
|newmask| or |oldmask| point to invalid addresses
.RE
The !pthread_kill! function returns the following error codes
on error:
.RS
.TP
!EINVAL!
|signo| is not a valid signal number
.TP
!ESRCH!
the thread |thread| does not exist (e.g. it has already terminated)
.RE
The !sigwait! function never returns an error.
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!sigprocmask!(2),
!kill!(2),
!sigaction!(2),
!sigsuspend!(2).
.SH NOTES
For !sigwait! to work reliably, the signals being waited for must be
blocked in all threads, not only in the calling thread, since
otherwise the POSIX semantics for signal delivery do not guarantee
that it's the thread doing the !sigwait! that will receive the signal.
The best way to achieve this is block those signals before any threads
are created, and never unblock them in the program other than by
calling !sigwait!.
.SH BUGS
Signal handling in LinuxThreads departs significantly from the POSIX
standard. According to the standard, ``asynchronous'' (external)
signals are addressed to the whole process (the collection of all
threads), which then delivers them to one particular thread. The
thread that actually receives the signal is any thread that does
not currently block the signal.
In LinuxThreads, each thread is actually a kernel process with its own
PID, so external signals are always directed to one particular thread.
If, for instance, another thread is blocked in !sigwait! on that
signal, it will not be restarted.
The LinuxThreads implementation of !sigwait! installs dummy signal
handlers for the signals in |set| for the duration of the wait. Since
signal handlers are shared between all threads, other threads must not
attach their own signal handlers to these signals, or alternatively
they should all block these signals (which is recommended anyway --
see the Notes section).

View File

@ -0,0 +1,132 @@
.TH SEMAPHORES 3 LinuxThreads
.XREF sem_wait
.XREF sem_trywait
.XREF sem_post
.XREF sem_getvalue
.XREF sem_destroy
.SH NAME
sem_init, sem_wait, sem_trywait, sem_post, sem_getvalue, sem_destroy \- operations on semaphores
.SH SYNOPSIS
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t * sem);
int sem_trywait(sem_t * sem);
int sem_post(sem_t * sem);
int sem_getvalue(sem_t * sem, int * sval);
int sem_destroy(sem_t * sem);
.SH DESCRIPTION
This manual page documents POSIX 1003.1b semaphores, not to be
confused with SystemV semaphores as described in !ipc!(5), !semctl!(2)
and !semop!(2).
Semaphores are counters for resources shared between threads. The
basic operations on semaphores are: increment the counter atomically,
and wait until the counter is non-null and decrement it atomically.
!sem_init! initializes the semaphore object pointed to by |sem|. The
count associated with the semaphore is set initially to |value|. The
|pshared| argument indicates whether the semaphore is local to the
current process (|pshared| is zero) or is to be shared between several
processes (|pshared| is not zero). LinuxThreads currently does not
support process-shared semaphores, thus !sem_init! always returns with
error !ENOSYS! if |pshared| is not zero.
!sem_wait! suspends the calling thread until the semaphore pointed to
by |sem| has non-zero count. It then atomically decreases the
semaphore count.
!sem_trywait! is a non-blocking variant of !sem_wait!. If the
semaphore pointed to by |sem| has non-zero count, the count is
atomically decreased and !sem_trywait! immediately returns 0.
If the semaphore count is zero, !sem_trywait! immediately returns with
error !EAGAIN!.
!sem_post! atomically increases the count of the semaphore pointed to
by |sem|. This function never blocks and can safely be used in
asynchronous signal handlers.
!sem_getvalue! stores in the location pointed to by |sval| the current
count of the semaphore |sem|.
!sem_destroy! destroys a semaphore object, freeing the resources it
might hold. No threads should be waiting on the semaphore at the time
!sem_destroy! is called. In the LinuxThreads implementation, no
resources are associated with semaphore objects, thus !sem_destroy!
actually does nothing except checking that no thread is waiting on the
semaphore.
.SH CANCELLATION
!sem_wait! is a cancellation point.
.SH "ASYNC-SIGNAL SAFETY"
On processors supporting atomic compare-and-swap (Intel 486, Pentium
and later, Alpha, PowerPC, MIPS II, Motorola 68k), the !sem_post!
function is async-signal safe and can therefore be
called from signal handlers. This is the only thread synchronization
function provided by POSIX threads that is async-signal safe.
On the Intel 386 and the Sparc, the current LinuxThreads
implementation of !sem_post! is not async-signal safe by lack of the
required atomic operations.
.SH "RETURN VALUE"
The !sem_wait! and !sem_getvalue! functions always return 0.
All other semaphore functions return 0 on success and -1 on error, in
addition to writing an error code in !errno!.
.SH ERRORS
The !sem_init! function sets !errno! to the following codes on error:
.RS
.TP
!EINVAL!
|value| exceeds the maximal counter value !SEM_VALUE_MAX!
.TP
!ENOSYS!
|pshared| is not zero
.RE
The !sem_trywait! function sets !errno! to the following error code on error:
.RS
.TP
!EAGAIN!
the semaphore count is currently 0
.RE
The !sem_post! function sets !errno! to the following error code on error:
.RS
.TP
!ERANGE!
after incrementation, the semaphore value would exceed !SEM_VALUE_MAX!
(the semaphore count is left unchanged in this case)
.RE
The !sem_destroy! function sets !errno! to the following error code on error:
.RS
.TP
!EBUSY!
some threads are currently blocked waiting on the semaphore.
.RE
.SH AUTHOR
Xavier Leroy <Xavier.Leroy@inria.fr>
.SH "SEE ALSO"
!pthread_mutex_init!(3),
!pthread_cond_init!(3),
!pthread_cancel!(3),
!ipc!(5).

68
linuxthreads/man/troffprepro Executable file
View File

@ -0,0 +1,68 @@
#!/usr/local/bin/perl
$insynopsis = 0;
open(INPUT, $ARGV[0]) || die("cannot open $ARGV[0]");
open(OUTPUT, "> $ARGV[1]") || die("cannot create $ARGV[1]");
select(OUTPUT);
line:
while(<INPUT>) {
if (/^\.XREF (.*)$/) {
$xref = $1;
$_ = $ARGV[1];
m/^.*\.(([1-8]).*)$/;
$suffix = $1;
$extension = $2;
open(XREF, "> $xref.$suffix");
print XREF ".so man$extension/$ARGV[1]\n";
close(XREF);
next line;
}
if (/^\.SH/) {
$insynopsis = /SYNOPSIS/;
print $_;
next;
}
if ($insynopsis) {
if (/^#/) {
print ".B ", $_;
}
elsif (/^[a-z]/) {
chop;
# if (m/^([a-zA-Z][a-zA-Z0-9_]*\s+[a-zA-Z][a-zA-Z0-9_]*)\(/) {
# print ".B \"", $1, "\"\n";
# $_ = '(' . $';
# }
# s/([a-zA-Z][a-zA-Z0-9_]*)(\s*[,()=])/" \1 "\2/g;
s/([ *])([a-zA-Z][a-zA-Z0-9_]*)(\s*[,)=])/\1" \2 "\3/g;
print ".BI \"", $_, "\"\n";
}
else {
print $_;
}
next;
}
chop;
s/!([^!]+)!\|([^|]+)\|([^\s]*)\s*/\n.BI "\1" "\2\3"\n/g;
s/([!|])([^!|]+)\1([^\s]*)\s*/do subst($1,$2,$3)/eg;
s/^\n+//;
s/\n+$//;
s/\n\n+/\n/g;
print $_, "\n";
}
close(INPUT);
close(OUTPUT);
sub subst {
local ($a, $b, $c) = @_;
if ($c) {
"\n" . ($a eq "!" ? ".BR " : ".IR ") . "\"$b\" $c\n"
} else {
"\n" . ($a eq "!" ? ".B " : ".I ") . "\"$b\"\n"
}
}

400
linuxthreads/manager.c Normal file
View File

@ -0,0 +1,400 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* The "thread manager" thread: manages creation and termination of threads */
#include <errno.h>
#include <sched.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h> /* for select */
#include <sys/mman.h> /* for mmap */
#include <sys/time.h>
#include <sys/wait.h> /* for waitpid macros */
#include <linux/tasks.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
/* Array of active threads. Entry 0 is reserved for the initial thread. */
struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] =
{ { 0, &__pthread_initial_thread}, /* All NULLs */ };
/* Mapping from stack segment to thread descriptor. */
/* Stack segment numbers are also indices into the __pthread_handles array. */
/* Stack segment number 0 is reserved for the initial thread. */
static inline pthread_descr thread_segment(int seg)
{
return (pthread_descr)(THREAD_STACK_START_ADDRESS - (seg - 1) * STACK_SIZE)
- 1;
}
/* Flag set in signal handler to record child termination */
static volatile int terminated_children = 0;
/* Flag set when the initial thread is blocked on pthread_exit waiting
for all other threads to terminate */
static int main_thread_exiting = 0;
/* Counter used to generate unique thread identifier.
Thread identifier is pthread_threads_counter + segment. */
static pthread_t pthread_threads_counter = 0;
/* Forward declarations */
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
void * (*start_routine)(void *), void *arg,
sigset_t *mask, int father_pid);
static void pthread_handle_free(pthread_descr th);
static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode);
static void pthread_reap_children(void);
static void pthread_kill_all_threads(int sig, int main_thread_also);
/* The server thread managing requests for thread creation and termination */
int __pthread_manager(void *arg)
{
int reqfd = (int)arg;
sigset_t mask;
fd_set readfds;
struct timeval timeout;
int n;
struct pthread_request request;
/* If we have special thread_self processing, initialize it. */
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(&__pthread_manager_thread);
#endif
/* Set the error variable. */
__pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno;
__pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno;
/* Block all signals except PTHREAD_SIG_RESTART */
sigfillset(&mask);
sigdelset(&mask, PTHREAD_SIG_RESTART);
sigprocmask(SIG_SETMASK, &mask, NULL);
/* Enter server loop */
while(1) {
FD_ZERO(&readfds);
FD_SET(reqfd, &readfds);
timeout.tv_sec = 2;
timeout.tv_usec = 0;
n = __select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
/* Check for termination of the main thread */
if (getppid() == 1) {
pthread_kill_all_threads(SIGKILL, 0);
_exit(0);
}
/* Check for dead children */
if (terminated_children) {
terminated_children = 0;
pthread_reap_children();
}
/* Read and execute request */
if (n == 1 && FD_ISSET(reqfd, &readfds)) {
n = __libc_read(reqfd, (char *)&request, sizeof(request));
ASSERT(n == sizeof(request));
switch(request.req_kind) {
case REQ_CREATE:
request.req_thread->p_retcode =
pthread_handle_create((pthread_t *) &request.req_thread->p_retval,
request.req_args.create.attr,
request.req_args.create.fn,
request.req_args.create.arg,
&request.req_args.create.mask,
request.req_thread->p_pid);
restart(request.req_thread);
break;
case REQ_FREE:
pthread_handle_free(request.req_args.free.thread);
break;
case REQ_PROCESS_EXIT:
pthread_handle_exit(request.req_thread,
request.req_args.exit.code);
break;
case REQ_MAIN_THREAD_EXIT:
main_thread_exiting = 1;
if (__pthread_main_thread->p_nextlive == __pthread_main_thread) {
restart(__pthread_main_thread);
return 0;
}
break;
}
}
}
}
/* Process creation */
static int pthread_start_thread(void *arg)
{
pthread_descr self = (pthread_descr) arg;
void * outcome;
/* Initialize special thread_self processing, if any. */
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(self);
#endif
/* Make sure our pid field is initialized, just in case we get there
before our father has initialized it. */
self->p_pid = __getpid();
/* Initial signal mask is that of the creating thread. (Otherwise,
we'd just inherit the mask of the thread manager.) */
sigprocmask(SIG_SETMASK, &self->p_start_args.mask, NULL);
/* Set the scheduling policy and priority for the new thread, if needed */
if (self->p_start_args.schedpolicy >= 0)
__sched_setscheduler(self->p_pid, self->p_start_args.schedpolicy,
&self->p_start_args.schedparam);
/* Run the thread code */
outcome = self->p_start_args.start_routine(self->p_start_args.arg);
/* Exit with the given return value */
pthread_exit(outcome);
return 0;
}
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
void * (*start_routine)(void *), void *arg,
sigset_t * mask, int father_pid)
{
size_t sseg;
int pid;
pthread_descr new_thread;
pthread_t new_thread_id;
int i;
/* Find a free stack segment for the current stack */
for (sseg = 1; ; sseg++) {
if (sseg >= PTHREAD_THREADS_MAX) return EAGAIN;
if (__pthread_handles[sseg].h_descr != NULL) continue;
new_thread = thread_segment(sseg);
/* Allocate space for stack and thread descriptor. */
if (mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0)
!= MAP_FAILED) break;
/* It seems part of this segment is already mapped. Try the next. */
}
/* Allocate new thread identifier */
pthread_threads_counter += PTHREAD_THREADS_MAX;
new_thread_id = sseg + pthread_threads_counter;
/* Initialize the thread descriptor */
new_thread->p_nextwaiting = NULL;
new_thread->p_tid = new_thread_id;
new_thread->p_priority = 0;
new_thread->p_spinlock = &(__pthread_handles[sseg].h_spinlock);
new_thread->p_signal = 0;
new_thread->p_signal_jmp = NULL;
new_thread->p_cancel_jmp = NULL;
new_thread->p_terminated = 0;
new_thread->p_detached = attr == NULL ? 0 : attr->detachstate;
new_thread->p_exited = 0;
new_thread->p_retval = NULL;
new_thread->p_joining = NULL;
new_thread->p_cleanup = NULL;
new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE;
new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED;
new_thread->p_canceled = 0;
new_thread->p_errnop = &new_thread->p_errno;
new_thread->p_errno = 0;
new_thread->p_h_errnop = &new_thread->p_h_errno;
new_thread->p_h_errno = 0;
for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
new_thread->p_specific[i] = NULL;
/* Initialize the thread handle */
__pthread_handles[sseg].h_spinlock = 0; /* should already be 0 */
__pthread_handles[sseg].h_descr = new_thread;
/* Determine scheduling parameters for the thread */
new_thread->p_start_args.schedpolicy = -1;
if (attr != NULL) {
switch(attr->inheritsched) {
case PTHREAD_EXPLICIT_SCHED:
new_thread->p_start_args.schedpolicy = attr->schedpolicy;
new_thread->p_start_args.schedparam = attr->schedparam;
break;
case PTHREAD_INHERIT_SCHED:
/* schedpolicy doesn't need to be set, only get priority */
__sched_getparam(father_pid, &new_thread->p_start_args.schedparam);
break;
}
new_thread->p_priority =
new_thread->p_start_args.schedparam.sched_priority;
}
/* Finish setting up arguments to pthread_start_thread */
new_thread->p_start_args.start_routine = start_routine;
new_thread->p_start_args.arg = arg;
new_thread->p_start_args.mask = *mask;
/* Do the cloning */
pid = __clone(pthread_start_thread, (void **) new_thread,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
PTHREAD_SIG_RESTART,
new_thread);
/* Check if cloning succeeded */
if (pid == -1) {
/* Free the stack */
munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
INITIAL_STACK_SIZE);
__pthread_handles[sseg].h_descr = NULL;
return errno;
}
/* Insert new thread in doubly linked list of active threads */
new_thread->p_prevlive = __pthread_main_thread;
new_thread->p_nextlive = __pthread_main_thread->p_nextlive;
__pthread_main_thread->p_nextlive->p_prevlive = new_thread;
__pthread_main_thread->p_nextlive = new_thread;
/* Set pid field of the new thread, in case we get there before the
child starts. */
new_thread->p_pid = pid;
/* We're all set */
*thread = new_thread_id;
return 0;
}
/* Free the resources of a thread. */
static void pthread_free(pthread_descr th)
{
pthread_handle handle;
ASSERT(th->p_exited);
/* Make the handle invalid */
handle = thread_handle(th->p_tid);
acquire(&handle->h_spinlock);
handle->h_descr = NULL;
release(&handle->h_spinlock);
/* If initial thread, nothing to free */
if (th == &__pthread_initial_thread) return;
/* Free the stack and thread descriptor area */
munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE);
}
/* Handle threads that have exited */
static void pthread_exited(pid_t pid)
{
pthread_descr th;
int detached;
/* Find thread with that pid */
for (th = __pthread_main_thread->p_nextlive;
th != __pthread_main_thread;
th = th->p_nextlive) {
if (th->p_pid == pid) {
/* Remove thread from list of active threads */
th->p_nextlive->p_prevlive = th->p_prevlive;
th->p_prevlive->p_nextlive = th->p_nextlive;
/* Mark thread as exited, and if detached, free its resources */
acquire(th->p_spinlock);
th->p_exited = 1;
detached = th->p_detached;
release(th->p_spinlock);
if (detached) pthread_free(th);
break;
}
}
/* If all threads have exited and the main thread is pending on a
pthread_exit, wake up the main thread and terminate ourselves. */
if (main_thread_exiting &&
__pthread_main_thread->p_nextlive == __pthread_main_thread) {
restart(__pthread_main_thread);
_exit(0);
}
}
static void pthread_reap_children(void)
{
pid_t pid;
int status;
while ((pid = __libc_waitpid(-1, &status, WNOHANG | __WCLONE)) > 0) {
pthread_exited(pid);
if (WIFSIGNALED(status)) {
/* If a thread died due to a signal, send the same signal to
all other threads, including the main thread. */
pthread_kill_all_threads(WTERMSIG(status), 1);
_exit(0);
}
}
}
/* Free the resources of a thread */
static void pthread_handle_free(pthread_descr th)
{
acquire(th->p_spinlock);
if (th->p_exited) {
release(th->p_spinlock);
pthread_free(th);
} else {
/* The Unix process of the thread is still running.
Mark the thread as detached so that the thread manager will
deallocate its resources when the Unix process exits. */
th->p_detached = 1;
release(th->p_spinlock);
}
}
/* Send a signal to all running threads */
static void pthread_kill_all_threads(int sig, int main_thread_also)
{
pthread_descr th;
for (th = __pthread_main_thread->p_nextlive;
th != __pthread_main_thread;
th = th->p_nextlive) {
kill(th->p_pid, sig);
}
if (main_thread_also) {
kill(__pthread_main_thread->p_pid, sig);
}
}
/* Process-wide exit() */
static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode)
{
pthread_descr th;
__pthread_exit_requested = 1;
__pthread_exit_code = exitcode;
/* Send the CANCEL signal to all running threads, including the main
thread, but excluding the thread from which the exit request originated
(that thread must complete the exit, e.g. calling atexit functions
and flushing stdio buffers). */
for (th = issuing_thread->p_nextlive;
th != issuing_thread;
th = th->p_nextlive) {
kill(th->p_pid, PTHREAD_SIG_CANCEL);
}
/* Now, wait for all these threads, so that they don't become zombies
and their times are properly added to the thread manager's times. */
for (th = issuing_thread->p_nextlive;
th != issuing_thread;
th = th->p_nextlive) {
waitpid(th->p_pid, NULL, __WCLONE);
}
restart(issuing_thread);
_exit(0);
}
/* Handler for PTHREAD_SIG_RESTART in thread manager thread */
void __pthread_manager_sighandler(int sig)
{
terminated_children = 1;
}

234
linuxthreads/mutex.c Normal file
View File

@ -0,0 +1,234 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Mutexes */
#include <errno.h>
#include <sched.h>
#include <stddef.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "queue.h"
#include "restart.h"
int __pthread_mutex_init(pthread_mutex_t * mutex,
const pthread_mutexattr_t * mutex_attr)
{
mutex->m_spinlock = 0;
mutex->m_count = 0;
mutex->m_owner = NULL;
mutex->m_kind =
mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->mutexkind;
queue_init(&mutex->m_waiting);
return 0;
}
weak_alias (__pthread_mutex_init, pthread_mutex_init)
int __pthread_mutex_destroy(pthread_mutex_t * mutex)
{
int count;
acquire(&mutex->m_spinlock);
count = mutex->m_count;
release(&mutex->m_spinlock);
if (count > 0) return EBUSY;
return 0;
}
weak_alias (__pthread_mutex_destroy, pthread_mutex_destroy)
int __pthread_mutex_trylock(pthread_mutex_t * mutex)
{
pthread_descr self;
acquire(&mutex->m_spinlock);
switch(mutex->m_kind) {
case PTHREAD_MUTEX_FAST_NP:
if (mutex->m_count == 0) {
mutex->m_count = 1;
release(&mutex->m_spinlock);
return 0;
}
break;
case PTHREAD_MUTEX_RECURSIVE_NP:
self = thread_self();
if (mutex->m_count == 0 || mutex->m_owner == self) {
mutex->m_count++;
mutex->m_owner = self;
release(&mutex->m_spinlock);
return 0;
}
break;
case PTHREAD_MUTEX_ERRORCHECK_NP:
self = thread_self();
if (mutex->m_count == 0) {
mutex->m_count = 1;
mutex->m_owner = self;
release(&mutex->m_spinlock);
return 0;
}
break;
default:
release(&mutex->m_spinlock);
return EINVAL;
}
release(&mutex->m_spinlock);
return EBUSY;
}
weak_alias (__pthread_mutex_trylock, pthread_mutex_trylock)
int __pthread_mutex_lock(pthread_mutex_t * mutex)
{
pthread_descr self;
while(1) {
acquire(&mutex->m_spinlock);
switch(mutex->m_kind) {
case PTHREAD_MUTEX_FAST_NP:
if (mutex->m_count == 0) {
mutex->m_count = 1;
release(&mutex->m_spinlock);
return 0;
}
self = thread_self();
break;
case PTHREAD_MUTEX_RECURSIVE_NP:
self = thread_self();
if (mutex->m_count == 0 || mutex->m_owner == self) {
mutex->m_count++;
mutex->m_owner = self;
release(&mutex->m_spinlock);
return 0;
}
break;
case PTHREAD_MUTEX_ERRORCHECK_NP:
self = thread_self();
if (mutex->m_count == 0) {
mutex->m_count = 1;
mutex->m_owner = self;
release(&mutex->m_spinlock);
return 0;
} else if (mutex->m_owner == self) {
release(&mutex->m_spinlock);
return EDEADLK;
}
break;
default:
release(&mutex->m_spinlock);
return EINVAL;
}
/* Suspend ourselves, then try again */
enqueue(&mutex->m_waiting, self);
release(&mutex->m_spinlock);
suspend(self); /* This is not a cancellation point */
}
}
weak_alias (__pthread_mutex_lock, pthread_mutex_lock)
int __pthread_mutex_unlock(pthread_mutex_t * mutex)
{
pthread_descr th;
acquire(&mutex->m_spinlock);
switch (mutex->m_kind) {
case PTHREAD_MUTEX_FAST_NP:
mutex->m_count = 0;
break;
case PTHREAD_MUTEX_RECURSIVE_NP:
mutex->m_count--;
if (mutex->m_count > 0) {
release(&mutex->m_spinlock);
return 0;
}
mutex->m_count = 0; /* so that excess unlocks do not break everything */
break;
case PTHREAD_MUTEX_ERRORCHECK_NP:
if (mutex->m_count == 0 || mutex->m_owner != thread_self()) {
release(&mutex->m_spinlock);
return EPERM;
}
mutex->m_count = 0;
break;
default:
release(&mutex->m_spinlock);
return EINVAL;
}
th = dequeue(&mutex->m_waiting);
release(&mutex->m_spinlock);
if (th != NULL) restart(th);
return 0;
}
weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
{
attr->mutexkind = PTHREAD_MUTEX_FAST_NP;
return 0;
}
weak_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
{
return 0;
}
weak_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
{
if (kind != PTHREAD_MUTEX_FAST_NP
&& kind != PTHREAD_MUTEX_RECURSIVE_NP
&& kind != PTHREAD_MUTEX_ERRORCHECK_NP)
return EINVAL;
attr->mutexkind = kind;
return 0;
}
weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
int __pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind)
{
*kind = attr->mutexkind;
return 0;
}
weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np)
/* Once-only execution */
static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER;
enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
{
/* Test without locking first for speed */
if (*once_control == DONE) return 0;
/* Lock and test again */
pthread_mutex_lock(&once_masterlock);
/* If init_routine is being called from another routine, wait until
it completes. */
while (*once_control == IN_PROGRESS) {
pthread_cond_wait(&once_finished, &once_masterlock);
}
/* Here *once_control is stable and either NEVER or DONE. */
if (*once_control == NEVER) {
*once_control = IN_PROGRESS;
pthread_mutex_unlock(&once_masterlock);
init_routine();
pthread_mutex_lock(&once_masterlock);
*once_control = DONE;
pthread_cond_broadcast(&once_finished);
}
pthread_mutex_unlock(&once_masterlock);
return 0;
}
weak_alias (__pthread_once, pthread_once)

97
linuxthreads/ptfork.c Normal file
View File

@ -0,0 +1,97 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* The "atfork" stuff */
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include "pthread.h"
#include "internals.h"
struct handler_list {
void (*handler)(void);
struct handler_list * next;
};
static pthread_mutex_t pthread_atfork_lock = PTHREAD_MUTEX_INITIALIZER;
static struct handler_list * pthread_atfork_prepare = NULL;
static struct handler_list * pthread_atfork_parent = NULL;
static struct handler_list * pthread_atfork_child = NULL;
static void pthread_insert_list(struct handler_list ** list,
void (*handler)(void),
struct handler_list * newlist,
int at_end)
{
if (handler == NULL) return;
if (at_end) {
while(*list != NULL) list = &((*list)->next);
}
newlist->handler = handler;
newlist->next = *list;
*list = newlist;
}
struct handler_list_block {
struct handler_list prepare, parent, child;
};
int __pthread_atfork(void (*prepare)(void),
void (*parent)(void),
void (*child)(void))
{
struct handler_list_block * block =
(struct handler_list_block *) malloc(sizeof(struct handler_list_block));
if (block == NULL) return ENOMEM;
pthread_mutex_lock(&pthread_atfork_lock);
/* "prepare" handlers are called in LIFO */
pthread_insert_list(&pthread_atfork_prepare, prepare, &block->prepare, 0);
/* "parent" handlers are called in FIFO */
pthread_insert_list(&pthread_atfork_parent, parent, &block->parent, 1);
/* "child" handlers are called in FIFO */
pthread_insert_list(&pthread_atfork_child, child, &block->child, 1);
pthread_mutex_unlock(&pthread_atfork_lock);
return 0;
}
weak_alias (__pthread_atfork, pthread_atfork)
static inline void pthread_call_handlers(struct handler_list * list)
{
for (/*nothing*/; list != NULL; list = list->next) (list->handler)();
}
extern int __fork(void);
int fork(void)
{
int pid;
struct handler_list * prepare, * child, * parent;
pthread_mutex_lock(&pthread_atfork_lock);
prepare = pthread_atfork_prepare;
child = pthread_atfork_child;
parent = pthread_atfork_parent;
pthread_mutex_unlock(&pthread_atfork_lock);
pthread_call_handlers(prepare);
pid = __fork();
if (pid == 0) {
__pthread_reset_main_thread();
__fresetlockfiles();
pthread_call_handlers(child);
} else {
pthread_call_handlers(parent);
}
return pid;
}

445
linuxthreads/pthread.c Normal file
View File

@ -0,0 +1,445 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Thread creation, initialization, and basic low-level routines */
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
/* Descriptor of the initial thread */
struct _pthread_descr_struct __pthread_initial_thread = {
&__pthread_initial_thread, /* pthread_descr p_nextlive */
&__pthread_initial_thread, /* pthread_descr p_prevlive */
NULL, /* pthread_descr p_nextwaiting */
PTHREAD_THREADS_MAX, /* pthread_t p_tid */
0, /* int p_pid */
0, /* int p_priority */
&__pthread_handles[0].h_spinlock, /* int * p_spinlock */
0, /* int p_signal */
NULL, /* sigjmp_buf * p_signal_buf */
NULL, /* sigjmp_buf * p_cancel_buf */
0, /* char p_terminated */
0, /* char p_detached */
0, /* char p_exited */
NULL, /* void * p_retval */
0, /* int p_retval */
NULL, /* pthread_descr p_joining */
NULL, /* struct _pthread_cleanup_buffer * p_cleanup */
0, /* char p_cancelstate */
0, /* char p_canceltype */
0, /* char p_canceled */
NULL, /* int *p_errnop */
0, /* int p_errno */
NULL, /* int *p_h_errnop */
0, /* int p_h_errno */
PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
{NULL} /* void * p_specific[PTHREAD_KEYS_MAX] */
};
/* Descriptor of the manager thread; none of this is used but the error
variables and the address for identification. */
struct _pthread_descr_struct __pthread_manager_thread = {
NULL, /* pthread_descr p_nextlive */
NULL, /* pthread_descr p_prevlive */
NULL, /* pthread_descr p_nextwaiting */
0, /* int p_tid */
0, /* int p_pid */
0, /* int p_priority */
NULL, /* int * p_spinlock */
0, /* int p_signal */
NULL, /* sigjmp_buf * p_signal_buf */
NULL, /* sigjmp_buf * p_cancel_buf */
0, /* char p_terminated */
0, /* char p_detached */
0, /* char p_exited */
NULL, /* void * p_retval */
0, /* int p_retval */
NULL, /* pthread_descr p_joining */
NULL, /* struct _pthread_cleanup_buffer * p_cleanup */
0, /* char p_cancelstate */
0, /* char p_canceltype */
0, /* char p_canceled */
NULL, /* int *p_errnop */
0, /* int p_errno */
NULL, /* int *p_h_errnop */
0, /* int p_h_errno */
PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
{NULL} /* void * p_specific[PTHREAD_KEYS_MAX] */
};
/* Pointer to the main thread (the father of the thread manager thread) */
/* Originally, this is the initial thread, but this changes after fork() */
pthread_descr __pthread_main_thread = &__pthread_initial_thread;
/* Limit between the stack of the initial thread (above) and the
stacks of other threads (below). Aligned on a STACK_SIZE boundary. */
char *__pthread_initial_thread_bos = NULL;
/* File descriptor for sending requests to the thread manager. */
/* Initially -1, meaning that the thread manager is not running. */
int __pthread_manager_request = -1;
/* Other end of the pipe for sending requests to the thread manager. */
int __pthread_manager_reader;
/* PID of thread manager */
static int __pthread_manager_pid;
/* Limits of the thread manager stack */
char *__pthread_manager_thread_bos = NULL;
char *__pthread_manager_thread_tos = NULL;
/* For process-wide exit() */
int __pthread_exit_requested = 0;
int __pthread_exit_code = 0;
/* Signal numbers used for the communication. */
int __pthread_sig_restart;
int __pthread_sig_cancel;
/* These variables are used by the setup code. */
extern int _errno;
extern int _h_errno;
/* Forward declarations */
static void pthread_exit_process(int retcode, void *arg);
static void pthread_handle_sigcancel(int sig);
/* Initialize the pthread library.
Initialization is split in two functions:
- a constructor function that blocks the PTHREAD_SIG_RESTART signal
(must do this very early, since the program could capture the signal
mask with e.g. sigsetjmp before creating the first thread);
- a regular function called from pthread_create when needed. */
static void pthread_initialize(void) __attribute__((constructor));
static void pthread_initialize(void)
{
struct sigaction sa;
sigset_t mask;
/* If already done (e.g. by a constructor called earlier!), bail out */
if (__pthread_initial_thread_bos != NULL) return;
/* For the initial stack, reserve at least STACK_SIZE bytes of stack
below the current stack address, and align that on a
STACK_SIZE boundary. */
__pthread_initial_thread_bos =
(char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));
/* Update the descriptor for the initial thread. */
__pthread_initial_thread.p_pid = __getpid();
/* If we have special thread_self processing, initialize that for the
main thread now. */
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(&__pthread_initial_thread);
#endif
/* The errno/h_errno variable of the main thread are the global ones. */
__pthread_initial_thread.p_errnop = &_errno;
__pthread_initial_thread.p_h_errnop = &_h_errno;
/* Allocate the signals used. */
__pthread_sig_restart = __libc_allocate_rtsig (1);
__pthread_sig_cancel = __libc_allocate_rtsig (1);
if (__pthread_sig_restart < 0 || __pthread_sig_cancel < 0)
{
/* The kernel does not support real-time signals. Use as before
the available signals in the fixed set. */
__pthread_sig_restart = SIGUSR1;
__pthread_sig_cancel = SIGUSR2;
}
/* Setup signal handlers for the initial thread.
Since signal handlers are shared between threads, these settings
will be inherited by all other threads. */
sa.sa_handler = __pthread_sighandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* does not matter for regular threads, but
better for the thread manager */
sigaction(PTHREAD_SIG_RESTART, &sa, NULL);
sa.sa_handler = pthread_handle_sigcancel;
sa.sa_flags = 0;
sigaction(PTHREAD_SIG_CANCEL, &sa, NULL);
/* Initially, block PTHREAD_SIG_RESTART. Will be unblocked on demand. */
sigemptyset(&mask);
sigaddset(&mask, PTHREAD_SIG_RESTART);
sigprocmask(SIG_BLOCK, &mask, NULL);
/* Register an exit function to kill all other threads. */
/* Do it early so that user-registered atexit functions are called
before pthread_exit_process. */
__on_exit(pthread_exit_process, NULL);
}
static int pthread_initialize_manager(void)
{
int manager_pipe[2];
/* If basic initialization not done yet (e.g. we're called from a
constructor run before our constructor), do it now */
if (__pthread_initial_thread_bos == NULL) pthread_initialize();
/* Setup stack for thread manager */
__pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE);
if (__pthread_manager_thread_bos == NULL) return -1;
__pthread_manager_thread_tos =
__pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE;
/* Setup pipe to communicate with thread manager */
if (pipe(manager_pipe) == -1) {
free(__pthread_manager_thread_bos);
return -1;
}
__pthread_manager_request = manager_pipe[1]; /* writing end */
__pthread_manager_reader = manager_pipe[0]; /* reading end */
/* Start the thread manager */
__pthread_manager_pid =
__clone(__pthread_manager, (void **) __pthread_manager_thread_tos,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
(void *)(long)manager_pipe[0]);
if (__pthread_manager_pid == -1) {
free(__pthread_manager_thread_bos);
__libc_close(manager_pipe[0]);
__libc_close(manager_pipe[1]);
__pthread_manager_request = -1;
return -1;
}
return 0;
}
/* Thread creation */
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void * (*start_routine)(void *), void *arg)
{
pthread_descr self = thread_self();
struct pthread_request request;
if (__pthread_manager_request < 0) {
if (pthread_initialize_manager() < 0) return EAGAIN;
}
request.req_thread = self;
request.req_kind = REQ_CREATE;
request.req_args.create.attr = attr;
request.req_args.create.fn = start_routine;
request.req_args.create.arg = arg;
sigprocmask(SIG_SETMASK, (const sigset_t *) NULL,
&request.req_args.create.mask);
__libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
suspend(self);
if (self->p_retcode == 0) *thread = (pthread_t) self->p_retval;
return self->p_retcode;
}
/* Simple operations on thread identifiers */
pthread_t pthread_self(void)
{
pthread_descr self = thread_self();
return self->p_tid;
}
int pthread_equal(pthread_t thread1, pthread_t thread2)
{
return thread1 == thread2;
}
/* Thread scheduling */
int pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param *param)
{
pthread_handle handle = thread_handle(thread);
pthread_descr th;
acquire(&handle->h_spinlock);
if (invalid_handle(handle, thread)) {
release(&handle->h_spinlock);
return ESRCH;
}
th = handle->h_descr;
if (__sched_setscheduler(th->p_pid, policy, param) == -1) {
release(&handle->h_spinlock);
return errno;
}
th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority;
release(&handle->h_spinlock);
return 0;
}
int pthread_getschedparam(pthread_t thread, int *policy,
struct sched_param *param)
{
pthread_handle handle = thread_handle(thread);
int pid, pol;
acquire(&handle->h_spinlock);
if (invalid_handle(handle, thread)) {
release(&handle->h_spinlock);
return ESRCH;
}
pid = handle->h_descr->p_pid;
release(&handle->h_spinlock);
pol = __sched_getscheduler(pid);
if (pol == -1) return errno;
if (__sched_getparam(pid, param) == -1) return errno;
*policy = pol;
return 0;
}
/* Process-wide exit() request */
static void pthread_exit_process(int retcode, void *arg)
{
struct pthread_request request;
pthread_descr self = thread_self();
if (__pthread_manager_request >= 0) {
request.req_thread = self;
request.req_kind = REQ_PROCESS_EXIT;
request.req_args.exit.code = retcode;
__libc_write(__pthread_manager_request,
(char *) &request, sizeof(request));
suspend(self);
/* Main thread should accumulate times for thread manager and its
children, so that timings for main thread account for all threads. */
if (self == __pthread_main_thread)
waitpid(__pthread_manager_pid, NULL, __WCLONE);
}
}
/* The handler for the RESTART signal just records the signal received
in the thread descriptor, and optionally performs a siglongjmp
(for pthread_cond_timedwait). Also used in sigwait.
For the thread manager thread, redirect the signal to
__pthread_manager_sighandler. */
void __pthread_sighandler(int sig)
{
pthread_descr self = thread_self();
if (self == &__pthread_manager_thread) {
__pthread_manager_sighandler(sig);
} else {
self->p_signal = sig;
if (self->p_signal_jmp != NULL) siglongjmp(*self->p_signal_jmp, 1);
}
}
/* The handler for the CANCEL signal checks for cancellation
(in asynchronous mode) and for process-wide exit and exec requests. */
static void pthread_handle_sigcancel(int sig)
{
pthread_descr self = thread_self();
sigjmp_buf * jmpbuf;
if (__pthread_exit_requested) {
/* Main thread should accumulate times for thread manager and its
children, so that timings for main thread account for all threads. */
if (self == __pthread_main_thread)
waitpid(__pthread_manager_pid, NULL, __WCLONE);
_exit(__pthread_exit_code);
}
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
if (self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_exit(PTHREAD_CANCELED);
jmpbuf = self->p_cancel_jmp;
if (jmpbuf != NULL) {
self->p_cancel_jmp = NULL;
siglongjmp(*jmpbuf, 1);
}
}
}
/* Reset the state of the thread machinery after a fork().
Close the pipe used for requests and set the main thread to the forked
thread.
Notice that we can't free the stack segments, as the forked thread
may hold pointers into them. */
void __pthread_reset_main_thread()
{
pthread_descr self = thread_self();
if (__pthread_manager_request != -1) {
/* Free the thread manager stack */
free(__pthread_manager_thread_bos);
__pthread_manager_thread_bos = __pthread_manager_thread_tos = NULL;
/* Close the two ends of the pipe */
__libc_close(__pthread_manager_request);
__libc_close(__pthread_manager_reader);
__pthread_manager_request = __pthread_manager_reader = -1;
}
/* Update the pid of the main thread */
self->p_pid = __getpid();
/* Make the forked thread the main thread */
__pthread_main_thread = self;
self->p_nextlive = self;
self->p_prevlive = self;
/* Now this thread modifies the global variables. */
self->p_errnop = &_errno;
self->p_h_errnop = &_h_errno;
}
/* Process-wide exec() request */
void __pthread_kill_other_threads_np(void)
{
/* Terminate all other threads and thread manager */
pthread_exit_process(0, NULL);
/* Make current thread the main thread in case the calling thread
changes its mind, does not exec(), and creates new threads instead. */
__pthread_reset_main_thread();
}
weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np)
/* Debugging aid */
#ifdef DEBUG
#include <stdarg.h>
void __pthread_message(char * fmt, long arg)
{
char buffer[1024];
va_list args;
sprintf(buffer, "%05d : ", __getpid());
va_start(args, fmt);
vsnprintf(buffer + 8, sizeof(buffer) - 8, fmt, args);
va_end(args);
__libc_write(2, buffer, strlen(buffer));
}
#endif
#ifndef PIC
/* We need a hook to force the cancelation wrappers to be linked in when
static libpthread is used. */
extern const int __pthread_provide_wrappers;
static const int *const __pthread_require_wrappers =
&__pthread_provide_wrappers;
#endif

62
linuxthreads/queue.h Normal file
View File

@ -0,0 +1,62 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Waiting queues */
typedef struct _pthread_queue pthread_queue;
static inline void queue_init(pthread_queue * q)
{
q->head = q->tail = NULL;
}
static inline void enqueue(pthread_queue * q, pthread_descr th)
{
int prio;
pthread_descr * elt;
ASSERT(th->p_nextwaiting == NULL);
if (q->tail == NULL) {
q->head = th;
q->tail = th;
return;
}
prio = th->p_priority;
if (prio > 0) {
/* Insert in queue according to priority order */
for (elt = &(q->head); *elt != NULL; elt = &((*elt)->p_nextwaiting)) {
if (prio > (*elt)->p_priority) {
th->p_nextwaiting = *elt;
*elt = th;
return;
}
}
}
/* Priority is no greater than any thread in the queue.
Insert at end of queue */
q->tail->p_nextwaiting = th;
q->tail = th;
}
static inline pthread_descr dequeue(pthread_queue * q)
{
pthread_descr th;
th = q->head;
if (th != NULL) {
q->head = th->p_nextwaiting;
if (q->head == NULL) q->tail = NULL;
th->p_nextwaiting = NULL;
}
return th;
}

57
linuxthreads/restart.h Normal file
View File

@ -0,0 +1,57 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
#include <signal.h>
/* Primitives for controlling thread execution */
static inline void restart(pthread_descr th)
{
kill(th->p_pid, PTHREAD_SIG_RESTART);
}
static inline void suspend(pthread_descr self)
{
sigset_t mask;
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */
do {
self->p_signal = 0;
sigsuspend(&mask); /* Wait for signal */
} while (self->p_signal != PTHREAD_SIG_RESTART);
}
static inline void suspend_with_cancellation(pthread_descr self)
{
sigset_t mask;
sigjmp_buf jmpbuf;
sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */
/* No need to save the signal mask, we'll restore it ourselves */
if (sigsetjmp(jmpbuf, 0) == 0) {
self->p_cancel_jmp = &jmpbuf;
if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
do {
self->p_signal = 0;
sigsuspend(&mask); /* Wait for a signal */
} while (self->p_signal != PTHREAD_SIG_RESTART);
}
self->p_cancel_jmp = NULL;
} else {
sigaddset(&mask, PTHREAD_SIG_RESTART); /* Reblock the restart signal */
sigprocmask(SIG_SETMASK, &mask, NULL);
}
}

276
linuxthreads/rwlock.c Normal file
View File

@ -0,0 +1,276 @@
/* Read-write lock implementation.
Copyright (C) 1998 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Xavier Leroy <Xavier.Leroy@inria.fr>
and Ulrich Drepper <drepper@cygnus.com>, 1998.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <errno.h>
#include <pthread.h>
#include "internals.h"
#include "queue.h"
#include "restart.h"
#include "spinlock.h"
int
pthread_rwlock_init (pthread_rwlock_t *rwlock,
const pthread_rwlockattr_t *attr)
{
rwlock->rw_spinlock = 0;
rwlock->rw_readers = 0;
rwlock->rw_writer = NULL;
queue_init(&rwlock->rw_read_waiting);
queue_init(&rwlock->rw_write_waiting);
if (attr == NULL)
{
rwlock->rw_kind = PTHREAD_RWLOCK_DEFAULT_NP;
rwlock->rw_pshared = PTHREAD_PROCESS_PRIVATE;
}
else
{
rwlock->rw_kind = attr->lockkind;
rwlock->rw_pshared = attr->pshared;
}
return 0;
}
int
pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
{
int readers;
_pthread_descr writer;
acquire (&rwlock->rw_spinlock);
readers = rwlock->rw_readers;
writer = rwlock->rw_writer;
release (&rwlock->rw_spinlock);
if (readers > 0 || writer != NULL)
return EBUSY;
return 0;
}
int
pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
{
pthread_descr self;
while (1)
{
acquire (&rwlock->rw_spinlock);
if (rwlock->rw_writer == NULL
|| (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
&& rwlock->rw_readers != 0))
/* We can add a reader lock. */
break;
/* Suspend ourselves, then try again */
self = thread_self ();
enqueue (&rwlock->rw_read_waiting, self);
release (&rwlock->rw_spinlock);
suspend (self); /* This is not a cancellation point */
}
++rwlock->rw_readers;
release (&rwlock->rw_spinlock);
return 0;
}
int
pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
{
int result = EBUSY;
acquire (&rwlock->rw_spinlock);
if (rwlock->rw_writer == NULL
|| (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
&& rwlock->rw_readers != 0))
{
++rwlock->rw_readers;
result = 0;
}
release (&rwlock->rw_spinlock);
return result;
}
int
pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
{
pthread_descr self = thread_self ();
while(1)
{
acquire (&rwlock->rw_spinlock);
if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL)
{
rwlock->rw_writer = self;
release (&rwlock->rw_spinlock);
return 0;
}
/* Suspend ourselves, then try again */
enqueue (&rwlock->rw_write_waiting, self);
release (&rwlock->rw_spinlock);
suspend (self); /* This is not a cancellation point */
}
}
int
pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
{
int result = EBUSY;
acquire (&rwlock->rw_spinlock);
if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL)
{
rwlock->rw_writer = thread_self ();
result = 0;
}
release (&rwlock->rw_spinlock);
return result;
}
int
pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
{
struct _pthread_queue torestart;
pthread_descr th;
acquire (&rwlock->rw_spinlock);
if (rwlock->rw_writer != NULL)
{
/* Unlocking a write lock. */
if (rwlock->rw_writer != thread_self ())
{
release (&rwlock->rw_spinlock);
return EPERM;
}
rwlock->rw_writer = NULL;
if (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
|| (th = dequeue (&rwlock->rw_write_waiting)) == NULL)
{
/* Restart all waiting readers. */
torestart = rwlock->rw_read_waiting;
queue_init (&rwlock->rw_read_waiting);
release (&rwlock->rw_spinlock);
while ((th = dequeue (&torestart)) != NULL)
restart (th);
}
else
{
/* Restart one waiting writer. */
release (&rwlock->rw_spinlock);
restart (th);
}
}
else
{
/* Unlocking a read lock. */
if (rwlock->rw_readers == 0)
{
release (&rwlock->rw_spinlock);
return EPERM;
}
--rwlock->rw_readers;
if (rwlock->rw_readers == 0)
/* Restart one waiting writer, if any. */
th = dequeue (&rwlock->rw_write_waiting);
else
th = NULL;
release (&rwlock->rw_spinlock);
if (th != NULL)
restart (th);
}
return 0;
}
int
pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
{
attr->lockkind = 0;
attr->pshared = 0;
return 0;
}
int
pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr)
{
return 0;
}
int
pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared)
{
*pshared = attr->pshared;
return 0;
}
int
pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared)
{
if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
return EINVAL;
attr->pshared = pshared;
return 0;
}
int
pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t *attr, int *pref)
{
*pref = attr->lockkind;
return 0;
}
int
pthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref)
{
if (pref != PTHREAD_RWLOCK_PREFER_READER_NP
&& pref != PTHREAD_RWLOCK_PREFER_WRITER_NP
&& pref != PTHREAD_RWLOCK_DEFAULT_NP)
return EINVAL;
attr->lockkind = pref;
return 0;
}

236
linuxthreads/semaphore.c Normal file
View File

@ -0,0 +1,236 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Semaphores a la POSIX 1003.1b */
#include "pthread.h"
#include "semaphore.h"
#include "internals.h"
#include "restart.h"
#if !defined HAS_COMPARE_AND_SWAP && !defined TEST_FOR_COMPARE_AND_SWAP
/* If we have no atomic compare and swap, fake it using an extra spinlock. */
#include "spinlock.h"
static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
{
int ret;
acquire(&sem->sem_spinlock);
ret = (sem->sem_status == oldval);
if (ret) sem->sem_status = newval;
release(&sem->sem_spinlock);
return ret;
}
#elif defined TEST_FOR_COMPARE_AND_SWAP
#include "spinlock.h"
static int has_compare_and_swap = -1; /* to be determined at run-time */
static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
{
int ret;
if (has_compare_and_swap == 1)
return __compare_and_swap(&sem->sem_status, oldval, newval);
acquire(&sem->sem_spinlock);
ret = (sem->sem_status == oldval);
if (ret) sem->sem_status = newval;
release(&sem->sem_spinlock);
return ret;
}
#else
/* But if we do have an atomic compare and swap, use it! */
static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
{
return __compare_and_swap(&sem->sem_status, oldval, newval);
}
#endif
/* The state of a semaphore is represented by a long int encoding
either the semaphore count if >= 0 and no thread is waiting on it,
or the head of the list of threads waiting for the semaphore.
To distinguish the two cases, we encode the semaphore count N
as 2N+1, so that it has the lowest bit set.
A sequence of sem_wait operations on a semaphore initialized to N
result in the following successive states:
2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ...
*/
static void sem_restart_list(pthread_descr waiting);
int sem_init(sem_t *sem, int pshared, unsigned int value)
{
if ((long)value > SEM_VALUE_MAX) {
errno = EINVAL;
return -1;
}
if (pshared) {
errno = ENOSYS;
return -1;
}
#ifdef TEST_FOR_COMPARE_AND_SWAP
if (has_compare_and_swap == -1) {
has_compare_and_swap = compare_and_swap_is_available();
}
#endif
#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
sem->sem_spinlock = 0;
#endif
sem->sem_status = ((long)value << 1) + 1;
return 0;
}
int sem_wait(sem_t * sem)
{
long oldstatus, newstatus;
volatile pthread_descr self = thread_self();
pthread_descr * th;
while (1) {
do {
oldstatus = sem->sem_status;
if ((oldstatus & 1) && (oldstatus != 1))
newstatus = oldstatus - 2;
else {
newstatus = (long) self;
self->p_nextwaiting = (pthread_descr) oldstatus;
}
}
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
if (newstatus & 1)
/* We got the semaphore. */
return 0;
/* Wait for sem_post or cancellation */
suspend_with_cancellation(self);
/* This is a cancellation point */
if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
/* Remove ourselves from the waiting list if we're still on it */
/* First check if we're at the head of the list. */
do {
oldstatus = sem->sem_status;
if (oldstatus != (long) self) break;
newstatus = (long) self->p_nextwaiting;
}
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
/* Now, check if we're somewhere in the list.
There's a race condition with sem_post here, but it does not matter:
the net result is that at the time pthread_exit is called,
self is no longer reachable from sem->sem_status. */
if (oldstatus != (long) self && (oldstatus & 1) == 0) {
for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
*th != NULL && *th != (pthread_descr) 1;
th = &((*th)->p_nextwaiting)) {
if (*th == self) {
*th = self->p_nextwaiting;
break;
}
}
}
pthread_exit(PTHREAD_CANCELED);
}
}
}
int sem_trywait(sem_t * sem)
{
long oldstatus, newstatus;
do {
oldstatus = sem->sem_status;
if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
errno = EAGAIN;
return -1;
}
newstatus = oldstatus - 2;
}
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
return 0;
}
int sem_post(sem_t * sem)
{
long oldstatus, newstatus;
do {
oldstatus = sem->sem_status;
if ((oldstatus & 1) == 0)
newstatus = 3;
else {
if (oldstatus >= SEM_VALUE_MAX) {
/* Overflow */
errno = ERANGE;
return -1;
}
newstatus = oldstatus + 2;
}
}
while (! sem_compare_and_swap(sem, oldstatus, newstatus));
if ((oldstatus & 1) == 0)
sem_restart_list((pthread_descr) oldstatus);
return 0;
}
int sem_getvalue(sem_t * sem, int * sval)
{
long status = sem->sem_status;
if (status & 1)
*sval = (int)((unsigned long) status >> 1);
else
*sval = 0;
return 0;
}
int sem_destroy(sem_t * sem)
{
if ((sem->sem_status & 1) == 0) {
errno = EBUSY;
return -1;
}
return 0;
}
/* Auxiliary function for restarting all threads on a waiting list,
in priority order. */
static void sem_restart_list(pthread_descr waiting)
{
pthread_descr th, towake, *p;
/* Sort list of waiting threads by decreasing priority (insertion sort) */
towake = NULL;
while (waiting != (pthread_descr) 1) {
th = waiting;
waiting = waiting->p_nextwaiting;
p = &towake;
while (*p != NULL && th->p_priority < (*p)->p_priority)
p = &((*p)->p_nextwaiting);
th->p_nextwaiting = *p;
*p = th;
}
/* Wake up threads in priority order */
while (towake != NULL) {
th = towake;
towake = towake->p_nextwaiting;
th->p_nextwaiting = NULL;
restart(th);
}
}

38
linuxthreads/semaphore.h Normal file
View File

@ -0,0 +1,38 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
#ifndef _SEMAPHORE_H
#define _SEMAPHORE_H 1
#include <features.h>
#include <limits.h>
#define SEM_VALUE_MAX INT_MAX
/* Get the semaphore structure definition. */
#include <bits/semaphore.h>
__BEGIN_DECLS
extern int sem_init __P((sem_t *__sem, int __pshared, unsigned int __value));
extern int sem_destroy __P((sem_t *__sem));
extern int sem_wait __P((sem_t *__sem));
extern int sem_trywait __P((sem_t *__sem));
extern int sem_post __P((sem_t *__sem));
extern int sem_getvalue __P((sem_t *__sem, int *__sval));
__END_DECLS
#endif /* semaphore.h */

View File

@ -0,0 +1,2 @@
# Xavier Leroy's Linux clone based thread library.
.*-.*-linux.* libpthread=0

148
linuxthreads/signals.c Normal file
View File

@ -0,0 +1,148 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Handling of signals */
#include <errno.h>
#include <signal.h>
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
{
sigset_t mask;
if (newmask != NULL) {
mask = *newmask;
/* Don't allow PTHREAD_SIG_RESTART to be unmasked.
Don't allow PTHREAD_SIG_CANCEL to be masked. */
switch(how) {
case SIG_SETMASK:
sigaddset(&mask, PTHREAD_SIG_RESTART);
sigdelset(&mask, PTHREAD_SIG_CANCEL);
break;
case SIG_BLOCK:
sigdelset(&mask, PTHREAD_SIG_CANCEL);
break;
case SIG_UNBLOCK:
sigdelset(&mask, PTHREAD_SIG_RESTART);
break;
}
newmask = &mask;
}
if (sigprocmask(how, newmask, oldmask) == -1)
return errno;
else
return 0;
}
int pthread_kill(pthread_t thread, int signo)
{
pthread_handle handle = thread_handle(thread);
int pid;
acquire(&handle->h_spinlock);
if (invalid_handle(handle, thread)) {
release(&handle->h_spinlock);
return ESRCH;
}
pid = handle->h_descr->p_pid;
release(&handle->h_spinlock);
if (kill(pid, signo) == -1)
return errno;
else
return 0;
}
/* The set of signals on which some thread is doing a sigwait */
static sigset_t sigwaited;
static pthread_mutex_t sigwaited_mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t sigwaited_changed = PTHREAD_COND_INITIALIZER;
int sigwait(const sigset_t * set, int * sig)
{
volatile pthread_descr self = thread_self();
sigset_t mask;
int s;
struct sigaction action, saved_signals[NSIG];
sigjmp_buf jmpbuf;
pthread_mutex_lock(&sigwaited_mut);
/* Make sure no other thread is waiting on our signals */
test_again:
for (s = 1; s < NSIG; s++) {
if (sigismember(set, s) && sigismember(&sigwaited, s)) {
pthread_cond_wait(&sigwaited_changed, &sigwaited_mut);
goto test_again;
}
}
/* Get ready to block all signals except those in set
and the cancellation signal */
sigfillset(&mask);
sigdelset(&mask, PTHREAD_SIG_CANCEL);
/* Signals in set are assumed blocked on entrance */
/* Install our signal handler on all signals in set,
and unblock them in mask.
Also mark those signals as being sigwaited on */
for (s = 1; s < NSIG; s++) {
if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
sigdelset(&mask, s);
action.sa_handler = __pthread_sighandler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(s, &action, &(saved_signals[s]));
sigaddset(&sigwaited, s);
}
}
pthread_mutex_unlock(&sigwaited_mut);
/* Test for cancellation */
if (sigsetjmp(jmpbuf, 1) == 0) {
self->p_cancel_jmp = &jmpbuf;
if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
/* Reset the signal count */
self->p_signal = 0;
/* Unblock the signals and wait for them */
sigsuspend(&mask);
}
}
self->p_cancel_jmp = NULL;
/* The signals are now reblocked. Restore the sighandlers. */
pthread_mutex_lock(&sigwaited_mut);
for (s = 1; s < NSIG; s++) {
if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
sigaction(s, &(saved_signals[s]), NULL);
sigdelset(&sigwaited, s);
}
}
pthread_cond_broadcast(&sigwaited_changed);
pthread_mutex_unlock(&sigwaited_mut);
/* Check for cancellation */
pthread_testcancel();
/* We should have self->p_signal != 0 and equal to the signal received */
*sig = self->p_signal;
return 0;
}
int raise (int sig)
{
int retcode = pthread_kill(pthread_self(), sig);
if (retcode == 0)
return 0;
else {
errno = retcode;
return -1;
}
}

174
linuxthreads/specific.c Normal file
View File

@ -0,0 +1,174 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Thread-specific data */
#include <stddef.h>
#include <stdlib.h>
#include "pthread.h"
#include "internals.h"
typedef void (*destr_function)(void *);
/* Table of keys. */
struct pthread_key_struct {
int in_use; /* already allocated? */
destr_function destr; /* destruction routine */
};
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] =
{ { 0, NULL } };
/* Mutex to protect access to pthread_keys */
static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Create a new key */
int __pthread_key_create(pthread_key_t * key, destr_function destr)
{
int i;
pthread_mutex_lock(&pthread_keys_mutex);
for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
if (! pthread_keys[i].in_use) {
/* Mark key in use */
pthread_keys[i].in_use = 1;
pthread_keys[i].destr = destr;
pthread_mutex_unlock(&pthread_keys_mutex);
*key = i;
return 0;
}
}
pthread_mutex_unlock(&pthread_keys_mutex);
return EAGAIN;
}
weak_alias (__pthread_key_create, pthread_key_create)
/* Delete a key */
int pthread_key_delete(pthread_key_t key)
{
pthread_descr self = thread_self();
pthread_descr th;
unsigned int idx1st, idx2nd;
pthread_mutex_lock(&pthread_keys_mutex);
if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) {
pthread_mutex_unlock(&pthread_keys_mutex);
return EINVAL;
}
pthread_keys[key].in_use = 0;
pthread_keys[key].destr = NULL;
/* Set the value of the key to NULL in all running threads, so that
if the key is reallocated later by pthread_key_create, its
associated values will be NULL in all threads. */
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
th = self;
do {
if (th->p_specific[idx1st] != NULL)
th->p_specific[idx1st][idx2nd] = NULL;
th = th->p_nextlive;
} while (th != self);
pthread_mutex_unlock(&pthread_keys_mutex);
return 0;
}
/* Set the value of a key */
int __pthread_setspecific(pthread_key_t key, const void * pointer)
{
pthread_descr self = thread_self();
unsigned int idx1st, idx2nd;
if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use)
return EINVAL;
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
if (self->p_specific[idx1st] == NULL) {
self->p_specific[idx1st] =
calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *));
if (self->p_specific[idx1st] == NULL)
return ENOMEM;
}
self->p_specific[idx1st][idx2nd] = (void *) pointer;
return 0;
}
weak_alias (__pthread_setspecific, pthread_setspecific)
/* Get the value of a key */
void * __pthread_getspecific(pthread_key_t key)
{
pthread_descr self = thread_self();
unsigned int idx1st, idx2nd;
if (key >= PTHREAD_KEYS_MAX)
return NULL;
idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
if (self->p_specific[idx1st] == NULL || !pthread_keys[key].in_use)
return NULL;
return self->p_specific[idx1st][idx2nd];
}
weak_alias (__pthread_getspecific, pthread_getspecific)
/* Call the destruction routines on all keys */
void __pthread_destroy_specifics()
{
pthread_descr self = thread_self();
int i, j, round, found_nonzero;
destr_function destr;
void * data;
for (round = 0, found_nonzero = 1;
found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
round++) {
found_nonzero = 0;
for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
if (self->p_specific[i] != NULL)
for (j = 0; j < PTHREAD_KEY_2NDLEVEL_SIZE; j++) {
destr = pthread_keys[i * PTHREAD_KEY_2NDLEVEL_SIZE + j].destr;
data = self->p_specific[i][j];
if (destr != NULL && data != NULL) {
self->p_specific[i][j] = NULL;
destr(data);
found_nonzero = 1;
}
}
}
for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
if (self->p_specific[i] != NULL) free(self->p_specific[i]);
}
}
/* Thread-specific data for libc. */
int __libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer)
{
pthread_descr self = thread_self();
self->p_libc_specific[key] = (void *) pointer;
return 0;
}
void * __libc_internal_tsd_get(enum __libc_tsd_key_t key)
{
pthread_descr self = thread_self();
return self->p_libc_specific[key];
}

30
linuxthreads/spinlock.h Normal file
View File

@ -0,0 +1,30 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) and */
/* Richard Henderson (rth@tamu.edu) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
/* Spin locks */
static inline void acquire(int * spinlock)
{
while (testandset(spinlock)) __sched_yield();
}
static inline void release(int * spinlock)
{
#ifndef RELEASE
*spinlock = 0;
#else
RELEASE(spinlock);
#endif
}

View File

@ -0,0 +1,31 @@
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _SEMAPHORE_H
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
#endif
/* Due to the implementation of the load-locked/store-conditional
instructions, we cannot pack semaphores closer than a cache line
or risk threads deadlocking on unrelated semaphores. */
typedef struct {
long int sem_status;
long int sem_reserved[3];
} sem_t;

View File

@ -0,0 +1,102 @@
/* Machine-dependent pthreads configuration and inline functions.
Alpha version.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <asm/pal.h>
/* Spinlock implementation; required. */
extern inline long testandset(int *spinlock)
{
long ret, temp;
__asm__ __volatile__(
"/* Inline spinlock test & set */\n"
"1:\t"
"ldl_l %0,%3\n\t"
"bne %0,2f\n\t"
"or $31,1,%1\n\t"
"stl_c %1,%2\n\t"
"beq %1,1b\n"
"2:\tmb\n"
"/* End spinlock test & set */"
: "=&r"(ret), "=&r"(temp), "=m"(*spinlock)
: "m"(*spinlock)
: "memory");
return ret;
}
/* Spinlock release; default is just set to zero. */
#define RELEASE(spinlock) \
__asm__ __volatile__("mb" : : : "memory"); \
*spinlock = 0
/* Begin allocating thread stacks at this address. Default is to allocate
them just below the initial program stack. */
#define THREAD_STACK_START_ADDRESS 0x40000000000
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char *stack_pointer __asm__("$30");
/* Return the thread descriptor for the current thread. */
#define THREAD_SELF \
({ \
register pthread_descr __self __asm__("$0"); \
__asm__ ("call_pal %1" : "=r"(__self) : "i"(PAL_rduniq) : "$0"); \
__self; \
})
/* Initialize the thread-unique value. */
#define INIT_THREAD_SELF(descr) \
{ \
register pthread_descr __self __asm__("$16") = (descr); \
__asm__ __volatile__ ("call_pal %1" : : "r"(__self), "i"(PAL_wruniq)); \
}
/* Compare-and-swap for semaphores. */
#define HAS_COMPARE_AND_SWAP
extern inline int __compare_and_swap(long * p, long oldval, long newval)
{
long ret;
__asm__ __volatile__ (
"/* Inline compare & swap */\n"
"1:\t"
"ldq_l %0,%4\n\t"
"cmpeq %0,%2,%0\n\t"
"beq %0,2f\n\t"
"mov %3,%0\n\t"
"stq_c %0,%1\n\t"
"beq %0,1b\n\t"
"2:\tmb\n"
"/* End compare & swap */"
: "=&r"(ret), "=m"(*p)
: "r"(oldval), "r"(newval), "m"(*p));
return ret;
}

View File

@ -0,0 +1 @@
pthread/no-cmpxchg

View File

@ -0,0 +1,44 @@
/* Machine-dependent pthreads configuration and inline functions.
ARM version.
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Philip Blundell <philb@gnu.org>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* This will not work on ARM1 or ARM2 because SWP is lacking on those
machines. Unfortunately we have no way to detect this at compile
time; let's hope nobody tries to use one. */
/* Spinlock implementation; required. */
extern inline int
testandset (int *spinlock)
{
register unsigned int ret;
__asm__ __volatile__("swp %0, %1, [%2]"
: "=r"(ret)
: "0"(1), "r"(spinlock));
return ret;
}
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__ ("sp");

View File

@ -0,0 +1 @@
pthread/no-cmpxchg

View File

@ -0,0 +1,93 @@
/* Machine-dependent pthreads configuration and inline functions.
i386 version.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Spinlock implementation; required. */
extern inline int
testandset (int *spinlock)
{
int ret;
__asm__ __volatile__("xchgl %0, %1"
: "=r"(ret), "=m"(*spinlock)
: "0"(1), "m"(*spinlock));
return ret;
}
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__ ("%esp");
/* Compare-and-swap for semaphores.
Available on the 486 and above, but not on the 386.
We test dynamically whether it's available or not. */
#define HAS_COMPARE_AND_SWAP
#define TEST_FOR_COMPARE_AND_SWAP
extern inline int
__compare_and_swap (long int *p, long int oldval, long int newval)
{
char ret;
long int readval;
__asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
: "=q" (ret), "=m" (*p), "=a" (readval)
: "r" (newval), "m" (*p), "a" (oldval));
return ret;
}
extern inline int
get_eflags (void)
{
int res;
__asm__ __volatile__ ("pushfl; popl %0" : "=r" (res) : );
return res;
}
extern inline void
set_eflags (int newflags)
{
__asm__ __volatile__ ("pushl %0; popfl" : : "r" (newflags) : "cc");
}
extern inline int
compare_and_swap_is_available (void)
{
int oldflags = get_eflags ();
int changed;
/* Flip AC bit in EFLAGS. */
set_eflags (oldflags ^ 0x40000);
/* See if bit changed. */
changed = (get_eflags () ^ oldflags) & 0x40000;
/* Restore EFLAGS. */
set_eflags (oldflags);
/* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
Otherwise, it's a 486 or above and it has cmpxchg. */
return changed != 0;
}

View File

@ -0,0 +1 @@
pthread/cmpxchg

View File

@ -0,0 +1,58 @@
/* Machine-dependent pthreads configuration and inline functions.
m68k version.
Copyright (C) 1996 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Spinlock implementation; required. */
extern inline int
testandset (int *spinlock)
{
char ret;
__asm__ __volatile__("tas %1; sne %0"
: "=dm"(ret), "=m"(*spinlock)
: "m"(*spinlock)
: "cc");
return ret;
}
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__ ("%sp");
/* Compare-and-swap for semaphores. */
#define HAS_COMPARE_AND_SWAP
extern inline int
__compare_and_swap (long int *p, long int oldval, long int newval)
{
char ret;
long int readval;
__asm__ __volatile__ ("casl %2, %3, %1; seq %0"
: "=dm" (ret), "=m" (*p), "=d" (readval)
: "d" (newval), "m" (*p), "2" (oldval));
return ret;
}

View File

@ -0,0 +1 @@
pthread/cmpxchg

View File

@ -0,0 +1,84 @@
/* Machine-dependent pthreads configuration and inline functions.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>.
Based on the Alpha version by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
TODO: This version makes use of MIPS ISA 2 features. It won't
work on ISA 1. These machines will have to take the overhead of
a sysmips(MIPS_ATOMIC_SET, ...) syscall which isn't implemented
yet correctly. There is however a better solution for R3000
uniprocessor machines possible. */
/* Spinlock implementation; required. */
extern inline long testandset(int *spinlock)
{
long ret, temp;
__asm__ __volatile__(
"# Inline spinlock test & set\n\t"
".set\tmips2\n"
"1:\tll\t%0,%3\n\t"
"bnez\t%0,2f\n\t"
".set\tnoreorder\n\t"
"li\t%1,1\n\t"
".set\treorder\n\t"
"sc\t%1,%2\n\t"
"beqz\t%1,1b\n"
"2:\t.set\tmips0\n\t"
"/* End spinlock test & set */"
: "=&r"(ret), "=&r" (temp), "=m"(*spinlock)
: "m"(*spinlock)
: "memory");
return ret;
}
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__ ("$29");
/* Compare-and-swap for semaphores. */
#define HAS_COMPARE_AND_SWAP
extern inline int __compare_and_swap(long * p, long oldval, long newval)
{
long ret;
__asm__ __volatile__ (
"/* Inline compare & swap */\n\t"
".set\tmips2\n"
"1:\tll\t%0,%4\n\t"
".set\tnoreorder\n\t"
"bne\t%0,%2,2f\n\t"
"move\t%0,%3\n\t"
".set\treorder\n\t"
"sc\t%0,%1\n\t"
"beqz\t%0,1b\n"
"2:\t.set\tmips0\n\t"
"/* End compare & swap */"
: "=&r"(ret), "=m"(*p)
: "r"(oldval), "r"(newval), "m"(*p));
return ret;
}

View File

@ -0,0 +1 @@
pthread/cmpxchg

View File

@ -0,0 +1,32 @@
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _SEMAPHORE_H
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
#endif
/* Due to the implementation of the load-locked/store-conditional
instructions, we cannot pack semaphores closer than a cache line
or risk threads deadlocking on unrelated semaphores. */
typedef struct
{
int sem_status;
int sem_reserved[7];
} sem_t;

View File

@ -0,0 +1,84 @@
/* Machine-dependent pthreads configuration and inline functions.
powerpc version.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* These routines are from Appendix G of the 'PowerPC 601 RISC Microprocessor
User's Manual', by IBM and Motorola. */
/* For multiprocessor systems, we want to ensure all memory accesses
are completed before we reset a lock. */
#if 0
/* on non multiprocessor systems, you can just: */
#define sync() /* nothing */
#else
#define sync() __asm__ __volatile__ ("sync")
#endif
/* Spinlock implementation; required. */
extern inline int
testandset (int *spinlock)
{
int ret;
sync();
__asm__ __volatile__(
"0: lwarx %0,0,%1 ;"
" cmpwi %0,0;"
" bne 1f;"
" stwcx. %2,0,%1;"
" bne- 0b;"
"1: "
: "=&r"(ret)
: "r"(spinlock), "r"(1)
: "cr0", "memory");
sync();
return ret != 0;
}
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__ ("r1");
/* Compare-and-swap for semaphores. */
/* note that test-and-set(x) is the same as compare-and-swap(x, 0, 1) */
#define HAS_COMPARE_AND_SWAP
extern inline int
__compare_and_swap (int *p, int oldval, int newval)
{
int ret;
sync();
__asm__ __volatile__(
"0: lwarx %0,0,%1 ;"
" xor. %0,%3,%0;"
" bne 1f;"
" stwcx. %2,0,%1;"
" bne- 0b;"
"1: "
: "=&r"(ret)
: "r"(p), "r"(newval), "r"(oldval)
: "cr0", "memory");
sync();
return ret == 0;
}

View File

@ -0,0 +1,3 @@
ifeq ($(subdir),libio)
sysdep_headers += bits/stdio-lock.h
endif

View File

@ -0,0 +1,208 @@
/* libc-internal interface for mutex locks. LinuxThreads version.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _BITS_LIBC_LOCK_H
#define _BITS_LIBC_LOCK_H 1
#include <pthread.h>
/* Mutex type. */
#ifdef _LIBC
typedef pthread_mutex_t __libc_lock_t;
#else
typedef struct __libc_lock_opaque__ __libc_lock_t;
#endif
/* Type for key to thread-specific data. */
typedef pthread_key_t __libc_key_t;
/* Define a lock variable NAME with storage class CLASS. The lock must be
initialized with __libc_lock_init before it can be used (or define it
with __libc_lock_define_initialized, below). Use `extern' for CLASS to
declare a lock defined in another module. In public structure
definitions you must use a pointer to the lock structure (i.e., NAME
begins with a `*'), because its storage size will not be known outside
of libc. */
#define __libc_lock_define(CLASS,NAME) \
CLASS __libc_lock_t NAME;
/* Define an initialized lock variable NAME with storage class CLASS. */
#define __libc_lock_define_initialized(CLASS,NAME) \
CLASS __libc_lock_t NAME = PTHREAD_MUTEX_INITIALIZER;
/* Define an initialized recursive lock variable NAME with storage
class CLASS. */
#define __libc_lock_define_initialized_recursive(CLASS,NAME) \
CLASS __libc_lock_t NAME = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
/* Initialize the named lock variable, leaving it in a consistent, unlocked
state. */
#define __libc_lock_init(NAME) \
(__pthread_mutex_init != NULL ? __pthread_mutex_init (&(NAME), NULL) : 0);
/* Same as last but this time we initialize a recursive mutex. */
#define __libc_lock_init_recursive(NAME) \
do { \
if (__pthread_mutex_init != NULL) \
{ \
pthread_mutexattr_t __attr; \
__pthread_mutexattr_init (&__attr); \
__pthread_mutexattr_setkind_np (&__attr, PTHREAD_MUTEX_RECURSIVE_NP); \
__pthread_mutex_init (&(NAME), &__attr); \
__pthread_mutexattr_destroy (&__attr); \
} \
} while (0);
/* Finalize the named lock variable, which must be locked. It cannot be
used again until __libc_lock_init is called again on it. This must be
called on a lock variable before the containing storage is reused. */
#define __libc_lock_fini(NAME) \
(__pthread_mutex_destroy != NULL ? __pthread_mutex_destroy (&(NAME)) : 0);
/* Finalize recursive named lock. */
#define __libc_lock_fini_recursive(NAME) __libc_lock_fini (NAME)
/* Lock the named lock variable. */
#define __libc_lock_lock(NAME) \
(__pthread_mutex_lock != NULL ? __pthread_mutex_lock (&(NAME)) : 0);
/* Lock the recursive named lock variable. */
#define __libc_lock_lock_recursive(NAME) __libc_lock_lock (NAME)
/* Try to lock the named lock variable. */
#define __libc_lock_trylock(NAME) \
(__pthread_mutex_trylock != NULL ? __pthread_mutex_trylock (&(NAME)) : 0)
/* Try to lock the recursive named lock variable. */
#define __libc_lock_trylock_recursive(NAME) __libc_lock_trylock (NAME)
/* Unlock the named lock variable. */
#define __libc_lock_unlock(NAME) \
(__pthread_mutex_unlock != NULL ? __pthread_mutex_unlock (&(NAME)) : 0);
/* Unlock the recursive named lock variable. */
#define __libc_lock_unlock_recursive(NAME) __libc_lock_unlock (NAME)
/* Define once control variable. */
#define __libc_once_define(CLASS, NAME) \
CLASS pthread_once_t NAME = PTHREAD_ONCE_INIT
/* Call handler iff the first call. */
#define __libc_once(ONCE_CONTROL, INIT_FUNCTION) \
do { \
if (__pthread_once != NULL) \
__pthread_once (&(ONCE_CONTROL), (INIT_FUNCTION)); \
else if ((ONCE_CONTROL) == 0) { \
INIT_FUNCTION (); \
(ONCE_CONTROL) = 1; \
} \
} while (0)
/* Start critical region with cleanup. */
#define __libc_cleanup_region_start(FCT, ARG) \
{ struct _pthread_cleanup_buffer _buffer; \
if (_pthread_cleanup_push_defer != NULL) { \
_pthread_cleanup_push_defer (&_buffer, (FCT), (ARG)); \
}
/* End critical region with cleanup. */
#define __libc_cleanup_region_end(DOIT) \
if (_pthread_cleanup_push_defer != NULL) { \
_pthread_cleanup_pop_restore (&_buffer, (DOIT)); \
} \
}
/* Create thread-specific key. */
#define __libc_key_create(KEY, DESTRUCTOR) \
(__pthread_key_create != NULL ? __pthread_key_create (KEY, DESTRUCTOR) : 1)
/* Get thread-specific data. */
#define __libc_getspecific(KEY) \
(__pthread_getspecific != NULL ? __pthread_getspecific (KEY) : NULL)
/* Set thread-specific data. */
#define __libc_setspecific(KEY, VALUE) \
(__pthread_setspecific != NULL ? __pthread_setspecific (KEY, VALUE) : 0)
#ifdef _LIBC
/* Fast thread-specific data internal to libc. */
enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, _LIBC_TSD_KEY_N };
extern void *__libc_internal_tsd_get __P ((enum __libc_tsd_key_t));
extern int __libc_internal_tsd_set __P ((enum __libc_tsd_key_t,
__const void *));
#endif
/* Register handlers to execute before and after `fork'. */
#define __libc_atfork(PREPARE, PARENT, CHILD) \
(__pthread_atfork != NULL ? __pthread_atfork (PREPARE, PARENT, CHILD) : 0)
/* Make the pthread functions weak so that we can elide them from
single-threaded processes. */
#ifdef weak_extern
weak_extern (__pthread_mutex_init)
weak_extern (__pthread_mutex_destroy)
weak_extern (__pthread_mutex_lock)
weak_extern (__pthread_mutex_trylock)
weak_extern (__pthread_mutex_unlock)
weak_extern (__pthread_mutexattr_init)
weak_extern (__pthread_mutexattr_destroy)
weak_extern (__pthread_mutexattr_setkind_np)
weak_extern (__pthread_key_create)
weak_extern (__pthread_setspecific)
weak_extern (__pthread_getspecific)
weak_extern (__libc_internal_tsd_get)
weak_extern (__libc_internal_tsd_set)
weak_extern (__pthread_once)
weak_extern (__pthread_initialize)
weak_extern (__pthread_atfork)
weak_extern (_pthread_cleanup_push_defer)
weak_extern (_pthread_cleanup_pop_restore)
#else
# pragma weak __pthread_mutex_init
# pragma weak __pthread_mutex_destroy
# pragma weak __pthread_mutex_lock
# pragma weak __pthread_mutex_trylock
# pragma weak __pthread_mutex_unlock
# pragma weak __pthread_mutexattr_init
# pragma weak __pthread_mutexattr_destroy
# pragma weak __pthread_mutexattr_setkind_np
# pragma weak __pthread_key_create
# pragma weak __pthread_setspecific
# pragma weak __pthread_getspecific
# pragma weak __libc_internal_tsd_get
# pragma weak __libc_internal_tsd_set
# pragma weak __pthread_once
# pragma weak __pthread_initialize
# pragma weak __pthread_atfork
# pragma weak _pthread_cleanup_push_defer
# pragma weak _pthread_cleanup_pop_restore
#endif
/* We need portable names for some functions. E.g., when they are
used as argument to __libc_cleanup_region_start. */
#define __libc_mutex_unlock __pthread_mutex_unlock
#endif /* bits/libc-lock.h */

View File

@ -0,0 +1,35 @@
/* Thread package specific definitions of stream lock type.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <pthread.h>
typedef pthread_mutex_t _IO_lock_t;
/* We need recursive (counting) mutexes. */
#define _IO_lock_initializer PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
#define _IO_cleanup_region_start(_fct, _fp) \
__libc_cleanup_region_start (_fct, _fp)
#define _IO_cleanup_region_end(_doit) \
__libc_cleanup_region_end (_doit)
#define _IO_lock_init(_name) \
__libc_lock_init_recursive (_name)
#define _IO_lock_fini(_name) \
__libc_lock_fini_recursive (_name)

View File

@ -0,0 +1,26 @@
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _SEMAPHORE_H
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
#endif
typedef struct {
long int sem_status;
} sem_t;

View File

@ -0,0 +1,27 @@
/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _SEMAPHORE_H
# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
#endif
typedef struct {
long int sem_status;
int sem_spinlock;
} sem_t;

View File

@ -0,0 +1,578 @@
/* Linuxthreads - a simple clone()-based implementation of Posix */
/* threads for Linux. */
/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) */
/* */
/* This program is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Library General Public License */
/* as published by the Free Software Foundation; either version 2 */
/* of the License, or (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU Library General Public License for more details. */
#ifndef _PTHREAD_H
#define _PTHREAD_H 1
#include <features.h>
#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <unistd.h>
#define __need_sigset_t
#include <signal.h>
#define __need_timespec
#include <time.h>
/* Linux has no ENOTSUP error code. */
#ifndef ENOTSUP
#define ENOTSUP EOPNOTSUPP
#endif
__BEGIN_DECLS
/*** Types ***/
/* Thread identifiers */
typedef unsigned long int pthread_t;
/* Thread descriptors */
typedef struct _pthread_descr_struct *_pthread_descr;
/* Waiting queues (not abstract because mutexes and conditions aren't). */
struct _pthread_queue
{
_pthread_descr head; /* First element, or NULL if queue empty. */
_pthread_descr tail; /* Last element, or NULL if queue empty. */
};
/* Mutexes (not abstract because of PTHREAD_MUTEX_INITIALIZER). */
typedef struct
{
int m_spinlock; /* Spin lock to guarantee mutual exclusion. */
int m_count; /* 0 if free, > 0 if taken. */
_pthread_descr m_owner; /* Owner of mutex (for recursive mutexes) */
int m_kind; /* Kind of mutex */
struct _pthread_queue m_waiting; /* Threads waiting on this mutex. */
} pthread_mutex_t;
#define PTHREAD_MUTEX_INITIALIZER \
{0, 0, 0, PTHREAD_MUTEX_FAST_NP, {0, 0}}
#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP \
{0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, {0, 0}}
/* Conditions (not abstract because of PTHREAD_COND_INITIALIZER */
typedef struct
{
int c_spinlock; /* Spin lock to protect the queue. */
struct _pthread_queue c_waiting; /* Threads waiting on this condition. */
} pthread_cond_t;
#define PTHREAD_COND_INITIALIZER {0, {0, 0}}
#ifdef __USE_UNIX98
/* Read-write locks. */
typedef struct
{
int rw_spinlock; /* Spin lock to guarantee mutual exclusion */
int rw_readers; /* Number of readers */
_pthread_descr rw_writer; /* Identity of writer, or NULL if none */
struct _pthread_queue rw_read_waiting; /* Threads waiting for reading */
struct _pthread_queue rw_write_waiting; /* Threads waiting for writing */
int rw_kind; /* Reader/Writer preference selection */
int rw_pshared; /* Shared between processes or not */
} pthread_rwlock_t;
# define PTHREAD_RWLOCK_INITIALIZER \
{ 0, 0, 0, {0, 0}, {0, 0}, \
PTHREAD_RWLOCK_DEFAULT_NP, PTHREAD_PROCESS_PRIVATE }
#endif
/* Attributes */
enum
{
PTHREAD_CREATE_JOINABLE,
PTHREAD_CREATE_DETACHED
};
enum
{
PTHREAD_INHERIT_SCHED,
PTHREAD_EXPLICIT_SCHED
};
enum
{
PTHREAD_SCOPE_SYSTEM,
PTHREAD_SCOPE_PROCESS
};
typedef struct
{
int detachstate;
int schedpolicy;
struct sched_param schedparam;
int inheritsched;
int scope;
} pthread_attr_t;
enum
{
PTHREAD_MUTEX_FAST_NP,
PTHREAD_MUTEX_RECURSIVE_NP,
PTHREAD_MUTEX_ERRORCHECK_NP
#ifdef __USE_UNIX98
,
PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,
PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
#endif
};
typedef struct
{
int mutexkind;
} pthread_mutexattr_t;
typedef struct
{
int dummy;
} pthread_condattr_t;
#ifdef __USE_UNIX98
enum
{
PTHREAD_PROCESS_PRIVATE,
PTHREAD_PROCESS_SHARED
};
enum
{
PTHREAD_RWLOCK_PREFER_READER_NP,
PTHREAD_RWLOCK_PREFER_WRITER_NP,
PTHREAD_RWLOCK_DEFAULT_NP = PTHREAD_RWLOCK_PREFER_WRITER_NP
};
typedef struct
{
int lockkind;
int pshared;
} pthread_rwlockattr_t;
#endif
/* Keys for thread-specific data */
typedef unsigned int pthread_key_t;
/* Once-only execution */
typedef int pthread_once_t;
#define PTHREAD_ONCE_INIT 0
/* Cleanup buffers */
struct _pthread_cleanup_buffer
{
void (*routine) __P ((void *)); /* Function to call. */
void *arg; /* Its argument. */
int canceltype; /* Saved cancellation type. */
struct _pthread_cleanup_buffer *prev; /* Chaining of cleanup functions. */
};
/* Cancellation */
enum { PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE };
enum { PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS };
#define PTHREAD_CANCELED ((void *) -1)
/* Function for handling threads. */
/* Create a thread with given attributes ATTR (or default attributes
if ATTR is NULL), and call function START_ROUTINE with given
arguments ARG. */
extern int pthread_create __P ((pthread_t *__thread,
__const pthread_attr_t *__attr,
void *(*__start_routine) (void *),
void *__arg));
/* Obtain the identifier of the current thread. */
extern pthread_t pthread_self __P ((void));
/* Compare two thread identifiers. */
extern int pthread_equal __P ((pthread_t __thread1, pthread_t __thread2));
/* Terminate calling thread. */
extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
/* Make calling thread wait for termination of the thread TH. The
exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
is not NULL. */
extern int pthread_join __P ((pthread_t __th, void **__thread_return));
/* Indicate that the thread TH is never to be joined with PTHREAD_JOIN.
The resources of TH will therefore be freed immediately when it
terminates, instead of waiting for another thread to perform PTHREAD_JOIN
on it. */
extern int pthread_detach __P ((pthread_t __th));
/* Functions for handling attributes. */
/* Initialize thread attribute *ATTR with default attributes
(detachstate is PTHREAD_JOINABLE, scheduling policy is SCHED_OTHER). */
extern int pthread_attr_init __P ((pthread_attr_t *__attr));
/* Destroy thread attribute *ATTR. */
extern int pthread_attr_destroy __P ((pthread_attr_t *__attr));
/* Set the `detachstate' attribute in *ATTR according to DETACHSTATE. */
extern int pthread_attr_setdetachstate __P ((pthread_attr_t *__attr,
int __detachstate));
/* Return in *DETACHSTATE the `detachstate' attribute in *ATTR. */
extern int pthread_attr_getdetachstate __P ((__const pthread_attr_t *__attr,
int *__detachstate));
/* Set scheduling parameters (priority, etc) in *ATTR according to PARAM. */
extern int pthread_attr_setschedparam __P ((pthread_attr_t *__attr,
__const struct sched_param *__param));
/* Return in *PARAM the scheduling parameters of *ATTR. */
extern int pthread_attr_getschedparam __P ((__const pthread_attr_t *__attr,
struct sched_param *__param));
/* Set scheduling policy in *ATTR according to POLICY. */
extern int pthread_attr_setschedpolicy __P ((pthread_attr_t *__attr,
int __policy));
/* Return in *POLICY the scheduling policy of *ATTR. */
extern int pthread_attr_getschedpolicy __P ((__const pthread_attr_t *__attr,
int *__policy));
/* Set scheduling inheritance mode in *ATTR according to INHERIT. */
extern int pthread_attr_setinheritsched __P ((pthread_attr_t *__attr,
int __inherit));
/* Return in *INHERIT the scheduling inheritance mode of *ATTR. */
extern int pthread_attr_getinheritsched __P ((__const pthread_attr_t *__attr,
int *__inherit));
/* Set scheduling contention scope in *ATTR according to SCOPE. */
extern int pthread_attr_setscope __P ((pthread_attr_t *__attr, int __scope));
/* Return in *SCOPE the scheduling contention scope of *ATTR. */
extern int pthread_attr_getscope __P ((__const pthread_attr_t *__attr,
int *__scope));
/* Functions for scheduling control. */
/* Set the scheduling parameters for TARGET_THREAD according to POLICY
and *PARAM. */
extern int pthread_setschedparam __P ((pthread_t __target_thread, int __policy,
__const struct sched_param *__param));
/* Return in *POLICY and *PARAM the scheduling parameters for TARGET_THREAD. */
extern int pthread_getschedparam __P ((pthread_t __target_thread,
int *__policy,
struct sched_param *__param));
/* Functions for mutex handling. */
/* Initialize MUTEX using attributes in *MUTEX_ATTR, or use the
default values if later is NULL. */
extern int __pthread_mutex_init __P ((pthread_mutex_t *__mutex,
__const pthread_mutexattr_t *__mutex_attr));
extern int pthread_mutex_init __P ((pthread_mutex_t *__mutex,
__const pthread_mutexattr_t *__mutex_attr));
/* Destroy MUTEX. */
extern int __pthread_mutex_destroy __P ((pthread_mutex_t *__mutex));
extern int pthread_mutex_destroy __P ((pthread_mutex_t *__mutex));
/* Try to lock MUTEX. */
extern int __pthread_mutex_trylock __P ((pthread_mutex_t *__mutex));
extern int pthread_mutex_trylock __P ((pthread_mutex_t *__mutex));
/* Wait until lock for MUTEX becomes available and lock it. */
extern int __pthread_mutex_lock __P ((pthread_mutex_t *__mutex));
extern int pthread_mutex_lock __P ((pthread_mutex_t *__mutex));
/* Unlock MUTEX. */
extern int __pthread_mutex_unlock __P ((pthread_mutex_t *__mutex));
extern int pthread_mutex_unlock __P ((pthread_mutex_t *__mutex));
/* Functions for handling mutex attributes. */
/* Initialize mutex attribute object ATTR with default attributes
(kind is PTHREAD_MUTEX_FAST_NP). */
extern int __pthread_mutexattr_init __P ((pthread_mutexattr_t *__attr));
extern int pthread_mutexattr_init __P ((pthread_mutexattr_t *__attr));
/* Destroy mutex attribute object ATTR. */
extern int __pthread_mutexattr_destroy __P ((pthread_mutexattr_t *__attr));
extern int pthread_mutexattr_destroy __P ((pthread_mutexattr_t *__attr));
/* Set the mutex kind attribute in *ATTR to KIND (either PTHREAD_MUTEX_FAST_NP
or PTHREAD_MUTEX_RECURSIVE_NP). */
extern int __pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
int __kind));
extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr,
int __kind));
/* Return in *KIND the mutex kind attribute in *ATTR. */
extern int pthread_mutexattr_getkind_np __P ((__const pthread_mutexattr_t *__attr,
int *__kind));
/* Functions for handling conditional variables. */
/* Initialize condition variable COND using attributes ATTR, or use
the default values if later is NULL. */
extern int pthread_cond_init __P ((pthread_cond_t *__cond,
__const pthread_condattr_t *__cond_attr));
/* Destroy condition variable COND. */
extern int pthread_cond_destroy __P ((pthread_cond_t *__cond));
/* Wake up one thread waiting for condition variable COND. */
extern int pthread_cond_signal __P ((pthread_cond_t *__cond));
/* Wake up all threads waiting for condition variables COND. */
extern int pthread_cond_broadcast __P ((pthread_cond_t *__cond));
/* Wait for condition variable COND to be signaled or broadcast.
MUTEX is assumed to be locked before. */
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,
pthread_mutex_t *__mutex));
/* Wait for condition variable COND to be signaled or broadcast until
ABSTIME. MUTEX is assumed to be locked before. ABSTIME is an
absolute time specification; zero is the beginning of the epoch
(00:00:00 GMT, January 1, 1970). */
extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,
pthread_mutex_t *__mutex,
__const struct timespec *__abstime));
/* Functions for handling condition variable attributes. */
/* Initialize condition variable attribute ATTR. */
extern int pthread_condattr_init __P ((pthread_condattr_t *__attr));
/* Destroy condition variable attribute ATTR. */
extern int pthread_condattr_destroy __P ((pthread_condattr_t *__attr));
#ifdef __USE_UNIX98
/* Functions for handling read-write locks. */
/* Initialize read-write lock RWLOCK using attributes ATTR, or use
the default values if later is NULL. */
extern int pthread_rwlock_init __P ((pthread_rwlock_t *__rwlock,
__const pthread_rwlockattr_t *__attr));
/* Destroy read-write lock RWLOCK. */
extern int pthread_rwlock_destroy __P ((pthread_rwlock_t *__rwlock));
/* Acquire read lock for RWLOCK. */
extern int pthread_rwlock_rdlock __P ((pthread_rwlock_t *__rwlock));
/* Try to acquire read lock for RWLOCK. */
extern int pthread_rwlock_tryrdlock __P ((pthread_rwlock_t *__rwlock));
/* Acquire write lock for RWLOCK. */
extern int pthread_rwlock_wrlock __P ((pthread_rwlock_t *__rwlock));
/* Try to acquire writelock for RWLOCK. */
extern int pthread_rwlock_trywrlock __P ((pthread_rwlock_t *__rwlock));
/* Unlock RWLOCK. */
extern int pthread_rwlock_unlock __P ((pthread_rwlock_t *__rwlock));
/* Functions for handling read-write lock attributes. */
/* Initialize attribute object ATTR with default values. */
extern int pthread_rwlockattr_init __P ((pthread_rwlockattr_t *__attr));
/* Destroy attribute object ATTR. */
extern int pthread_rwlockattr_destroy __P ((pthread_rwlockattr_t *__attr));
/* Return current setting of process-shared attribute of ATTR in PSHARED. */
extern int pthread_rwlockattr_getpshared __P ((__const
pthread_rwlockattr_t *__attr,
int *__pshared));
/* Set process-shared attribute of ATTR to PSHARED. */
extern int pthread_rwlockattr_setpshared __P ((pthread_rwlockattr_t *__attr,
int __pshared));
/* Return current setting of reader/writer preference. */
extern int pthread_rwlockattr_getkind_np __P ((__const
pthread_rwlockattr_t *__attr,
int *__pref));
/* Set reader/write preference. */
extern int pthread_rwlockattr_setkind_np __P ((pthread_rwlockattr_t *__attr,
int __pref));
#endif
/* Functions for handling thread-specific data */
/* Create a key value identifying a location in the thread-specific data
area. Each thread maintains a distinct thread-specific data area.
DESTR_FUNCTION, if non-NULL, is called with
the value associated to that key when the key is destroyed.
DESTR_FUNCTION is not called if the value associated is NULL
when the key is destroyed. */
extern int __pthread_key_create __P ((pthread_key_t *__key,
void (*__destr_function) (void *)));
extern int pthread_key_create __P ((pthread_key_t *__key,
void (*__destr_function) (void *)));
/* Destroy KEY. */
extern int pthread_key_delete __P ((pthread_key_t __key));
/* Store POINTER in the thread-specific data slot identified by KEY. */
extern int __pthread_setspecific __P ((pthread_key_t __key,
__const void *__pointer));
extern int pthread_setspecific __P ((pthread_key_t __key,
__const void *__pointer));
/* Return current value of the thread-specific data slot identified by KEY. */
extern void *__pthread_getspecific __P ((pthread_key_t __key));
extern void *pthread_getspecific __P ((pthread_key_t __key));
/* Functions for handling initialization */
/* Guarantee that the initialization function INIT_ROUTINE will be called
only once, even if pthread_once is executed several times with the
same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or
extern variable initialized to PTHREAD_ONCE_INIT. */
extern int __pthread_once __P ((pthread_once_t *__once_control,
void (*__init_routine) (void)));
extern int pthread_once __P ((pthread_once_t *__once_control,
void (*__init_routine) (void)));
/* Functions for handling cancellation. */
/* Set cancelability state of current thread to STATE, returning old
state in *OLDSTATE if OLDSTATE is not NULL. */
extern int pthread_setcancelstate __P ((int __state, int *__oldstate));
/* Set cancellation state of current thread to TYPE, returning the old
type in *OLDTYPE if OLDTYPE is not NULL. */
extern int __pthread_setcanceltype __P ((int __type, int *__oldtype));
extern int pthread_setcanceltype __P ((int __type, int *__oldtype));
/* Cancel THREAD immediately or at the next possibility. */
extern int pthread_cancel __P ((pthread_t __thread));
/* Test for pending cancellation for the current thread and terminate
the thread as per pthread_exit(PTHREAD_CANCELED) if it has been
cancelled. */
extern void pthread_testcancel __P ((void));
/* Install a cleanup handler: ROUTINE will be called with arguments ARG
when the thread is cancelled or calls pthread_exit. ROUTINE will also
be called with arguments ARG when the matching pthread_cleanup_pop
is executed with non-zero EXECUTE argument.
pthread_cleanup_push and pthread_cleanup_pop are macros and must always
be used in matching pairs at the same nesting level of braces. */
#define pthread_cleanup_push(routine,arg) \
{ struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
extern void _pthread_cleanup_push __P ((struct _pthread_cleanup_buffer *__buffer,
void (*__routine) (void *),
void *__arg));
/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
If EXECUTE is non-zero, the handler function is called. */
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); }
extern void _pthread_cleanup_pop __P ((struct _pthread_cleanup_buffer *__buffer,
int __execute));
/* Install a cleanup handler as pthread_cleanup_push does, but also
saves the current cancellation type and set it to deferred cancellation. */
#define pthread_cleanup_push_defer_np(routine,arg) \
{ struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push_defer (&_buffer, (routine), (arg));
extern void _pthread_cleanup_push_defer __P ((struct _pthread_cleanup_buffer *__buffer,
void (*__routine) (void *),
void *__arg));
/* Remove a cleanup handler as pthread_cleanup_pop does, but also
restores the cancellation type that was in effect when the matching
pthread_cleanup_push_defer was called. */
#define pthread_cleanup_pop_restore_np(execute) \
_pthread_cleanup_pop_restore (&_buffer, (execute)); }
extern void _pthread_cleanup_pop_restore __P ((struct _pthread_cleanup_buffer *__buffer,
int __execute));
/* Functions for handling signals. */
/* Modify the signal mask for the calling thread. The arguments have
the same meaning as for sigprocmask(2). */
extern int pthread_sigmask __P ((int __how, __const sigset_t *__newmask,
sigset_t *__oldmask));
/* Send signal SIGNO to the given thread. */
extern int pthread_kill __P ((pthread_t __thread, int __signo));
/* Functions for handling process creation and process execution. */
/* Install handlers to be called when a new process is created with FORK.
The PREPARE handler is called in the parent process just before performing
FORK. The PARENT handler is called in the parent process just after FORK.
The CHILD handler is called in the child process. Each of the three
handlers can be NULL, meaning that no handler needs to be called at that
point.
PTHREAD_ATFORK can be called several times, in which case the PREPARE
handlers are called in LIFO order (last added with PTHREAD_ATFORK,
first called before FORK), and the PARENT and CHILD handlers are called
in FIFO (first added, first called). */
extern int __pthread_atfork __P ((void (*__prepare) (void),
void (*__parent) (void),
void (*__child) (void)));
extern int pthread_atfork __P ((void (*__prepare) (void),
void (*__parent) (void),
void (*__child) (void)));
/* Terminate all threads in the program except the calling process.
Should be called just before invoking one of the exec*() functions. */
extern void __pthread_kill_other_threads_np __P ((void));
extern void pthread_kill_other_threads_np __P ((void));
/* This function is called to initialize the pthread library. */
extern void __pthread_initialize __P ((void));
__END_DECLS
#endif /* pthread.h */

View File

@ -0,0 +1 @@
pthread/no-cmpxchg

View File

@ -0,0 +1,55 @@
/* Machine-dependent pthreads configuration and inline functions.
sparc version.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* Spinlock implementation; required. */
static inline int testandset(int *spinlock)
{
int ret;
__asm__ __volatile__("ldstub %1,%0"
: "=r"(ret), "=m"(*spinlock)
: "m"(*spinlock));
return ret;
}
/* Spinlock release; default is just set to zero. */
#define RELEASE(spinlock) \
__asm__ __volatile__("stbar; stb %1,%0" : "=m"(*(spinlock)) : "r"(0));
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__("%sp");
/* Registers %g6 and %g7 are reserved by the ABI for "system use". It
happens that Solaris uses %g6 for the thread pointer -- we do the same. */
struct _pthread_descr_struct;
register struct _pthread_descr_struct *__thread_self __asm__("%g6");
/* Return the thread descriptor for the current thread. */
#define THREAD_SELF __thread_self
/* Initialize the thread-unique value. */
#define INIT_THREAD_SELF(descr) (__thread_self = (descr))

View File

@ -0,0 +1 @@
pthread/cmpxchg

View File

@ -0,0 +1,67 @@
/* Machine-dependent pthreads configuration and inline functions.
Sparc v9 version.
Copyright (C) 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Richard Henderson <rth@tamu.edu>.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Spinlock implementation; required. */
extern inline int
testandset (int *spinlock)
{
int ret;
__asm__ __volatile__("ldstub %1,%0"
: "=r"(ret), "=m"(*spinlock) : "m"(*spinlock));
return ret;
}
/* Get some notion of the current stack. Need not be exactly the top
of the stack, just something somewhere in the current frame. */
#define CURRENT_STACK_FRAME stack_pointer
register char * stack_pointer __asm__ ("%sp");
/* Registers %g6 and %g7 are reserved by the ABI for "system use". It
happens that Solaris uses %g6 for the thread pointer -- we do the same. */
struct _pthread_descr_struct;
register struct _pthread_descr_struct *__thread_self __asm__("%g6");
/* Return the thread descriptor for the current thread. */
#define THREAD_SELF __thread_self
/* Initialize the thread-unique value. */
#define INIT_THREAD_SELF(descr) (__thread_self = (descr))
/* Compare-and-swap for semaphores. */
#define HAS_COMPARE_AND_SWAP
extern inline int
__compare_and_swap (long int *p, long int oldval, long int newval)
{
long int readval;
__asm__ __volatile__ ("cas %1, %2, %0"
: "=r"(readval), "=m"(*p)
: "r"(oldval), "m"(*p), "0"(newval));
return readval == newval;
}

View File

@ -0,0 +1 @@
pthread

View File

@ -0,0 +1,40 @@
/* Minimum guaranteed maximum values for system limits. Linux version.
Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* The kernel sources contain a file with all the needed information. */
#include <linux/limits.h>
/* The number of data keys per process. */
#define _POSIX_THREAD_KEYS_MAX 128
/* This is the value this implementation supports. */
#define PTHREAD_KEYS_MAX 1024
/* Controlling the iterations of destructors for thread-specific data. */
#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4
/* Number of iterations this implementation does. */
#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
/* The number of threads per process. */
#define _POSIX_THREAD_THREADS_MAX 64
/* This is the value this implementation supports. */
#define PTHREAD_THREADS_MAX 1024
/* Maximum amount by which a process can descrease its asynchronous I/O
priority level. */
#define AIO_PRIO_DELTA_MAX 20

View File

@ -0,0 +1,94 @@
/* Define POSIX options for Linux.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _POSIX_OPT_H
#define _POSIX_OPT_H 1
/* Job control is supported. */
#define _POSIX_JOB_CONTROL 1
/* Processes have a saved set-user-ID and a saved set-group-ID. */
#define _POSIX_SAVED_IDS 1
/* Priority scheduling is supported. */
#define _POSIX_PRIORITY_SCHEDULING 1
/* Synchronizing file data is supported. */
#define _POSIX_SYNCHRONIZED_IO 1
/* The fsync function is present. */
#define _POSIX_FSYNC 1
/* Mapping of files to memory is supported. */
#define _POSIX_MAPPED_FILES 1
/* Locking of all memory is supported. */
#define _POSIX_MEMLOCK 1
/* Locking of ranges of memory is supported. */
#define _POSIX_MEMLOCK_RANGE 1
/* Setting of memory protections is supported. */
#define _POSIX_MEMORY_PROTECTION 1
/* Implementation supports `poll' function. */
#define _POSIX_POLL 1
/* Implementation supports `select' and `pselect' functions. */
#define _POSIX_SELECT 1
/* Only root can change owner of file. */
#define _POSIX_CHOWN_RESTRICTED 1
/* `c_cc' member of 'struct termios' structure can be disabled by
using the value _POSIX_VDISABLE. */
#define _POSIX_VDISABLE '\0'
/* Filenames are not silently truncated. */
#define _POSIX_NO_TRUNC 1
/* X/Open realtime support is available. */
#define _XOPEN_REALTIME 1
/* X/Open realtime thread support is available. */
#define _XOPEN_REALTIME_THREADS 1
/* XPG4.2 shared memory is supported. */
#define _XOPEN_SHM 1
/* Tell we have POSIX threads. */
#define _POSIX_THREADS 1
/* We have the reentrant functions described in POSIX. */
#define _POSIX_REENTRANT_FUNCTIONS 1
#define _POSIX_THREAD_SAFE_FUNCTIONS 1
/* We provide priority scheduling for threads. */
#define _POSIX_THREAD_PRIORITY_SCHEDULING 1
/* We support POSIX.1b semaphores, but only the non-shared form for now. */
/*#define _POSIX_SEMAPHORES 1 XXX We are not quite there now. */
/* Real-time signals are supported. */
#define _POSIX_REALTIME_SIGNALS 1
/* We support asynchronous I/O. */
#define _POSIX_ASYNCHRONOUS_IO 1
#endif /* posix_opt.h */

View File

@ -0,0 +1,3 @@
# Local configure fragment for sysdeps/unix/sysv/linux.
DEFINES="$DEFINES -D_LIBC_REENTRANT"

87
linuxthreads/weaks.c Normal file
View File

@ -0,0 +1,87 @@
/* The weak pthread functions for Linux.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
extern int __pthread_return_0 __P ((void));
extern int __pthread_return_1 __P ((void));
extern void __pthread_return_void __P ((void));
/* Those are pthread functions which return 0 if successful. */
weak_alias (__pthread_return_0, pthread_attr_init)
weak_alias (__pthread_return_0, pthread_attr_destroy)
weak_alias (__pthread_return_0, pthread_attr_setdetachstate)
weak_alias (__pthread_return_0, pthread_attr_getdetachstate)
weak_alias (__pthread_return_0, pthread_attr_setschedparam)
weak_alias (__pthread_return_0, pthread_attr_getschedparam)
weak_alias (__pthread_return_0, pthread_attr_setschedpolicy)
weak_alias (__pthread_return_0, pthread_attr_getschedpolicy)
weak_alias (__pthread_return_0, pthread_attr_setinheritsched)
weak_alias (__pthread_return_0, pthread_attr_getinheritsched)
weak_alias (__pthread_return_0, pthread_attr_setscope)
weak_alias (__pthread_return_0, pthread_attr_getscope)
weak_alias (__pthread_return_0, pthread_mutex_init)
weak_alias (__pthread_return_0, pthread_mutex_destroy)
weak_alias (__pthread_return_0, pthread_mutex_lock)
weak_alias (__pthread_return_0, pthread_mutex_unlock)
weak_alias (__pthread_return_0, pthread_mutexattr_setkind_np)
weak_alias (__pthread_return_0, pthread_mutexattr_getkind_np)
weak_alias (__pthread_return_0, pthread_condattr_init)
weak_alias (__pthread_return_0, pthread_condattr_destroy)
weak_alias (__pthread_return_0, pthread_setschedparam)
weak_alias (__pthread_return_0, pthread_getschedparam)
weak_alias (__pthread_return_0, pthread_setcancelstate)
weak_alias (__pthread_return_0, pthread_setcanceltype)
weak_alias (__pthread_return_0, pthread_self)
weak_alias (__pthread_return_0, pthread_cond_init)
weak_alias (__pthread_return_0, pthread_cond_destroy)
weak_alias (__pthread_return_0, pthread_cond_wait)
weak_alias (__pthread_return_0, pthread_cond_signal)
weak_alias (__pthread_return_0, pthread_cond_broadcast)
/* Those are pthread functions which return 1 if successful. */
weak_alias (__pthread_return_1, pthread_equal)
/* pthread_exit () is a special case. */
void
weak_function
pthread_exit (void *retval)
{
exit (EXIT_SUCCESS);
}
int
__pthread_return_0 (void)
{
return 0;
}
int
__pthread_return_1 (void)
{
return 1;
}
void
__pthread_return_void (void)
{
}

183
linuxthreads/wrapsyscall.c Normal file
View File

@ -0,0 +1,183 @@
/* Wrapper arpund system calls to provide cancelation points.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <stdarg.h>
#include <stddef.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/socket.h>
#ifndef PIC
/* We need a hook to force this file to be linked in when static
libpthread is used. */
const int __pthread_provide_wrappers = 0;
#endif
#define CANCELABLE_SYSCALL(res_type, name, param_list, params) \
res_type __libc_##name param_list; \
res_type \
name param_list \
{ \
res_type result; \
int oldtype; \
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); \
result = __libc_##name params; \
pthread_setcanceltype (oldtype, NULL); \
return result; \
}
#define CANCELABLE_SYSCALL_VA(res_type, name, param_list, params, last_arg) \
res_type __libc_##name param_list; \
res_type \
name param_list \
{ \
res_type result; \
int oldtype; \
va_list ap; \
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); \
va_start (ap, last_arg); \
result = __libc_##name params; \
va_end (ap); \
pthread_setcanceltype (oldtype, NULL); \
return result; \
}
/* close(2). */
CANCELABLE_SYSCALL (int, close, (int fd), (fd))
strong_alias (close, __close)
/* fcntl(2). */
CANCELABLE_SYSCALL_VA (int, fcntl, (int fd, int cmd, ...),
(fd, cmd, va_arg (ap, long int)), cmd)
strong_alias (fcntl, __fcntl)
/* fsync(2). */
CANCELABLE_SYSCALL (int, fsync, (int fd), (fd))
/* lseek(2). */
CANCELABLE_SYSCALL (off_t, lseek, (int fd, off_t offset, int whence),
(fd, offset, whence))
strong_alias (lseek, __lseek)
/* msync(2). */
CANCELABLE_SYSCALL (int, msync, (const void *start, size_t length, int flags),
(start, length, flags))
/* nanosleep(2). */
CANCELABLE_SYSCALL (int, nanosleep, (const struct timespec *requested_time,
struct timespec *remaining),
(requested_time, remaining))
/* open(2). */
CANCELABLE_SYSCALL_VA (int, open, (const char *pathname, int flags, ...),
(pathname, flags, va_arg (ap, mode_t)), flags)
strong_alias (open, __open)
/* pause(2). */
CANCELABLE_SYSCALL (int, pause, (void), ())
/* read(2). */
CANCELABLE_SYSCALL (ssize_t, read, (int fd, void *buf, size_t count),
(fd, buf, count))
strong_alias (read, __read)
/* system(3). */
CANCELABLE_SYSCALL (int, system, (const char *line), (line))
/* tcdrain(2). */
CANCELABLE_SYSCALL (int, tcdrain, (int fd), (fd))
/* wait(2). */
CANCELABLE_SYSCALL (__pid_t, wait, (__WAIT_STATUS_DEFN stat_loc), (stat_loc))
strong_alias (wait, __wait)
/* waitpid(2). */
CANCELABLE_SYSCALL (__pid_t, waitpid, (__pid_t pid, int *stat_loc,
int options),
(pid, stat_loc, options))
/* write(2). */
CANCELABLE_SYSCALL (ssize_t, write, (int fd, const void *buf, size_t n),
(fd, buf, n))
strong_alias (write, __write)
/* The following system calls are thread cancellation points specified
in XNS. */
/* accept(2). */
CANCELABLE_SYSCALL (int, accept, (int fd, __SOCKADDR_ARG addr,
socklen_t *addr_len),
(fd, addr, addr_len))
/* connect(2). */
CANCELABLE_SYSCALL (int, connect, (int fd, __CONST_SOCKADDR_ARG addr,
socklen_t len),
(fd, addr, len))
strong_alias (connect, __connect)
/* recv(2). */
CANCELABLE_SYSCALL (int, recv, (int fd, __ptr_t buf, size_t n, int flags),
(fd, buf, n, flags))
/* recvfrom(2). */
CANCELABLE_SYSCALL (int, recvfrom, (int fd, __ptr_t buf, size_t n, int flags,
__SOCKADDR_ARG addr, socklen_t *addr_len),
(fd, buf, n, flags, addr, addr_len))
/* recvmsg(2). */
CANCELABLE_SYSCALL (int, recvmsg, (int fd, struct msghdr *message, int flags),
(fd, message, flags))
/* send(2). */
CANCELABLE_SYSCALL (int, send, (int fd, const __ptr_t buf, size_t n,
int flags),
(fd, buf, n, flags))
strong_alias (send, __send)
/* sendmsg(2). */
CANCELABLE_SYSCALL (int, sendmsg, (int fd, const struct msghdr *message,
int flags),
(fd, message, flags))
/* sendto(2). */
CANCELABLE_SYSCALL (int, sendto, (int fd, const __ptr_t buf, size_t n,
int flags, __CONST_SOCKADDR_ARG addr,
socklen_t addr_len),
(fd, buf, n, flags, addr, addr_len))