LinuxThreads library.
authordrepper <drepper>
Wed, 11 Mar 1998 12:42:22 +0000 (12:42 +0000)
committerdrepper <drepper>
Wed, 11 Mar 1998 12:42:22 +0000 (12:42 +0000)
87 files changed:
linuxthreads/Banner [new file with mode: 0644]
linuxthreads/ChangeLog [new file with mode: 0644]
linuxthreads/Changes [new file with mode: 0644]
linuxthreads/Examples/Makefile [new file with mode: 0644]
linuxthreads/Examples/ex1.c [new file with mode: 0644]
linuxthreads/Examples/ex2.c [new file with mode: 0644]
linuxthreads/Examples/ex3.c [new file with mode: 0644]
linuxthreads/Examples/ex4.c [new file with mode: 0644]
linuxthreads/Examples/ex5.c [new file with mode: 0644]
linuxthreads/FAQ.html [new file with mode: 0644]
linuxthreads/LICENSE [new file with mode: 0644]
linuxthreads/Makefile [new file with mode: 0644]
linuxthreads/README [new file with mode: 0644]
linuxthreads/README.Xfree3.2 [new file with mode: 0644]
linuxthreads/attr.c [new file with mode: 0644]
linuxthreads/cancel.c [new file with mode: 0644]
linuxthreads/condvar.c [new file with mode: 0644]
linuxthreads/configure [new file with mode: 0755]
linuxthreads/errno.c [new file with mode: 0644]
linuxthreads/internals.h [new file with mode: 0644]
linuxthreads/join.c [new file with mode: 0644]
linuxthreads/libpthread.map [new file with mode: 0644]
linuxthreads/lockfile.c [new file with mode: 0644]
linuxthreads/man/Makefile [new file with mode: 0644]
linuxthreads/man/pthread_atfork.man [new file with mode: 0644]
linuxthreads/man/pthread_attr_init.man [new file with mode: 0644]
linuxthreads/man/pthread_cancel.man [new file with mode: 0644]
linuxthreads/man/pthread_cleanup_push.man [new file with mode: 0644]
linuxthreads/man/pthread_cond_init.man [new file with mode: 0644]
linuxthreads/man/pthread_condattr_init.man [new file with mode: 0644]
linuxthreads/man/pthread_create.man [new file with mode: 0644]
linuxthreads/man/pthread_detach.man [new file with mode: 0644]
linuxthreads/man/pthread_equal.man [new file with mode: 0644]
linuxthreads/man/pthread_exit.man [new file with mode: 0644]
linuxthreads/man/pthread_join.man [new file with mode: 0644]
linuxthreads/man/pthread_key_create.man [new file with mode: 0644]
linuxthreads/man/pthread_kill_other_threads_np.man [new file with mode: 0644]
linuxthreads/man/pthread_mutex_init.man [new file with mode: 0644]
linuxthreads/man/pthread_mutexattr_init.man [new file with mode: 0644]
linuxthreads/man/pthread_once.man [new file with mode: 0644]
linuxthreads/man/pthread_self.man [new file with mode: 0644]
linuxthreads/man/pthread_setschedparam.man [new file with mode: 0644]
linuxthreads/man/pthread_sigmask.man [new file with mode: 0644]
linuxthreads/man/sem_init.man [new file with mode: 0644]
linuxthreads/man/troffprepro [new file with mode: 0755]
linuxthreads/manager.c [new file with mode: 0644]
linuxthreads/mutex.c [new file with mode: 0644]
linuxthreads/ptfork.c [new file with mode: 0644]
linuxthreads/pthread.c [new file with mode: 0644]
linuxthreads/queue.h [new file with mode: 0644]
linuxthreads/restart.h [new file with mode: 0644]
linuxthreads/rwlock.c [new file with mode: 0644]
linuxthreads/semaphore.c [new file with mode: 0644]
linuxthreads/semaphore.h [new file with mode: 0644]
linuxthreads/shlib-versions [new file with mode: 0644]
linuxthreads/signals.c [new file with mode: 0644]
linuxthreads/specific.c [new file with mode: 0644]
linuxthreads/spinlock.h [new file with mode: 0644]
linuxthreads/sysdeps/alpha/bits/semaphore.h [new file with mode: 0644]
linuxthreads/sysdeps/alpha/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/arm/Implies [new file with mode: 0644]
linuxthreads/sysdeps/arm/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/i386/Implies [new file with mode: 0644]
linuxthreads/sysdeps/i386/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/m68k/Implies [new file with mode: 0644]
linuxthreads/sysdeps/m68k/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/mips/Implies [new file with mode: 0644]
linuxthreads/sysdeps/mips/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/powerpc/Implies [new file with mode: 0644]
linuxthreads/sysdeps/powerpc/bits/semaphore.h [new file with mode: 0644]
linuxthreads/sysdeps/powerpc/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/pthread/Makefile [new file with mode: 0644]
linuxthreads/sysdeps/pthread/bits/libc-lock.h [new file with mode: 0644]
linuxthreads/sysdeps/pthread/bits/stdio-lock.h [new file with mode: 0644]
linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h [new file with mode: 0644]
linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h [new file with mode: 0644]
linuxthreads/sysdeps/pthread/pthread.h [new file with mode: 0644]
linuxthreads/sysdeps/sparc/sparc32/Implies [new file with mode: 0644]
linuxthreads/sysdeps/sparc/sparc32/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/sparc/sparc64/Implies [new file with mode: 0644]
linuxthreads/sysdeps/sparc/sparc64/pt-machine.h [new file with mode: 0644]
linuxthreads/sysdeps/unix/sysv/linux/Implies [new file with mode: 0644]
linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h [new file with mode: 0644]
linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h [new file with mode: 0644]
linuxthreads/sysdeps/unix/sysv/linux/configure [new file with mode: 0644]
linuxthreads/weaks.c [new file with mode: 0644]
linuxthreads/wrapsyscall.c [new file with mode: 0644]

diff --git a/linuxthreads/Banner b/linuxthreads/Banner
new file mode 100644 (file)
index 0000000..c1a3821
--- /dev/null
@@ -0,0 +1 @@
+linuxthreads-0.7 by Xavier Leroy
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
new file mode 100644 (file)
index 0000000..696d151
--- /dev/null
@@ -0,0 +1,271 @@
+1998-03-11 00:42  Wolfram Gloger  <wmglo@dent.med.uni-muenchen.de>
+
+       * linuxthreads/manager.c: Enable resetting of the thread
+       scheduling policy to SCHED_OTHER when the parent thread
+       has a different one.
+
+1998-02-01 13:51  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/unix/sysv/linux/bits/posix_opt.h: Define
+       _POSIX_ASYNCHRONOUS_IO.
+
+       * sysdeps/pthread/pthread.h: Define bits for Unix98 variants of
+       mutexes.
+       * mutex.c: Implement new mutex types.
+
+       * internals.h: Include <signal.h>.
+
+       * libpthread.map: Add __erno_location and __h_errno_location.
+
+       * errno.c: Return pointer to variable actually in use.  This might
+       not be the one in the thread structure.
+       * internals.h (struct _pthread_descr_struct): Add new fields p_errnop
+       and p_h_errnop.
+       * manager.c (__pthread_manager): Set p_errnop and p_h_errnop member
+       of manager thread structure.
+       (pthread_handle_create): Set p_errnop and p_h_errnop members for new
+       thread.
+       * pthread.c: Adapt initializer for thread structures.
+       (__pthread_initial_thread): Set p_errnop and p_h_errnop member.
+       (__pthread_reset_main_thread): Reset p_errnop and p_h_errnop of
+       current thread to global variables.
+
+1998-01-31 17:27  Ulrich Drepper  <drepper@cygnus.com>
+
+       * rwlock.c: New file.
+       * Makefile (libpthread-routines): Add rwlock.
+       * sysdeps/pthread/pthread.h: Define data structures and declare
+       functions.
+       * libpthread.map: Add new functions.
+
+1997-12-18 13:50  Philip Blundell  <pb@nexus.co.uk>
+
+       * sysdeps/arm/pt-machine.h: New file; add ARM support.
+       * sysdeps/arm/Implies: likewise.
+       * README: Document it.
+
+1997-12-13  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * signals.c: Remove unneeded initializer for sigwaited, saving a
+       warning.
+
+1997-04-11 01:18  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * semaphore.c (sem_init): Set sem_spinlock only if available.
+
+1997-12-04 01:48  Ulrich Drepper  <drepper@cygnus.com>
+
+       * mutex.c: Implement PTHREAD_MUTEX_CHECKERROR.
+       * sysdeps/pthread/pthread.h: Define PTHREAD_MUTEX_CHECKERROR.
+
+       * Makefile: Update from LinuxThreads 0.7.
+       * internals.h. Likewise.
+       * manager.c: Likewise.
+       * mutex.c: Likewise.
+       * pthread.c: Likewise.
+       * signals.c: Likewise.
+       * specific.c: Likewise.
+       * Examples/ex3.c: Likewise.
+
+1997-11-20 18:13  Ulrich Drepper  <drepper@cygnus.com>
+
+       * pthread.c (__pthread_reset_main_thread): Close pipe only if still
+       open.
+
+1997-10-29 05:38  Ulrich Drepper  <drepper@cygnus.com>
+
+       * wrapsyscall.c: Add socket functions which are also cancelation
+       points.
+
+1997-10-19 21:40  Wolfram Gloger  <wg@wolfram.dent.med.uni-muenchen.de>
+
+       * specific.c (__libc_internal_tsd_set, __libc_internal_tsd_get):
+       New functions for fast thread specific data within libc.
+
+       * internals.h: Add new array p_libc_specific to struct
+       _pthread_descr_struct.
+
+       * sysdeps/pthread/bits/libc-lock.h: Declare new functions.
+
+1997-10-13 05:39  Ulrich Drepper  <drepper@cygnus.com>
+
+       * semaphore.h: Add __BEGIN_DECLS/__END_DECLS.
+       Reported by Ralf Corsepius <corsepiu@faw.uni-ulm.de>.
+
+1997-08-29 03:05  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h (struct _pthread_descr_struct): Add definitions for
+       two-level specific key handling.
+       * manager.c (pthread_handle_create): Initialize specific memory array.
+       * specific.c: Implement two-level key handling.
+       * weaks.c: Don't provide dummy key handling.
+       * sysdeps/pthread/bits/libc-lock.h: Typedef __libc_lock_t (no #define).
+       Add definition of __libc_key_t.
+       * sysdeps/unix/sysv/linux/bits/local_lim.h: Define PTHREAD_KEYS_MAX
+       as 1024.
+       Add definition of _POSIX_THREAD_DESTRUCTOR_ITERATIONS and
+       PTHREAD_DESTRUCTOR_ITERATIONS.
+
+       * manager.c (pthread_handle_create): Compare mmap result with
+       MAP_FAILED.
+
+       * ptfork.c: Rename to __pthread_atfork and make old name a weak alias.
+       * sysdeps/pthread/bits/pthread.h: Add prototype for __pthread_atfork.
+
+1997-08-22 19:04  Richard Henderson  <rth@cygnus.com>
+
+       sysdeps/sparc -> sysdeps/sparc/sparc32
+       sysdeps/sparc64 -> sysdeps/sparc/sparc64
+
+       * internals.h: Change definition of THREAD_SELF to be an expression,
+       not a statement that did a return.
+       * sysdeps/alpha/pt-machine.h (THREAD_SELF): Update accordingly.
+       * sysdeps/sparc/sparc32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF):
+       Follow Solaris and use a "system reserved" register (%g6) to hold
+       the thread descriptor.
+       * sysdeps/sparc/sparc64/pt-machine.h: Likewise.
+
+1997-08-03 00:09  Ulrich Drepper  <drepper@cygnus.com>
+
+       * mutex.c: Correct pthread_once.  Patch by Xavier Leroy.
+       * sysdeps/pthread/pthread.h: Add prototype for __pthread_once.
+       * sysdeps/pthread/bits/pthread.h: Add macros for __libc_once.
+
+       * semaphore.c: Include spinlock.h only when needed.
+
+       * specific.c (__pthread_setsepcific, __pthread_getspecific): Reject
+       keys for entries not in use.
+
+       * weaks.c: Implement key handling functions for real.
+
+1997-06-29  01:04  Richard Henderson  <richard@gnu.ai.mit.edu>
+
+       Initial sparc64-linux support:
+       * linuxthreads/sysdeps/sparc64/Implies: New file.
+       * linuxthreads/sysdeps/sparc64/pt-machine.h: Likewise.
+
+1997-06-29 00:48  Ulrich Drepper  <drepper@cygnus.com>
+
+       * semaphore.c: Include spinlock.h at correct place.
+       Patch by HJ Lu.
+
+1997-06-13 10:06  Richard Henderson  <rth@tamu.edu>
+
+       The Great Bit File Move:
+       * sysdeps/alpha/semaphorebits.h: -> .../bits/semaphore.h.
+       * sysdeps/powerpc/semaphorebits.h: Likewise.
+       * sysdeps/pthread/cmpxchg/semaphorebits.h: Likewise.
+       * sysdeps/pthread/no-cmpxchg/semaphorebits.h: Likewise.
+       * sysdeps/pthread/libc-lock.h: -> bits/
+       * sysdeps/pthread/stdio-lock.h: Likewise.
+       * sysdeps/unix/sysv/linux/local_lim.h: Likewise.
+       * sysdeps/unix/sysv/linux/posix_opt.h: Likewise.
+       * semaphore.h: Likewise.
+       * sysdeps/pthread/pthread.h: Likewise.
+
+       * lockfile.c: <foo.h> -> <bits/foo.h>.
+       * semaphore.h: Likewise.
+
+       * Makefile: (headers): foo.h -> bits/foo.h.
+       * sysdeps/pthread/Makefile: Likewise.
+
+1997-04-11 01:18  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * semaphore.c (sem_init): Set sem_spinlock only if available.
+
+       * sysdeps/m68k/pt-machine.h (testandset, __compare_and_swap): Fix
+       asm constraints.
+
+1997-04-09 03:00  Ulrich Drepper  <drepper@cygnus.com>
+
+       Update from LinuxThreads 0.6.
+
+       * attr.c (pthread_attr_getdetachstate): Use __sched_get_priority_max
+       and __sched_get_priority_min instead of names without `__'.
+
+       * manager.c: Rewrite large parts to implement opaque pthread_t.
+
+       * cancel.c: Adapt for opaque pthread_t type.
+       * condvar.c: Likewise.
+       * errno.c: Likewise.
+       * join.c: Likewise.
+       * mutex.c: Likewise.
+       * pthread.c: Likewise.
+       * signals.c: Likewise.
+       * specific.c: Likewise.
+       * restart.h: Likewise.
+       * queue.h: Likewise.
+       * Examples/ex3.c: Likewise.
+       * Examples/ex4.c: Likewise.
+       * sysdeps/pthread/pthread.h: Likewise.
+
+       * pthread.c: Accumulate time for all threads in thread manager.
+
+       * semaphore.c: Implement fallback implementation for architectures
+       sometimes missing compare-exchange operations.
+
+       * cancel.c (pthread_cancel): Validate handle argument.
+       * join.c (pthread_join): Likewise.
+       (pthread_detach): Likewise.
+       * signals.c (pthread_kill): Likewise.
+
+       * spinlock.h (acquire): Use __sched_yield not sched_yield.
+
+       * queue.h (enqueue): Enqueue thread according to priority.
+
+       * internals.c (struct pthread_start_args): New struct for passing
+       args to cloning function.
+       (struct _pthread): Rename to _pthread_descr_struct and adapt for
+       opaque pthread_t.
+
+       * Examples/Makefile (clean): Pass -f option to rm.
+
+       * sysdeps/i386/pt-machine.h: Add check for compare-exchange instruction
+       and define TEST_FOR_COMPARE_AND_SWAP.
+       * sysdeps/i386/i486/pt-machine.h: Removed.
+
+       * sysdeps/unix/sysv/linux/local_lim.h (PTHREAD_THREADS_MAX): Increase
+       to 1024.
+
+1997-04-04 16:38  Ulrich Drepper  <drepper@cygnus.com>
+
+       * restart.h (suspend): Clear p_signal before suspending.
+       (suspend_with_cancellation): Likewise.
+       Patch by Xavier Leroy <Xavier.Leroy@inria.fr>.
+
+       * weaks.c: Make __pthread_key_create return 1.
+       * sysdeps/pthread/libc-lock.h: Define __libc_key_create,
+       __libc_getspecific, __libc_setspecific, and __libc_key_t.
+       * sysdeps/pthread/stdio-lock.h: Don't care for implementation not
+       using libio.
+
+1997-03-19 15:13  Miguel de Icaza  <miguel@nuclecu.unam.mx>
+
+       * sysdeps/sparc/pt-machine (RELEASE): Fix.
+
+1997-03-01 07:55  Geoff Keating  <geoffk@ozemail.com.au>
+
+       * sysdeps/powerpc/Implies: Added.
+       * sysdeps/powerpc/pt-machine.h: Added.
+       * sysdeps/powerpc/semaphorebits.h: Added.
+
+1997-01-22 01:22  Ulrich Drepper  <drepper@cygnus.com>
+
+       * linuxtheads/pthread.c (__pthread_initial_thread): Correct
+       initializer.
+       (__pthread_manager_thread): Likewise.
+       Reported by Andreas Jaeger.
+
+1997-01-18 22:15  Richard Henderson  <rth@tamu.edu>
+
+       Since sigset_t no longer fits in a register, we can't pass in the
+       thread's initial mask so easily.  Take this opportunity to simplify
+       the clone implementation by only accepting a single void* argument.
+
+       * linuxthreads/manager.c (__pthread_manager): Put thread vitals
+       in the thread struct instead of as arguments through clone.
+       (pthread_start_thread): Look for them there.
+       * linuxthreads/internals.h (struct _pthread): Add p_initial_fn,
+       p_initial_fn_arg, p_initial_mask.  Fix __pthread_manager proto.
+       * linuxthreads/pthread.c (pthread_initialize_manager): Revise
+       clone invocation.
diff --git a/linuxthreads/Changes b/linuxthreads/Changes
new file mode 100644 (file)
index 0000000..8ec26c9
--- /dev/null
@@ -0,0 +1,73 @@
+Release 0.7:
+- Destructors for thread-specific data now conform to the POSIX semantics
+  (call destructors again if non-NULL TSD remains after a round of
+   destruction).
+- Implemented thread-specific data as a sparse array, allows more TSD keys
+  and smaller thread descriptors (Ulrich Drepper).
+- Added "error checking" mutexes.
+- Protect against multiple sigwait() on the same signals.
+- Simplified implementation of semaphores when compare_and_swap is 
+  not available.
+- Fixed bug in fork() where stdin was closed if fork() was called before
+  the first pthread_create().
+- Fixed bug in the gethostby*_r functions (bad result if null bytes
+  in addresses).
+- Typos in manual pages corrected.
+- First cut at a PowerPC port (not working yet, runs into problems
+  with gcc and with the C library).
+
+Release 0.6:
+- Validation of thread identifiers: no more crashes when operating on
+  a thread that has exited (based on Pavel Krauz's ideas).
+- Added fallback implementation of semaphores for the 386 and the
+  Sparc. 
+- Fixed a bug in signal handling causing false restarts of suspended
+  threads.
+- Fixed a bug in realtime scheduling causing all threads to have
+  default scheduling on Ix86 with libc5.
+- With realtime scheduling, unlocking a mutex now restarts the
+  highest priority thread waiting on the mutex, not the
+  first-suspended thread (Richard Neitzel).
+- Timing a process now returns cumulative times for all threads, not
+  just times for the initial thread (suggested by Wolfram Gloger).
+- Cleaned up name space (internal defs prefixed by __, weak aliases
+  for non-portable extensions).
+- MIPS port (contributed by Ralf Baechle).
+
+Release 0.5:
+- Signal-safe semaphores a la POSIX 1003.1b added.
+- Locking bug in pthread_mutex_trylock over recursive mutexes fixed.
+- Race conditions in thread cancellation fixed.
+- Sparc port (contributed by Miguel de Icaza).
+- Support for getpwnam_r and getpwuid_r.
+- Added pthread_kill_other_threads_np to be used in conjunction with
+  exec*().
+
+Release 0.4:
+- Manual pages for all functions.
+- Synchronization bug causing accumulation of zombie processes fixed.
+- Race condition in pthread_cond_timedwait fixed.
+- Recursive mutexes are back by popular demand.
+- Partial support for realtime scheduling (initiated by Richard Neitzel).
+- pthread.h cleaned up a lot: now C++ compatible, added missing "const" 
+  qualifiers, added short documentation, put to GNU libc standards
+  for name space pollution (Ulrich Drepper).
+- Motorola 68k port (contributed by Andreas Schwab).
+- Interaction with fork(2) cleaned up a lot.
+
+Release 0.3:
+- Thread creation and reclaimation now performed by a centralized
+  "thread manager" thread.
+- Removed recursive mutexes to make regular mutexes more efficient.
+- Now available as a shared library (contributed by Richard Henderson).
+- Alpha port (contributed by Richard Henderson).
+- Fixed many small discrepancies with Posix 1003.1c.
+- Put under the LGPL instead of the GPL.
+
+Release 0.2:
+- Reentrant libc functions (adapted from libc 5.3.9 by Peeter Joot)
+- pthread_cond_wait did not reacquire the mutex correctly on return
+- More efficient pthread_cond_broadcast
+
+Release 0.1:
+- First public release
diff --git a/linuxthreads/Examples/Makefile b/linuxthreads/Examples/Makefile
new file mode 100644 (file)
index 0000000..c68b367
--- /dev/null
@@ -0,0 +1,15 @@
+CC=gcc
+CFLAGS=-g -O -Wall -I.. -D_REENTRANT
+LIBPTHREAD=../libpthread.a
+
+PROGS=ex1 ex2 ex3 ex4 ex5 proxy
+
+all: $(PROGS)
+
+.c:
+       $(CC) $(CFLAGS) -o $* $*.c $(LIBPTHREAD)
+
+$(PROGS):
+
+clean:
+       rm -f $(PROGS)
diff --git a/linuxthreads/Examples/ex1.c b/linuxthreads/Examples/ex1.c
new file mode 100644 (file)
index 0000000..c399fab
--- /dev/null
@@ -0,0 +1,36 @@
+/* Creates two threads, one printing 10000 "a"s, the other printing
+   10000 "b"s.
+   Illustrates: thread creation, thread joining. */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "pthread.h"
+
+void * process(void * arg)
+{
+  int i;
+  fprintf(stderr, "Starting process %s\n", (char *) arg);
+  for (i = 0; i < 10000; i++) {
+    write(1, (char *) arg, 1);
+  }
+  return NULL;
+}
+
+int main()
+{
+  int retcode;
+  pthread_t th_a, th_b;
+  void * retval;
+
+  retcode = pthread_create(&th_a, NULL, process, "a");
+  if (retcode != 0) fprintf(stderr, "create a failed %d\n", retcode);
+  retcode = pthread_create(&th_b, NULL, process, "b");
+  if (retcode != 0) fprintf(stderr, "create b failed %d\n", retcode);
+  retcode = pthread_join(th_a, &retval);
+  if (retcode != 0) fprintf(stderr, "join a failed %d\n", retcode);
+  retcode = pthread_join(th_b, &retval);
+  if (retcode != 0) fprintf(stderr, "join b failed %d\n", retcode);
+  return 0;
+}
+
diff --git a/linuxthreads/Examples/ex2.c b/linuxthreads/Examples/ex2.c
new file mode 100644 (file)
index 0000000..3f7f115
--- /dev/null
@@ -0,0 +1,116 @@
+/* The classic producer-consumer example.
+   Illustrates mutexes and conditions.
+   All integers between 0 and 9999 should be printed exactly twice,
+   once to the right of the arrow and once to the left. */
+
+#include <stdio.h>
+#include "pthread.h"
+
+#define BUFFER_SIZE 16
+
+/* Circular buffer of integers. */
+
+struct prodcons {
+  int buffer[BUFFER_SIZE];      /* the actual data */
+  pthread_mutex_t lock;         /* mutex ensuring exclusive access to buffer */
+  int readpos, writepos;        /* positions for reading and writing */
+  pthread_cond_t notempty;      /* signaled when buffer is not empty */
+  pthread_cond_t notfull;       /* signaled when buffer is not full */
+};
+
+/* Initialize a buffer */
+
+void init(struct prodcons * b)
+{
+  pthread_mutex_init(&b->lock, NULL);
+  pthread_cond_init(&b->notempty, NULL);
+  pthread_cond_init(&b->notfull, NULL);
+  b->readpos = 0;
+  b->writepos = 0;
+}
+
+/* Store an integer in the buffer */
+
+void put(struct prodcons * b, int data)
+{
+  pthread_mutex_lock(&b->lock);
+  /* Wait until buffer is not full */
+  while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
+    pthread_cond_wait(&b->notfull, &b->lock);
+    /* pthread_cond_wait reacquired b->lock before returning */
+  }
+  /* Write the data and advance write pointer */
+  b->buffer[b->writepos] = data;
+  b->writepos++;
+  if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
+  /* Signal that the buffer is now not empty */
+  pthread_cond_signal(&b->notempty);
+  pthread_mutex_unlock(&b->lock);
+}
+
+/* Read and remove an integer from the buffer */
+
+int get(struct prodcons * b)
+{
+  int data;
+  pthread_mutex_lock(&b->lock);
+  /* Wait until buffer is not empty */
+  while (b->writepos == b->readpos) {
+    pthread_cond_wait(&b->notempty, &b->lock);
+  }
+  /* Read the data and advance read pointer */
+  data = b->buffer[b->readpos];
+  b->readpos++;
+  if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
+  /* Signal that the buffer is now not full */
+  pthread_cond_signal(&b->notfull);
+  pthread_mutex_unlock(&b->lock);
+  return data;
+}
+
+/* A test program: one thread inserts integers from 1 to 10000,
+   the other reads them and prints them. */
+
+#define OVER (-1)
+
+struct prodcons buffer;
+
+void * producer(void * data)
+{
+  int n;
+  for (n = 0; n < 10000; n++) {
+    printf("%d --->\n", n);
+    put(&buffer, n);
+  }
+  put(&buffer, OVER);
+  return NULL;
+}
+
+void * consumer(void * data)
+{
+  int d;
+  while (1) {
+    d = get(&buffer);
+    if (d == OVER) break;
+    printf("---> %d\n", d);
+  }
+  return NULL;
+}
+
+int main()
+{
+  pthread_t th_a, th_b;
+  void * retval;
+
+  init(&buffer);
+  /* Create the threads */
+  pthread_create(&th_a, NULL, producer, 0);
+  pthread_create(&th_b, NULL, consumer, 0);
+  /* Wait until producer and consumer finish. */
+  pthread_join(th_a, &retval);
+  pthread_join(th_b, &retval);
+  return 0;
+}
+  
+
+
diff --git a/linuxthreads/Examples/ex3.c b/linuxthreads/Examples/ex3.c
new file mode 100644 (file)
index 0000000..002bc90
--- /dev/null
@@ -0,0 +1,144 @@
+/* Multi-thread searching.
+   Illustrates: thread cancellation, cleanup handlers. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+/* Defines the number of searching threads */
+#define NUM_THREADS 5
+
+/* Function prototypes */
+void *search(void *);
+void print_it(void *);
+
+/* Global variables */
+pthread_t threads[NUM_THREADS];
+pthread_mutex_t lock;
+int tries;
+
+int main(argc, argv)
+     int argc;
+     char ** argv;
+{
+  int i;
+  int pid;
+
+  /* create a number to search for */
+  pid = getpid();
+  printf("Searching for the number = %d...\n", pid);
+
+  /* Initialize the mutex lock */
+  pthread_mutex_init(&lock, NULL); 
+
+  /* Create the searching threads */
+  for (i=0; i<NUM_THREADS; i++)
+    pthread_create(&threads[i], NULL, search, (void *)pid);
+
+  /* Wait for (join) all the searching threads */
+  for (i=0; i<NUM_THREADS; i++) 
+    pthread_join(threads[i], NULL);
+
+  printf("It took %d tries to find the number.\n", tries);
+
+  /* Exit the program */
+  return 0;
+}
+
+/* This is the cleanup function that is called 
+   when the threads are cancelled */
+
+void print_it(void *arg)
+{
+  int *try = (int *) arg;
+  pthread_t tid;
+
+  /* Get the calling thread's ID */
+  tid = pthread_self();
+
+  /* Print where the thread was in its search when it was cancelled */
+  printf("Thread %lx was canceled on its %d try.\n", tid, *try); 
+}
+
+/* This is the search routine that is executed in each thread */
+
+void *search(void *arg)
+{
+  int num = (int) arg;
+  int i, j, ntries;
+  pthread_t tid;
+
+  /* get the calling thread ID */
+  tid = pthread_self();
+
+  /* use the thread ID to set the seed for the random number generator */
+  /* Since srand and rand are not thread-safe, serialize with lock */
+  pthread_mutex_lock(&lock);
+  srand((int)tid);
+  i = rand() & 0xFFFFFF;
+  pthread_mutex_unlock(&lock);
+  ntries = 0;
+
+  /* Set the cancellation parameters --
+     - Enable thread cancellation 
+     - Defer the action of the cancellation */
+
+  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+
+  /* Push the cleanup routine (print_it) onto the thread
+     cleanup stack.  This routine will be called when the 
+     thread is cancelled.  Also note that the pthread_cleanup_push
+     call must have a matching pthread_cleanup_pop call.  The
+     push and pop calls MUST be at the same lexical level 
+     within the code */
+
+  /* Pass address of `ntries' since the current value of `ntries' is not 
+     the one we want to use in the cleanup function */
+
+  pthread_cleanup_push(print_it, (void *)&ntries);
+
+  /* Loop forever */
+  while (1) {
+    i = (i + 1) & 0xFFFFFF;
+    ntries++;
+
+    /* Does the random number match the target number? */
+    if (num == i) {
+      /* Try to lock the mutex lock --
+         if locked, check to see if the thread has been cancelled
+         if not locked then continue */
+      while (pthread_mutex_trylock(&lock) == EBUSY)
+        pthread_testcancel();
+
+      /* Set the global variable for the number of tries */
+      tries = ntries;
+      printf("Thread %lx found the number!\n", tid);
+
+      /* Cancel all the other threads */
+      for (j=0; j<NUM_THREADS; j++) 
+        if (threads[j] != tid) pthread_cancel(threads[j]);
+
+      /* Break out of the while loop */
+      break;
+    }
+
+    /* Every 100 tries check to see if the thread has been cancelled. */
+    if (ntries % 100 == 0) {
+      pthread_testcancel();
+    }
+  }
+
+  /* The only way we can get here is when the thread breaks out
+     of the while loop.  In this case the thread that makes it here
+     has found the number we are looking for and does not need to run
+     the thread cleanup function.  This is why the pthread_cleanup_pop
+     function is called with a 0 argument; this will pop the cleanup
+     function off the stack without executing it */
+
+  pthread_cleanup_pop(0);
+  return((void *)0);
+}
+
diff --git a/linuxthreads/Examples/ex4.c b/linuxthreads/Examples/ex4.c
new file mode 100644 (file)
index 0000000..83bc54c
--- /dev/null
@@ -0,0 +1,107 @@
+/* Making a library function that uses static variables thread-safe.
+   Illustrates: thread-specific data, pthread_once(). */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+/* This is a typical example of a library function that uses
+   static variables to accumulate results between calls.
+   Here, it just returns the concatenation of all string arguments
+   that were given to it. */
+
+#if 0
+
+char * str_accumulate(char * s)
+{
+  static char accu[1024] = { 0 };
+  strcat(accu, s);
+  return accu;
+}
+
+#endif
+
+/* Of course, this cannot be used in a multi-threaded program
+   because all threads store "accu" at the same location.
+   So, we'll use thread-specific data to have a different "accu"
+   for each thread. */
+
+/* Key identifying the thread-specific data */
+static pthread_key_t str_key;
+/* "Once" variable ensuring that the key for str_alloc will be allocated
+   exactly once. */
+static pthread_once_t str_alloc_key_once = PTHREAD_ONCE_INIT;
+
+/* Forward functions */
+static void str_alloc_key(void);
+static void str_alloc_destroy_accu(void * accu);
+
+/* Thread-safe version of str_accumulate */
+
+char * str_accumulate(char * s)
+{
+  char * accu;
+
+  /* Make sure the key is allocated */
+  pthread_once(&str_alloc_key_once, str_alloc_key);
+  /* Get the thread-specific data associated with the key */
+  accu = (char *) pthread_getspecific(str_key);
+  /* It's initially NULL, meaning that we must allocate the buffer first. */
+  if (accu == NULL) {
+    accu = malloc(1024);
+    if (accu == NULL) return NULL;
+    accu[0] = 0;
+    /* Store the buffer pointer in the thread-specific data. */
+    pthread_setspecific(str_key, (void *) accu);
+    printf("Thread %lx: allocating buffer at %p\n", pthread_self(), accu);
+  }
+  /* Now we can use accu just as in the non thread-safe code. */
+  strcat(accu, s);
+  return accu;
+}
+
+/* Function to allocate the key for str_alloc thread-specific data. */
+
+static void str_alloc_key(void)
+{
+  pthread_key_create(&str_key, str_alloc_destroy_accu);
+  printf("Thread %lx: allocated key %d\n", pthread_self(), str_key);
+}
+
+/* Function to free the buffer when the thread exits. */
+/* Called only when the thread-specific data is not NULL. */
+
+static void str_alloc_destroy_accu(void * accu)
+{
+  printf("Thread %lx: freeing buffer at %p\n", pthread_self(), accu);
+  free(accu);
+}
+
+/* Test program */
+
+void * process(void * arg)
+{
+  char * res;
+  res = str_accumulate("Result of ");
+  res = str_accumulate((char *) arg);
+  res = str_accumulate(" thread");
+  printf("Thread %lx: \"%s\"\n", pthread_self(), res);
+  return NULL;
+}
+
+int main(int argc, char ** argv)
+{
+  char * res;
+  pthread_t th1, th2;
+
+  res = str_accumulate("Result of ");
+  pthread_create(&th1, NULL, process, "first");
+  pthread_create(&th2, NULL, process, "second");
+  res = str_accumulate("initial thread");
+  printf("Thread %lx: \"%s\"\n", pthread_self(), res);
+  pthread_join(th1, NULL);
+  pthread_join(th2, NULL);
+  pthread_exit(NULL);
+}
diff --git a/linuxthreads/Examples/ex5.c b/linuxthreads/Examples/ex5.c
new file mode 100644 (file)
index 0000000..366668e
--- /dev/null
@@ -0,0 +1,102 @@
+/* The classic producer-consumer example, implemented with semaphores.
+   All integers between 0 and 9999 should be printed exactly twice,
+   once to the right of the arrow and once to the left. */
+
+#include <stdio.h>
+#include "pthread.h"
+#include "semaphore.h"
+
+#define BUFFER_SIZE 16
+
+/* Circular buffer of integers. */
+
+struct prodcons {
+  int buffer[BUFFER_SIZE];      /* the actual data */
+  int readpos, writepos;        /* positions for reading and writing */
+  sem_t sem_read;               /* number of elements available for reading */
+  sem_t sem_write;              /* number of locations available for writing */
+};
+
+/* Initialize a buffer */
+
+void init(struct prodcons * b)
+{
+  sem_init(&b->sem_write, 0, BUFFER_SIZE - 1);
+  sem_init(&b->sem_read, 0, 0);
+  b->readpos = 0;
+  b->writepos = 0;
+}
+
+/* Store an integer in the buffer */
+
+void put(struct prodcons * b, int data)
+{
+  /* Wait until buffer is not full */
+  sem_wait(&b->sem_write);
+  /* Write the data and advance write pointer */
+  b->buffer[b->writepos] = data;
+  b->writepos++;
+  if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
+  /* Signal that the buffer contains one more element for reading */
+  sem_post(&b->sem_read);
+}
+
+/* Read and remove an integer from the buffer */
+
+int get(struct prodcons * b)
+{
+  int data;
+  /* Wait until buffer is not empty */
+  sem_wait(&b->sem_read);
+  /* Read the data and advance read pointer */
+  data = b->buffer[b->readpos];
+  b->readpos++;
+  if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
+  /* Signal that the buffer has now one more location for writing */
+  sem_post(&b->sem_write);
+  return data;
+}
+
+/* A test program: one thread inserts integers from 1 to 10000,
+   the other reads them and prints them. */
+
+#define OVER (-1)
+
+struct prodcons buffer;
+
+void * producer(void * data)
+{
+  int n;
+  for (n = 0; n < 10000; n++) {
+    printf("%d --->\n", n);
+    put(&buffer, n);
+  }
+  put(&buffer, OVER);
+  return NULL;
+}
+
+void * consumer(void * data)
+{
+  int d;
+  while (1) {
+    d = get(&buffer);
+    if (d == OVER) break;
+    printf("---> %d\n", d);
+  }
+  return NULL;
+}
+
+int main()
+{
+  pthread_t th_a, th_b;
+  void * retval;
+
+  init(&buffer);
+  /* Create the threads */
+  pthread_create(&th_a, NULL, producer, 0);
+  pthread_create(&th_b, NULL, consumer, 0);
+  /* Wait until producer and consumer finish. */
+  pthread_join(th_a, &retval);
+  pthread_join(th_b, &retval);
+  return 0;
+}
diff --git a/linuxthreads/FAQ.html b/linuxthreads/FAQ.html
new file mode 100644 (file)
index 0000000..45d2387
--- /dev/null
@@ -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>
diff --git a/linuxthreads/LICENSE b/linuxthreads/LICENSE
new file mode 100644 (file)
index 0000000..7bcca60
--- /dev/null
@@ -0,0 +1,501 @@
+GNU LIBRARY GENERAL PUBLIC LICENSE
+**********************************
+
+                         Version 2, June 1991
+
+     Copyright (C) 1991 Free Software Foundation, Inc.
+     59 Temple Place -- Suite 330, Boston, MA 02111-1307, USA
+     
+     Everyone is permitted to copy and distribute verbatim copies
+     of this license document, but changing it is not allowed.
+     
+     [This is the first released version of the library GPL.  It is
+      numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+Preamble
+========
+
+   The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+   This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+   When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it in
+new free programs; and that you know you can do these things.
+
+   To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the library, or if you modify it.
+
+   For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+   Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+   Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+   Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+   Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License, which was designed for utility
+programs.  This license, the GNU Library General Public License,
+applies to certain designated libraries.  This license is quite
+different from the ordinary one; be sure to read it in full, and don't
+assume that anything in it is the same as in the ordinary license.
+
+   The reason we have a separate public license for some libraries is
+that they blur the distinction we usually make between modifying or
+adding to a program and simply using it.  Linking a program with a
+library, without changing the library, is in some sense simply using
+the library, and is analogous to running a utility program or
+application program.  However, in a textual and legal sense, the linked
+executable is a combined work, a derivative of the original library,
+and the ordinary General Public License treats it as such.
+
+   Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+   However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended
+to permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to
+achieve this as regards changes in header files, but we have achieved
+it as regards changes in the actual functions of the Library.)  The
+hope is that this will lead to faster development of free libraries.
+
+   The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+   Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+     contains a notice placed by the copyright holder or other
+     authorized party saying it may be distributed under the terms of
+     this Library General Public License (also called "this License").
+     Each licensee is addressed as "you".
+
+     A "library" means a collection of software functions and/or data
+     prepared so as to be conveniently linked with application programs
+     (which use some of those functions and data) to form executables.
+
+     The "Library", below, refers to any such software library or work
+     which has been distributed under these terms.  A "work based on the
+     Library" means either the Library or any derivative work under
+     copyright law: that is to say, a work containing the Library or a
+     portion of it, either verbatim or with modifications and/or
+     translated straightforwardly into another language.  (Hereinafter,
+     translation is included without limitation in the term
+     "modification".)
+
+     "Source code" for a work means the preferred form of the work for
+     making modifications to it.  For a library, complete source code
+     means all the source code for all modules it contains, plus any
+     associated interface definition files, plus the scripts used to
+     control compilation and installation of the library.
+
+     Activities other than copying, distribution and modification are
+     not covered by this License; they are outside its scope.  The act
+     of running a program using the Library is not restricted, and
+     output from such a program is covered only if its contents
+     constitute a work based on the Library (independent of the use of
+     the Library in a tool for writing it).  Whether that is true
+     depends on what the Library does and what the program that uses
+     the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+     complete source code as you receive it, in any medium, provided
+     that you conspicuously and appropriately publish on each copy an
+     appropriate copyright notice and disclaimer of warranty; keep
+     intact all the notices that refer to this License and to the
+     absence of any warranty; and distribute a copy of this License
+     along with the Library.
+
+     You may charge a fee for the physical act of transferring a copy,
+     and you may at your option offer warranty protection in exchange
+     for a fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+     of it, thus forming a work based on the Library, and copy and
+     distribute such modifications or work under the terms of Section 1
+     above, provided that you also meet all of these conditions:
+
+       a. The modified work must itself be a software library.
+
+       b. You must cause the files modified to carry prominent notices
+          stating that you changed the files and the date of any change.
+
+       c. You must cause the whole of the work to be licensed at no
+          charge to all third parties under the terms of this License.
+
+       d. If a facility in the modified Library refers to a function or
+          a table of data to be supplied by an application program that
+          uses the facility, other than as an argument passed when the
+          facility is invoked, then you must make a good faith effort
+          to ensure that, in the event an application does not supply
+          such function or table, the facility still operates, and
+          performs whatever part of its purpose remains meaningful.
+
+          (For example, a function in a library to compute square roots
+          has a purpose that is entirely well-defined independent of the
+          application.  Therefore, Subsection 2d requires that any
+          application-supplied function or table used by this function
+          must be optional: if the application does not supply it, the
+          square root function must still compute square roots.)
+
+     These requirements apply to the modified work as a whole.  If
+     identifiable sections of that work are not derived from the
+     Library, and can be reasonably considered independent and separate
+     works in themselves, then this License, and its terms, do not
+     apply to those sections when you distribute them as separate
+     works.  But when you distribute the same sections as part of a
+     whole which is a work based on the Library, the distribution of
+     the whole must be on the terms of this License, whose permissions
+     for other licensees extend to the entire whole, and thus to each
+     and every part regardless of who wrote it.
+
+     Thus, it is not the intent of this section to claim rights or
+     contest your rights to work written entirely by you; rather, the
+     intent is to exercise the right to control the distribution of
+     derivative or collective works based on the Library.
+
+     In addition, mere aggregation of another work not based on the
+     Library with the Library (or with a work based on the Library) on
+     a volume of a storage or distribution medium does not bring the
+     other work under the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+     License instead of this License to a given copy of the Library.
+     To do this, you must alter all the notices that refer to this
+     License, so that they refer to the ordinary GNU General Public
+     License, version 2, instead of to this License.  (If a newer
+     version than version 2 of the ordinary GNU General Public License
+     has appeared, then you can specify that version instead if you
+     wish.)  Do not make any other change in these notices.
+
+     Once this change is made in a given copy, it is irreversible for
+     that copy, so the ordinary GNU General Public License applies to
+     all subsequent copies and derivative works made from that copy.
+
+     This option is useful when you wish to copy part of the code of
+     the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+     derivative of it, under Section 2) in object code or executable
+     form under the terms of Sections 1 and 2 above provided that you
+     accompany it with the complete corresponding machine-readable
+     source code, which must be distributed under the terms of Sections
+     1 and 2 above on a medium customarily used for software
+     interchange.
+
+     If distribution of object code is made by offering access to copy
+     from a designated place, then offering equivalent access to copy
+     the source code from the same place satisfies the requirement to
+     distribute the source code, even though third parties are not
+     compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+     Library, but is designed to work with the Library by being
+     compiled or linked with it, is called a "work that uses the
+     Library".  Such a work, in isolation, is not a derivative work of
+     the Library, and therefore falls outside the scope of this License.
+
+     However, linking a "work that uses the Library" with the Library
+     creates an executable that is a derivative of the Library (because
+     it contains portions of the Library), rather than a "work that
+     uses the library".  The executable is therefore covered by this
+     License.  Section 6 states terms for distribution of such
+     executables.
+
+     When a "work that uses the Library" uses material from a header
+     file that is part of the Library, the object code for the work may
+     be a derivative work of the Library even though the source code is
+     not.  Whether this is true is especially significant if the work
+     can be linked without the Library, or if the work is itself a
+     library.  The threshold for this to be true is not precisely
+     defined by law.
+
+     If such an object file uses only numerical parameters, data
+     structure layouts and accessors, and small macros and small inline
+     functions (ten lines or less in length), then the use of the object
+     file is unrestricted, regardless of whether it is legally a
+     derivative work.  (Executables containing this object code plus
+     portions of the Library will still fall under Section 6.)
+
+     Otherwise, if the work is a derivative of the Library, you may
+     distribute the object code for the work under the terms of Section
+     6.  Any executables containing that work also fall under Section 6,
+     whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+     link a "work that uses the Library" with the Library to produce a
+     work containing portions of the Library, and distribute that work
+     under terms of your choice, provided that the terms permit
+     modification of the work for the customer's own use and reverse
+     engineering for debugging such modifications.
+
+     You must give prominent notice with each copy of the work that the
+     Library is used in it and that the Library and its use are covered
+     by this License.  You must supply a copy of this License.  If the
+     work during execution displays copyright notices, you must include
+     the copyright notice for the Library among them, as well as a
+     reference directing the user to the copy of this License.  Also,
+     you must do one of these things:
+
+       a. Accompany the work with the complete corresponding
+          machine-readable source code for the Library including
+          whatever changes were used in the work (which must be
+          distributed under Sections 1 and 2 above); and, if the work
+          is an executable linked with the Library, with the complete
+          machine-readable "work that uses the Library", as object code
+          and/or source code, so that the user can modify the Library
+          and then relink to produce a modified executable containing
+          the modified Library.  (It is understood that the user who
+          changes the contents of definitions files in the Library will
+          not necessarily be able to recompile the application to use
+          the modified definitions.)
+
+       b. Accompany the work with a written offer, valid for at least
+          three years, to give the same user the materials specified in
+          Subsection 6a, above, for a charge no more than the cost of
+          performing this distribution.
+
+       c. If distribution of the work is made by offering access to copy
+          from a designated place, offer equivalent access to copy the
+          above specified materials from the same place.
+
+       d. Verify that the user has already received a copy of these
+          materials or that you have already sent this user a copy.
+
+     For an executable, the required form of the "work that uses the
+     Library" must include any data and utility programs needed for
+     reproducing the executable from it.  However, as a special
+     exception, the source code distributed need not include anything
+     that is normally distributed (in either source or binary form)
+     with the major components (compiler, kernel, and so on) of the
+     operating system on which the executable runs, unless that
+     component itself accompanies the executable.
+
+     It may happen that this requirement contradicts the license
+     restrictions of other proprietary libraries that do not normally
+     accompany the operating system.  Such a contradiction means you
+     cannot use both them and the Library together in an executable
+     that you distribute.
+
+  7. You may place library facilities that are a work based on the
+     Library side-by-side in a single library together with other
+     library facilities not covered by this License, and distribute
+     such a combined library, provided that the separate distribution
+     of the work based on the Library and of the other library
+     facilities is otherwise permitted, and provided that you do these
+     two things:
+
+       a. Accompany the combined library with a copy of the same work
+          based on the Library, uncombined with any other library
+          facilities.  This must be distributed under the terms of the
+          Sections above.
+
+       b. Give prominent notice with the combined library of the fact
+          that part of it is a work based on the Library, and explaining
+          where to find the accompanying uncombined form of the same
+          work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute the
+     Library except as expressly provided under this License.  Any
+     attempt otherwise to copy, modify, sublicense, link with, or
+     distribute the Library is void, and will automatically terminate
+     your rights under this License.  However, parties who have
+     received copies, or rights, from you under this License will not
+     have their licenses terminated so long as such parties remain in
+     full compliance.
+
+  9. You are not required to accept this License, since you have not
+     signed it.  However, nothing else grants you permission to modify
+     or distribute the Library or its derivative works.  These actions
+     are prohibited by law if you do not accept this License.
+     Therefore, by modifying or distributing the Library (or any work
+     based on the Library), you indicate your acceptance of this
+     License to do so, and all its terms and conditions for copying,
+     distributing or modifying the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+     Library), the recipient automatically receives a license from the
+     original licensor to copy, distribute, link with or modify the
+     Library subject to these terms and conditions.  You may not impose
+     any further restrictions on the recipients' exercise of the rights
+     granted herein.  You are not responsible for enforcing compliance
+     by third parties to this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+     infringement or for any other reason (not limited to patent
+     issues), conditions are imposed on you (whether by court order,
+     agreement or otherwise) that contradict the conditions of this
+     License, they do not excuse you from the conditions of this
+     License.  If you cannot distribute so as to satisfy simultaneously
+     your obligations under this License and any other pertinent
+     obligations, then as a consequence you may not distribute the
+     Library at all.  For example, if a patent license would not permit
+     royalty-free redistribution of the Library by all those who
+     receive copies directly or indirectly through you, then the only
+     way you could satisfy both it and this License would be to refrain
+     entirely from distribution of the Library.
+
+     If any portion of this section is held invalid or unenforceable
+     under any particular circumstance, the balance of the section is
+     intended to apply, and the section as a whole is intended to apply
+     in other circumstances.
+
+     It is not the purpose of this section to induce you to infringe any
+     patents or other property right claims or to contest validity of
+     any such claims; this section has the sole purpose of protecting
+     the integrity of the free software distribution system which is
+     implemented by public license practices.  Many people have made
+     generous contributions to the wide range of software distributed
+     through that system in reliance on consistent application of that
+     system; it is up to the author/donor to decide if he or she is
+     willing to distribute software through any other system and a
+     licensee cannot impose that choice.
+
+     This section is intended to make thoroughly clear what is believed
+     to be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+     certain countries either by patents or by copyrighted interfaces,
+     the original copyright holder who places the Library under this
+     License may add an explicit geographical distribution limitation
+     excluding those countries, so that distribution is permitted only
+     in or among countries not thus excluded.  In such case, this
+     License incorporates the limitation as if written in the body of
+     this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+     versions of the Library General Public License from time to time.
+     Such new versions will be similar in spirit to the present version,
+     but may differ in detail to address new problems or concerns.
+
+     Each version is given a distinguishing version number.  If the
+     Library specifies a version number of this License which applies
+     to it and "any later version", you have the option of following
+     the terms and conditions either of that version or of any later
+     version published by the Free Software Foundation.  If the Library
+     does not specify a license version number, you may choose any
+     version ever published by the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+     programs whose distribution conditions are incompatible with these,
+     write to the author to ask for permission.  For software which is
+     copyrighted by the Free Software Foundation, write to the Free
+     Software Foundation; we sometimes make exceptions for this.  Our
+     decision will be guided by the two goals of preserving the free
+     status of all derivatives of our free software and of promoting
+     the sharing and reuse of software generally.
+
+                                NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+     WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE
+     LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+     HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT
+     WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT
+     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+     FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE
+     QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU.  SHOULD THE
+     LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
+     SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+     WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
+     MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE
+     LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+     INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+     INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF
+     DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU
+     OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY
+     OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+                      END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+==============================================
+
+   If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of
+the ordinary General Public License).
+
+   To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should have
+at least the "copyright" line and a pointer to where the full notice is
+found.
+
+     ONE LINE TO GIVE THE LIBRARY'S NAME AND AN IDEA OF WHAT IT DOES.
+     Copyright (C) YEAR  NAME OF AUTHOR
+     
+     This library is free software; you can redistribute it and/or modify it
+     under the terms of the GNU Library General Public License as published
+     by the Free Software Foundation; either version 2 of the License, or (at
+     your option) any later version.
+     
+     This library is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     Library General Public License for more details.
+     
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+
+   Also add information on how to contact you by electronic and paper
+mail.
+
+   You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+     Yoyodyne, Inc., hereby disclaims all copyright interest in the library
+     `Frob' (a library for tweaking knobs) written by James Random Hacker.
+     
+     SIGNATURE OF TY COON, 1 April 1990
+     Ty Coon, President of Vice
+
+   That's all there is to it!
+
diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile
new file mode 100644 (file)
index 0000000..c4eddef
--- /dev/null
@@ -0,0 +1,44 @@
+# Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU Library General Public
+# License along with the GNU C Library; see the file COPYING.LIB.  If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+#
+#      Sub-makefile for linuxthreads portion of the library.
+#
+subdir := linuxthreads
+
+linuxthreads-version=0.7
+
+headers := pthread.h semaphore.h bits/semaphore.h
+distribute := internals.h queue.h restart.h spinlock.h
+
+routines := weaks
+
+extra-libs := libpthread
+extra-libs-others := $(extra-libs)
+
+libpthread-routines := attr cancel condvar join manager mutex ptfork \
+                      pthread signals specific errno lockfile \
+                      semaphore wrapsyscall rwlock
+libpthread-map := libpthread.map
+
+include ../Rules
+
+# Depend on libc.so so a DT_NEEDED is generated in the shared objects.
+# This ensures they will load libc.so for needed symbols if loaded by
+# a statically-linked program that hasn't already loaded it.
+$(objpfx)libpthread.so: $(common-objpfx)libc.so
diff --git a/linuxthreads/README b/linuxthreads/README
new file mode 100644 (file)
index 0000000..e824dd5
--- /dev/null
@@ -0,0 +1,163 @@
+        Linuxthreads - POSIX 1003.1c kernel threads for Linux
+
+      Copyright 1996, 1997 Xavier Leroy (Xavier.Leroy@inria.fr)
+
+
+DESCRIPTION:
+
+This is release 0.7 (late beta) of LinuxThreads, a BiCapitalized
+implementation of the Posix 1003.1c "pthread" interface for Linux.
+
+LinuxThreads provides kernel-level threads: each thread is a separate
+Unix process, sharing its address space with the other threads through
+the new system call clone(). Scheduling between threads is handled by
+the kernel scheduler, just like scheduling between Unix processes.
+
+
+REQUIREMENTS:
+
+- Linux version 2.0 and up (requires the new clone() system call
+  and the new realtime scheduler).
+
+- For Intel platforms: libc 5.2.18 or later is required.
+  5.2.18 or 5.4.12 or later are recommended;
+  5.3.12 and 5.4.7 have problems (see the FAQ.html file for more info).
+
+- Also supports glibc 2 (a.k.a. libc 6), which actually comes with
+  a specially-adapted version of this library.
+
+- Currently supports Intel, Alpha, Sparc, Motorola 68k, ARM and MIPS
+  platforms.
+
+- Multiprocessors are supported.
+
+
+INSTALLATION:
+
+- Edit the Makefile, set the variables in the "Configuration" section.
+
+- Do "make".
+
+- Do "make install".
+
+
+USING LINUXTHREADS:
+
+        gcc -D_REENTRANT ... -lpthread
+
+A complete set of manual pages is included. Also see the subdirectory
+Examples/ for some sample programs.
+
+
+STATUS:
+
+- All functions in the Posix 1003.1c base interface implemented.
+  Also supports priority scheduling.
+
+- For users of libc 5 (H.J.Lu's libc), a number of C library functions
+  are reimplemented or wrapped to make them thread-safe, including:
+  * malloc functions
+  * stdio functions (define _REENTRANT before including <stdio.h>)
+  * per-thread errno variable (define _REENTRANT before including <errno.h>)
+  * directory reading functions (opendir(), etc)
+  * sleep()
+  * gmtime(), localtime()
+
+  New library functions provided:
+  * flockfile(), funlockfile(), ftrylockfile()
+  * reentrant versions of network database functions (gethostbyname_r(), etc)
+    and password functions (getpwnam_r(), etc).
+
+- libc 6 (glibc 2) provides much better thread support than libc 5,
+  and comes with a specially-adapted version of LinuxThreads.
+  For serious multithreaded programming, you should consider switching
+  to glibc 2. It is available from prep.ai.mit.edu:/pub/gnu and its mirrors.
+
+
+WARNING:
+
+Many existing libraries are not compatible with LinuxThreads,
+either because they are not inherently thread-safe, or because they
+have not been compiled with the -D_REENTRANT.  For more info, see the
+FAQ.html file in this directory.
+
+A prime example of the latter is Xlib. If you link it with
+LinuxThreads, you'll probably get an "unknown 0 error" very
+early. This is just a consequence of the Xlib binaries using the
+global variable "errno" to fetch error codes, while LinuxThreads and
+the C library use the per-thread "errno" location.
+
+See the file README.Xfree3.3 for info on how to compile the Xfree 3.3
+libraries to make them compatible with LinuxThreads.
+
+
+KNOWN BUGS AND LIMITATIONS:
+
+- Threads share pretty much everything they should share according
+  to the standard: memory space, file descriptors, signal handlers,
+  current working directory, etc. One thing that they do not share
+  is their pid's and parent pid's. According to the standard, they
+  should have the same, but that's one thing we cannot achieve
+  in this implementation (until the CLONE_PID flag to clone() becomes
+  usable).
+
+- The current implementation uses the two signals SIGUSR1 and SIGUSR2,
+  so user-level code cannot employ them. Ideally, there should be two
+  signals reserved for this library. One signal is used for restarting
+  threads blocked on mutexes or conditions; the other is for thread
+  cancellation.
+
+- The stacks for the threads are allocated high in the memory space,
+  below the stack of the initial process, and spaced 2M apart.
+  Stacks are allocated with the "grow on demand" flag, so they don't
+  use much virtual space initially (4k, currently), but can grow
+  up to 2M if needed.
+
+  Reserving such a large address space for each thread means that,
+  on a 32-bit architecture, no more than about 1000 threads can
+  coexist (assuming a 2Gb address space for user processes),
+  but this is reasonable, since each thread uses up one entry in the
+  kernel's process table, which is usually limited to 512 processes.
+
+  Another potential problem of the "grow on demand" scheme is that
+  nothing prevents the user from mmap'ing something in the 2M address
+  window reserved for a thread stack, possibly causing later extensions of
+  that stack to fail. Mapping at fixed addresses should be avoided
+  when using this library.
+
+- Signal handling does not fully conform to the Posix standard,
+  due to the fact that threads are here distinct processes that can be
+  sent signals individually, so there's no notion of sending a signal
+  to "the" process (the collection of all threads).
+  More precisely, here is a summary of the standard requirements
+  and how they are met by the implementation:
+
+  1- Synchronous signals (generated by the thread execution, e.g. SIGFPE)
+     are delivered to the thread that raised them.
+     (OK.)
+
+  2- A fatal asynchronous signal terminates all threads in the process.
+     (OK. The thread manager notices when a thread dies on a signal
+      and kills all other threads with the same signal.)
+
+  3- An asynchronous signal will be delivered to one of the threads
+     of the program which does not block the signal (it is unspecified
+     which).
+     (No, the signal is delivered to the thread it's been sent to,
+      based on the pid of the thread. If that thread is currently
+      blocking the signal, the signal remains pending.)
+
+  4- The signal will be delivered to at most one thread.
+     (OK, except for signals generated from the terminal or sent to
+      the process group, which will be delivered to all threads.)
+
+- The current implementation of the MIPS support assumes a MIPS ISA II
+  processor or better.  These processors support atomic operations by
+  ll/sc instructions.  Older R2000/R3000 series processors are not
+  supported yet; support for these will have higher overhead.
+
+- The current implementation of the ARM support assumes that the SWP
+  (atomic swap register with memory) instruction is available.  This is
+  the case for all processors except for the ARM1 and ARM2.  On StrongARM,
+  the SWP instruction does not bypass the cache, so multi-processor support
+  will be more troublesome.
diff --git a/linuxthreads/README.Xfree3.2 b/linuxthreads/README.Xfree3.2
new file mode 100644 (file)
index 0000000..ac08e15
--- /dev/null
@@ -0,0 +1,352 @@
+This file describes how to make a threaded X11R6.
+
+You need the source-code of XFree-3.2. I used the sources of X11R6.1
+(files: xc-1.tar.gz xc-2.tar.gz xc-3.tar.gz) and the patches to
+XFree-3.2 (files: README.X11.patch R6.1pl1-3.2.diff.gz cfont32.tgz).
+
+Untar the xc-?.tar.gz files in a directory called XF3.2 and apply
+the XFree-3.2 patches as described in README.X11.patch or use the
+whole XFree86 source.
+
+Now apply the thread patch with
+
+patch -p0 < XF3.2.xc.diff
+
+Go to the XF3.2/xc directory and make the whole thing:
+nice make World >& world.log &
+tail -f world.log
+
+Wait a few hours or interrupt the process after the shared libs
+are made. The shared libs are:
+
+XF3.2/xc/lib/ICE/libICE.so.6.0*
+XF3.2/xc/lib/PEX5/libPEX5.so.6.0*
+XF3.2/xc/lib/SM/libSM.so.6.0*
+XF3.2/xc/lib/X11/libX11.so.6.1*
+XF3.2/xc/lib/XIE/libXIE.so.6.0*
+XF3.2/xc/lib/XThrStub/libXThrStub.so.6.0*
+XF3.2/xc/lib/Xaw/libXaw.so.6.1*
+XF3.2/xc/lib/Xext/libXext.so.6.1*
+XF3.2/xc/lib/Xi/libXi.so.6.0*
+XF3.2/xc/lib/Xmu/libXmu.so.6.0*
+XF3.2/xc/lib/Xt/libXt.so.6.0*
+XF3.2/xc/lib/Xtst/libXtst.so.6.1*
+
+(The Program dga didn't compile, but I have not check out why.)
+
+Now you can copy the resulting libs   
+
+cp XF3.2/xc/lib/*/*.so.?.? /usr/X11R6/lib/
+
+and create some links
+
+cd /usr/X11R6/lib/
+ln -s libXThrStub.so.6.0 libXThrStub.so.6
+ln -s libXThrStub.so.6 libXThrStub.so
+
+or use make install (not tested, and needs new configuration).
+
+It is possible with the libXThrSub to compile X11 programs without linking
+libpthread to them and not necessary to recompile already installed
+unthreaded X11 programs, because libXThrSub keeps the dynamic linker quit.
+On the other hand you can link libpthread to a X11 program to use threads.
+
+I used linux 2.0.23 and libc 5.4.7 .
+
+Hans-Helmut B├╝hmann     hans@expmech.ing.tu-bs.de
+
+----------------------------------------------------------------------------
+
+XF3.2.xc.diff:
+-----------------------------------------------------------------------------
+diff -u --recursive XF3.2.orig/xc/config/cf/linux.cf XF3.2/xc/config/cf/linux.cf
+--- XF3.2.orig/xc/config/cf/linux.cf   Sun Nov 10 17:05:30 1996
++++ XF3.2/xc/config/cf/linux.cf        Sun Nov 10 16:30:55 1996
+@@ -61,6 +61,14 @@
+ #define HasSnprintf           YES
+ #endif
++#define HasPosixThreads         YES
++#define ThreadedX               YES
++#define BuildThreadStubLibrary        YES
++#define NeedUIThrStubs                YES
++#define HasThreadSafeAPI        NO
++#define SystemMTDefines         -D_REENTRANT
++#define ThreadsLibraries        -lpthread
++
+ #define AvoidNullMakeCommand  YES
+ #define StripInstalledPrograms        YES
+ #define CompressAllFonts      YES
+@@ -158,7 +166,7 @@
+ #define LdPostLib             /* Never needed */
+ #ifdef i386Architecture
+-#define OptimizedCDebugFlags  DefaultGcc2i386Opt -m486
++#define OptimizedCDebugFlags  DefaultGcc2i386Opt -m486 -pipe
+ #define StandardDefines               -Dlinux -D__i386__ -D_POSIX_SOURCE \
+                               -D_BSD_SOURCE -D_SVID_SOURCE -DX_LOCALE
+ #define XawI18nDefines                -DUSE_XWCHAR_STRING -DUSE_XMBTOWC
+diff -u --recursive XF3.2.orig/xc/config/cf/lnxLib.tmpl XF3.2/xc/config/cf/lnxLib.tmpl
+--- XF3.2.orig/xc/config/cf/lnxLib.tmpl        Sun Nov 10 17:05:30 1996
++++ XF3.2/xc/config/cf/lnxLib.tmpl     Sat Nov  9 14:52:39 1996
+@@ -19,7 +19,7 @@
+ #define CplusplusLibC
+-#define SharedX11Reqs
++#define SharedX11Reqs         -L$(BUILDLIBDIR) -lXThrStub 
+ #define SharedOldXReqs        $(LDPRELIB) $(XLIBONLY)
+ #define SharedXtReqs  $(LDPRELIB) $(XLIBONLY) $(SMLIB) $(ICELIB)
+ #define SharedXawReqs $(LDPRELIB) $(XMULIB) $(XTOOLLIB) $(XLIB)
+diff -u --recursive XF3.2.orig/xc/include/Xthreads.h XF3.2/xc/include/Xthreads.h
+--- XF3.2.orig/xc/include/Xthreads.h   Thu Dec  7 02:19:09 1995
++++ XF3.2/xc/include/Xthreads.h        Sat Nov  9 01:04:55 1996
+@@ -229,12 +229,12 @@
+ #define xcondition_wait(c,m) pthread_cond_wait(c,m)
+ #define xcondition_signal(c) pthread_cond_signal(c)
+ #define xcondition_broadcast(c) pthread_cond_broadcast(c)
+-#ifdef _DECTHREADS_
++#if defined(_DECTHREADS_) || defined(linux)
+ static xthread_t _X_no_thread_id;
+ #define xthread_have_id(id) !pthread_equal(id, _X_no_thread_id)
+ #define xthread_clear_id(id) id = _X_no_thread_id
+ #define xthread_equal(id1,id2) pthread_equal(id1, id2)
+-#endif /* _DECTHREADS_ */
++#endif /* _DECTHREADS_ || linux */
+ #if _CMA_VENDOR_ == _CMA__IBM
+ #ifdef DEBUG                  /* too much of a hack to enable normally */
+ /* see also cma__obj_set_name() */
+diff -u --recursive XF3.2.orig/xc/lib/X11/util/makekeys.c XF3.2/xc/lib/X11/util/makekeys.c
+--- XF3.2.orig/xc/lib/X11/util/makekeys.c      Mon Apr 18 02:22:22 1994
++++ XF3.2/xc/lib/X11/util/makekeys.c   Sat Nov  9 00:44:14 1996
+@@ -73,7 +73,7 @@
+     register char c;
+     int first;
+     int best_max_rehash;
+-    int best_z;
++    int best_z = 0;
+     int num_found;
+     KeySym val;
+diff -u --recursive XF3.2.orig/xc/lib/XThrStub/Imakefile XF3.2/xc/lib/XThrStub/Imakefile
+--- XF3.2.orig/xc/lib/XThrStub/Imakefile       Sun Nov 10 17:08:12 1996
++++ XF3.2/xc/lib/XThrStub/Imakefile    Sat Nov  9 19:04:51 1996
+@@ -25,7 +25,7 @@
+       DEFINES = $(ALLOC_DEFINES)
+      INCLUDES = 
+          SRCS = $(STUBSRCS)
+-         OBJS = $(STUBOBJS
++         OBJS = $(STUBOBJS)
+      LINTLIBS = $(LINTXLIB)
+ #include <Library.tmpl>
+diff -u --recursive XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c XF3.2/xc/lib/XThrStub/UIThrStubs.c
+--- XF3.2.orig/xc/lib/XThrStub/UIThrStubs.c    Sun Nov 10 17:08:12 1996
++++ XF3.2/xc/lib/XThrStub/UIThrStubs.c Sun Nov 10 15:14:55 1996
+@@ -37,16 +37,43 @@
+  * specificies the thread library on the link line.
+  */
++#if defined(linux)
++#include <pthread.h>
++#else
+ #include <thread.h>
+ #include <synch.h>
++#endif
++#if defined(linux)
++static pthread_t no_thread_id;
++#endif /* defined(linux) */
++
++#if defined(linux)
++#pragma weak pthread_self = _Xthr_self_stub_
++pthread_t 
++_Xthr_self_stub_()
++{
++    return(no_thread_id);
++}
++#else /* defined(linux) */
+ #pragma weak thr_self = _Xthr_self_stub_
+ thread_t 
+ _Xthr_self_stub_()
+ {
+     return((thread_t)0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_mutex_init = _Xmutex_init_stub_
++int 
++_Xmutex_init_stub_(m, a)
++    pthread_mutex_t *m;
++    __const pthread_mutexattr_t *a;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak mutex_init = _Xmutex_init_stub_
+ int 
+ _Xmutex_init_stub_(m, t, a)
+@@ -56,7 +83,17 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_mutex_destroy = _Xmutex_destroy_stub_
++int
++_Xmutex_destroy_stub_(m)
++    pthread_mutex_t *m;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak mutex_destroy = _Xmutex_destroy_stub_
+ int
+ _Xmutex_destroy_stub_(m)
+@@ -64,7 +101,17 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_mutex_lock = _Xmutex_lock_stub_
++int
++_Xmutex_lock_stub_(m)
++    pthread_mutex_t *m;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak mutex_lock = _Xmutex_lock_stub_
+ int
+ _Xmutex_lock_stub_(m)
+@@ -72,7 +119,17 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_mutex_unlock = _Xmutex_unlock_stub_
++int
++_Xmutex_unlock_stub_(m)
++    pthread_mutex_t *m;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak mutex_unlock = _Xmutex_unlock_stub_
+ int
+ _Xmutex_unlock_stub_(m)
+@@ -80,7 +137,18 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak  pthread_cond_init = _Xcond_init_stub_
++int 
++_Xcond_init_stub_(c, a)
++    pthread_cond_t *c;
++    __const pthread_condattr_t *a;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak cond_init = _Xcond_init_stub_
+ int 
+ _Xcond_init_stub_(c, t, a)
+@@ -90,7 +158,17 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_cond_destroy = _Xcond_destroy_stub_
++int
++_Xcond_destroy_stub_(c)
++    pthread_cond_t *c;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak cond_destroy = _Xcond_destroy_stub_
+ int
+ _Xcond_destroy_stub_(c)
+@@ -98,7 +176,18 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_cond_wait = _Xcond_wait_stub_
++int
++_Xcond_wait_stub_(c,m)
++    pthread_cond_t *c;
++    pthread_mutex_t *m;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak cond_wait = _Xcond_wait_stub_
+ int
+ _Xcond_wait_stub_(c,m)
+@@ -107,7 +196,17 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_cond_signal = _Xcond_signal_stub_
++int
++_Xcond_signal_stub_(c)
++    pthread_cond_t *c;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak cond_signal = _Xcond_signal_stub_
+ int
+ _Xcond_signal_stub_(c)
+@@ -115,7 +214,17 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++#if defined(linux)
++#pragma weak pthread_cond_broadcast = _Xcond_broadcast_stub_
++int
++_Xcond_broadcast_stub_(c)
++    pthread_cond_t *c;
++{
++    return(0);
++}
++#else /* defined(linux) */
+ #pragma weak cond_broadcast = _Xcond_broadcast_stub_
+ int
+ _Xcond_broadcast_stub_(c)
+@@ -123,3 +232,15 @@
+ {
+     return(0);
+ }
++#endif /* defined(linux) */
++
++#if defined(linux)
++#pragma weak pthread_equal = _Xthr_equal_stub_
++int
++_Xthr_equal_stub_(t1, t2)
++    pthread_t t1;
++    pthread_t t2;
++{
++    return(1);
++}
++#endif /* defined(linux) */
+-------------------------------------------------------------------------
diff --git a/linuxthreads/attr.c b/linuxthreads/attr.c
new file mode 100644 (file)
index 0000000..9622b57
--- /dev/null
@@ -0,0 +1,117 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Handling of thread attributes */
+
+#include <unistd.h>
+#include "pthread.h"
+#include "internals.h"
+
+int pthread_attr_init(pthread_attr_t *attr)
+{
+  attr->detachstate = PTHREAD_CREATE_JOINABLE;
+  attr->schedpolicy = SCHED_OTHER;
+  attr->schedparam.sched_priority = 0;
+  attr->inheritsched = PTHREAD_EXPLICIT_SCHED;
+  attr->scope = PTHREAD_SCOPE_SYSTEM;
+  return 0;
+}
+
+int pthread_attr_destroy(pthread_attr_t *attr)
+{
+  return 0;
+}
+
+int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
+{
+  if (detachstate < PTHREAD_CREATE_JOINABLE ||
+      detachstate > PTHREAD_CREATE_DETACHED)
+    return EINVAL;
+  attr->detachstate = detachstate;
+  return 0;
+}
+
+int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
+{
+  *detachstate = attr->detachstate;
+  return 0;
+}
+
+int pthread_attr_setschedparam(pthread_attr_t *attr,
+                               const struct sched_param *param)
+{
+  int max_prio = __sched_get_priority_max(attr->schedpolicy);
+  int min_prio = __sched_get_priority_min(attr->schedpolicy);
+
+  if (param->sched_priority < min_prio || param->sched_priority > max_prio)
+    return EINVAL;
+  attr->schedparam = *param;
+  return 0;
+}
+
+int pthread_attr_getschedparam(const pthread_attr_t *attr,
+                               struct sched_param *param)
+{
+  *param = attr->schedparam;
+  return 0;
+}
+
+int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
+{
+  if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR)
+    return EINVAL;
+  if (policy != SCHED_OTHER && geteuid() != 0)
+    return ENOTSUP;
+  attr->schedpolicy = policy;
+  return 0;
+}
+
+int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
+{
+  *policy = attr->schedpolicy;
+  return 0;
+}
+
+int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit)
+{
+  if (inherit != PTHREAD_INHERIT_SCHED && inherit != PTHREAD_EXPLICIT_SCHED)
+    return EINVAL;
+  attr->inheritsched = inherit;
+  return 0;
+}
+
+int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit)
+{
+  *inherit = attr->inheritsched;
+  return 0;
+}
+
+int pthread_attr_setscope(pthread_attr_t *attr, int scope)
+{
+  switch (scope) {
+  case PTHREAD_SCOPE_SYSTEM:
+    attr->scope = scope;
+    return 0;
+  case PTHREAD_SCOPE_PROCESS:
+    return ENOTSUP;
+  default:
+    return EINVAL;
+  }
+}
+
+int pthread_attr_getscope(const pthread_attr_t *attr, int *scope)
+{
+  *scope = attr->scope;
+  return 0;
+}
diff --git a/linuxthreads/cancel.c b/linuxthreads/cancel.c
new file mode 100644 (file)
index 0000000..a6a0eca
--- /dev/null
@@ -0,0 +1,131 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Thread cancellation */
+
+#include <errno.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+
+int pthread_setcancelstate(int state, int * oldstate)
+{
+  pthread_descr self = thread_self();
+  if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE)
+    return EINVAL;
+  if (oldstate != NULL) *oldstate = self->p_cancelstate;
+  self->p_cancelstate = state;
+  if (self->p_canceled &&
+      self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
+      self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+    pthread_exit(PTHREAD_CANCELED);
+  return 0;
+}
+
+int pthread_setcanceltype(int type, int * oldtype)
+{
+  pthread_descr self = thread_self();
+  if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS)
+    return EINVAL;
+  if (oldtype != NULL) *oldtype = self->p_canceltype;
+  self->p_canceltype = type;
+  if (self->p_canceled &&
+      self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
+      self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+    pthread_exit(PTHREAD_CANCELED);
+  return 0;
+}
+
+int pthread_cancel(pthread_t thread)
+{
+  pthread_handle handle = thread_handle(thread);
+  int pid;
+
+  acquire(&handle->h_spinlock);
+  if (invalid_handle(handle, thread)) {
+    release(&handle->h_spinlock);
+    return ESRCH;
+  }
+  handle->h_descr->p_canceled = 1;
+  pid = handle->h_descr->p_pid;
+  release(&handle->h_spinlock);
+  kill(pid, PTHREAD_SIG_CANCEL);
+  return 0;
+}
+
+void pthread_testcancel(void)
+{
+  pthread_descr self = thread_self();
+  if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)
+    pthread_exit(PTHREAD_CANCELED);
+}
+
+void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer,
+                          void (*routine)(void *), void * arg)
+{
+  pthread_descr self = thread_self();
+  buffer->routine = routine;
+  buffer->arg = arg;
+  buffer->prev = self->p_cleanup;
+  self->p_cleanup = buffer;
+}
+
+void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer,
+                         int execute)
+{
+  pthread_descr self = thread_self();
+  if (execute) buffer->routine(buffer->arg);
+  self->p_cleanup = buffer->prev;
+}
+
+void _pthread_cleanup_push_defer(struct _pthread_cleanup_buffer * buffer,
+                                void (*routine)(void *), void * arg)
+{
+  pthread_descr self = thread_self();
+  buffer->routine = routine;
+  buffer->arg = arg;
+  buffer->canceltype = self->p_canceltype;
+  buffer->prev = self->p_cleanup;
+  self->p_canceltype = PTHREAD_CANCEL_DEFERRED;
+  self->p_cleanup = buffer;
+}
+
+void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer,
+                                 int execute)
+{
+  pthread_descr self = thread_self();
+  if (execute) buffer->routine(buffer->arg);
+  self->p_cleanup = buffer->prev;
+  self->p_canceltype = buffer->canceltype;
+  if (self->p_canceled &&
+      self->p_cancelstate == PTHREAD_CANCEL_ENABLE &&
+      self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+    pthread_exit(PTHREAD_CANCELED);
+}
+
+void __pthread_perform_cleanup(void)
+{
+  pthread_descr self = thread_self();
+  struct _pthread_cleanup_buffer * c;
+  for (c = self->p_cleanup; c != NULL; c = c->prev) c->routine(c->arg);
+}
+
+#ifndef PIC
+/* We need a hook to force the cancelation wrappers to be linked in when
+   static libpthread is used.  */
+extern const int __pthread_provide_wrappers;
+static const int * const __pthread_require_wrappers =
+  &__pthread_provide_wrappers;
+#endif
diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c
new file mode 100644 (file)
index 0000000..6807522
--- /dev/null
@@ -0,0 +1,207 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/* and Pavel Krauz (krauz@fsid.cvut.cz).                                */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Condition variables */
+
+#include <errno.h>
+#include <sched.h>
+#include <stddef.h>
+#include <sys/time.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "queue.h"
+#include "restart.h"
+
+static void remove_from_queue(pthread_queue * q, pthread_descr th);
+
+int pthread_cond_init(pthread_cond_t *cond,
+                      const pthread_condattr_t *cond_attr)
+{
+  cond->c_spinlock = 0;
+  queue_init(&cond->c_waiting);
+  return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+  pthread_descr head;
+
+  acquire(&cond->c_spinlock);
+  head = cond->c_waiting.head;
+  release(&cond->c_spinlock);
+  if (head != NULL) return EBUSY;
+  return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+  volatile pthread_descr self = thread_self();
+  acquire(&cond->c_spinlock);
+  enqueue(&cond->c_waiting, self);
+  release(&cond->c_spinlock);
+  pthread_mutex_unlock(mutex);
+  suspend_with_cancellation(self);
+  pthread_mutex_lock(mutex);
+  /* This is a cancellation point */
+  if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+    /* Remove ourselves from the waiting queue if we're still on it */
+    acquire(&cond->c_spinlock);
+    remove_from_queue(&cond->c_waiting, self);
+    release(&cond->c_spinlock);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+  return 0;
+}
+
+static inline int
+pthread_cond_timedwait_relative(pthread_cond_t *cond,
+                               pthread_mutex_t *mutex,
+                               const struct timespec * reltime)
+{
+  volatile pthread_descr self = thread_self();
+  sigset_t unblock, initial_mask;
+  int retsleep;
+  sigjmp_buf jmpbuf;
+
+  /* Wait on the condition */
+  acquire(&cond->c_spinlock);
+  enqueue(&cond->c_waiting, self);
+  release(&cond->c_spinlock);
+  pthread_mutex_unlock(mutex);
+  /* Set up a longjmp handler for the restart signal */
+  /* No need to save the signal mask, since PTHREAD_SIG_RESTART will be
+     blocked when doing the siglongjmp, and we'll just leave it blocked. */
+  if (sigsetjmp(jmpbuf, 0) == 0) {
+    self->p_signal_jmp = &jmpbuf;
+    self->p_signal = 0;
+    /* Check for cancellation */
+    if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+      retsleep = -1;
+    } else {
+      /* Unblock the restart signal */
+      sigemptyset(&unblock);
+      sigaddset(&unblock, PTHREAD_SIG_RESTART);
+      sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
+      /* Sleep for the required duration */
+      retsleep = __libc_nanosleep(reltime, NULL);
+      /* Block the restart signal again */
+      sigprocmask(SIG_SETMASK, &initial_mask, NULL);
+    }
+  } else {
+    retsleep = -1;
+  }
+  self->p_signal_jmp = NULL;
+  /* Here, either the condition was signaled (self->p_signal != 0)
+                   or we got canceled (self->p_canceled != 0)
+                   or the timeout occurred (retsleep == 0)
+                   or another interrupt occurred (retsleep == -1) */
+  /* Re-acquire the spinlock */
+  acquire(&cond->c_spinlock);
+  /* This is a cancellation point */
+  if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+    remove_from_queue(&cond->c_waiting, self);
+    release(&cond->c_spinlock);
+    pthread_mutex_lock(mutex);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+  /* If not signaled: also remove ourselves and return an error code */
+  if (self->p_signal == 0) {
+    remove_from_queue(&cond->c_waiting, self);
+    release(&cond->c_spinlock);
+    pthread_mutex_lock(mutex);
+    return retsleep == 0 ? ETIMEDOUT : EINTR;
+  }
+  /* Otherwise, return normally */
+  release(&cond->c_spinlock);
+  pthread_mutex_lock(mutex);
+  return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+                           const struct timespec * abstime)
+{
+  struct timeval now;
+  struct timespec reltime;
+  /* Compute a time offset relative to now */
+  __gettimeofday(&now, NULL);
+  reltime.tv_sec = abstime->tv_sec - now.tv_sec;
+  reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
+  if (reltime.tv_nsec < 0) {
+    reltime.tv_nsec += 1000000000;
+    reltime.tv_sec -= 1;
+  }
+  if (reltime.tv_sec < 0) return ETIMEDOUT;
+  return pthread_cond_timedwait_relative(cond, mutex, &reltime);
+}
+
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+  pthread_descr th;
+
+  acquire(&cond->c_spinlock);
+  th = dequeue(&cond->c_waiting);
+  release(&cond->c_spinlock);
+  if (th != NULL) restart(th);
+  return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t *cond)
+{
+  pthread_queue tosignal;
+  pthread_descr th;
+
+  acquire(&cond->c_spinlock);
+  /* Copy the current state of the waiting queue and empty it */
+  tosignal = cond->c_waiting;
+  queue_init(&cond->c_waiting);
+  release(&cond->c_spinlock);
+  /* Now signal each process in the queue */
+  while ((th = dequeue(&tosignal)) != NULL) restart(th);
+  return 0;
+}
+
+int pthread_condattr_init(pthread_condattr_t *attr)
+{
+  return 0;
+}
+
+int pthread_condattr_destroy(pthread_condattr_t *attr)
+{
+  return 0;
+}
+
+/* Auxiliary function on queues */
+
+static void remove_from_queue(pthread_queue * q, pthread_descr th)
+{
+  pthread_descr t;
+
+  if (q->head == NULL) return;
+  if (q->head == th) {
+    q->head = th->p_nextwaiting;
+    if (q->head == NULL) q->tail = NULL;
+    th->p_nextwaiting = NULL;
+    return;
+  }
+  for (t = q->head; t->p_nextwaiting != NULL; t = t->p_nextwaiting) {
+    if (t->p_nextwaiting == th) {
+      t->p_nextwaiting = th->p_nextwaiting;
+      if (th->p_nextwaiting == NULL) q->tail = t;
+      th->p_nextwaiting = NULL;
+      return;
+    }
+  }
+}
diff --git a/linuxthreads/configure b/linuxthreads/configure
new file mode 100755 (executable)
index 0000000..3eafc93
--- /dev/null
@@ -0,0 +1,5 @@
+# This is only to keep the GNU C library configure mechanism happy.
+#
+# Perhaps some day we need a real configuration script for different
+# kernel versions or so.
+exit 0
diff --git a/linuxthreads/errno.c b/linuxthreads/errno.c
new file mode 100644 (file)
index 0000000..3619aa8
--- /dev/null
@@ -0,0 +1,32 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Define the location of errno for the remainder of the C library */
+
+#include <errno.h>
+#include <netdb.h>
+#include "pthread.h"
+#include "internals.h"
+
+int * __errno_location()
+{
+  pthread_descr self = thread_self();
+  return self->p_errnop;
+}
+
+int * __h_errno_location()
+{
+  pthread_descr self = thread_self();
+  return self->p_h_errnop;
+}
diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h
new file mode 100644 (file)
index 0000000..7939605
--- /dev/null
@@ -0,0 +1,280 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Internal data structures */
+
+/* Includes */
+
+#include <bits/libc-lock.h> /* for _LIBC_TSD_KEY_N */
+#include <setjmp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "pt-machine.h"
+
+/* Arguments passed to thread creation routine */
+
+struct pthread_start_args {
+  void * (*start_routine)(void *); /* function to run */
+  void * arg;                   /* its argument */
+  sigset_t mask;                /* initial signal mask for thread */
+  int schedpolicy;              /* initial scheduling policy (if any) */
+  struct sched_param schedparam; /* initial scheduling parameters (if any) */
+};
+
+
+/* We keep thread specific data in a special data structure, a two-level
+   array.  The top-level array contains pointers to dynamically allocated
+   arrays of a certain number of data pointers.  So we can implement a
+   sparse array.  Each dynamic second-level array has
+       PTHREAD_KEY_2NDLEVEL_SIZE
+   entries.  This value shouldn't be too large.  */
+#define PTHREAD_KEY_2NDLEVEL_SIZE      32
+
+/* We need to address PTHREAD_KEYS_MAX key with PTHREAD_KEY_2NDLEVEL_SIZE
+   keys in each subarray.  */
+#define PTHREAD_KEY_1STLEVEL_SIZE \
+  ((PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE - 1) \
+   / PTHREAD_KEY_2NDLEVEL_SIZE)
+
+
+#define PTHREAD_START_ARGS_INITIALIZER { NULL, NULL, {{0, }}, 0, { 0 } }
+
+/* The type of thread descriptors */
+
+typedef struct _pthread_descr_struct * pthread_descr;
+
+struct _pthread_descr_struct {
+  pthread_descr p_nextlive, p_prevlive;
+                                /* Double chaining of active threads */
+  pthread_descr p_nextwaiting;  /* Next element in the queue holding the thr */
+  pthread_t p_tid;              /* Thread identifier */
+  int p_pid;                    /* PID of Unix process */
+  int p_priority;               /* Thread priority (== 0 if not realtime) */
+  int * p_spinlock;             /* Spinlock for synchronized accesses */
+  int p_signal;                 /* last signal received */
+  sigjmp_buf * p_signal_jmp;    /* where to siglongjmp on a signal or NULL */
+  sigjmp_buf * p_cancel_jmp;    /* where to siglongjmp on a cancel or NULL */
+  char p_terminated;            /* true if terminated e.g. by pthread_exit */
+  char p_detached;              /* true if detached */
+  char p_exited;                /* true if the assoc. process terminated */
+  void * p_retval;              /* placeholder for return value */
+  int p_retcode;                /* placeholder for return code */
+  pthread_descr p_joining;      /* thread joining on that thread or NULL */
+  struct _pthread_cleanup_buffer * p_cleanup; /* cleanup functions */
+  char p_cancelstate;           /* cancellation state */
+  char p_canceltype;            /* cancellation type (deferred/async) */
+  char p_canceled;              /* cancellation request pending */
+  int * p_errnop;               /* pointer to used errno variable */
+  int p_errno;                  /* error returned by last system call */
+  int * p_h_errnop;             /* pointer to used h_errno variable */
+  int p_h_errno;                /* error returned by last netdb function */
+  struct pthread_start_args p_start_args; /* arguments for thread creation */
+  void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE]; /* thread-specific data */
+  void * p_libc_specific[_LIBC_TSD_KEY_N]; /* thread-specific data for libc */
+};
+
+/* The type of thread handles. */
+
+typedef struct pthread_handle_struct * pthread_handle;
+
+struct pthread_handle_struct {
+  int h_spinlock;               /* Spinlock for sychronized access */
+  pthread_descr h_descr;        /* Thread descriptor or NULL if invalid */
+};
+
+/* The type of messages sent to the thread manager thread */
+
+struct pthread_request {
+  pthread_descr req_thread;     /* Thread doing the request */
+  enum {                        /* Request kind */
+    REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT
+  } req_kind;
+  union {                       /* Arguments for request */
+    struct {                    /* For REQ_CREATE: */
+      const pthread_attr_t * attr; /* thread attributes */
+      void * (*fn)(void *);     /*   start function */
+      void * arg;               /*   argument to start function */
+      sigset_t mask;            /*   signal mask */
+    } create;
+    struct {                    /* For REQ_FREE: */
+      pthread_descr thread;     /*   descriptor of thread to free */
+    } free;
+    struct {                    /* For REQ_PROCESS_EXIT: */
+      int code;                 /*   exit status */
+    } exit;
+  } req_args;
+};
+
+
+/* Signals used for suspend/restart and for cancellation notification.  */
+
+#ifdef SIGRTMIN
+/* The have real-time signals.  */
+extern int __pthread_sig_restart;
+extern int __pthread_sig_cancel;
+# define PTHREAD_SIG_RESTART __pthread_sig_restart
+# define PTHREAD_SIG_CANCEL __pthread_sig_cancel
+#else
+# define PTHREAD_SIG_RESTART SIGUSR1
+# define PTHREAD_SIG_CANCEL SIGUSR2
+#endif
+
+/* Global array of thread handles, used for validating a thread id
+   and retrieving the corresponding thread descriptor. Also used for
+   mapping the available stack segments. */
+
+extern struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX];
+
+/* Descriptor of the initial thread */
+
+extern struct _pthread_descr_struct __pthread_initial_thread;
+
+/* Descriptor of the manager thread */
+
+extern struct _pthread_descr_struct __pthread_manager_thread;
+
+/* Descriptor of the main thread */
+
+extern pthread_descr __pthread_main_thread;
+
+/* Limit between the stack of the initial thread (above) and the
+   stacks of other threads (below). Aligned on a STACK_SIZE boundary.
+   Initially 0, meaning that the current thread is (by definition)
+   the initial thread. */
+
+extern char *__pthread_initial_thread_bos;
+
+/* File descriptor for sending requests to the thread manager.
+   Initially -1, meaning that pthread_initialize must be called. */
+
+extern int __pthread_manager_request;
+
+/* Other end of the pipe for sending requests to the thread manager. */
+
+extern int __pthread_manager_reader;
+
+/* Limits of the thread manager stack. */
+
+extern char *__pthread_manager_thread_bos;
+extern char *__pthread_manager_thread_tos;
+
+/* Pending request for a process-wide exit */
+
+extern int __pthread_exit_requested, __pthread_exit_code;
+
+/* Return the handle corresponding to a thread id */
+
+static inline pthread_handle thread_handle(pthread_t id)
+{
+  return &__pthread_handles[id % PTHREAD_THREADS_MAX];
+}
+
+/* Validate a thread handle. Must have acquired h->h_spinlock before. */
+
+static inline int invalid_handle(pthread_handle h, pthread_t id)
+{
+  return h->h_descr == NULL || h->h_descr->p_tid != id;
+}
+
+/* Fill in defaults left unspecified by pt-machine.h.  */
+
+/* The page size we can get from the system.  This should likely not be
+   changed by the machine file but, you never know.  */
+#ifndef PAGE_SIZE
+#define PAGE_SIZE  (sysconf (_SC_PAGE_SIZE))
+#endif
+
+/* The max size of the thread stack segments.  If the default
+   THREAD_SELF implementation is used, this must be a power of two and
+   a multiple of PAGE_SIZE.  */
+#ifndef STACK_SIZE
+#define STACK_SIZE  (2 * 1024 * 1024)
+#endif
+
+/* The initial size of the thread stack.  Must be a multiple of PAGE_SIZE.  */
+#ifndef INITIAL_STACK_SIZE
+#define INITIAL_STACK_SIZE  (4 * PAGE_SIZE)
+#endif
+
+/* Size of the thread manager stack. The "- 32" avoids wasting space
+   with some malloc() implementations. */
+#ifndef THREAD_MANAGER_STACK_SIZE
+#define THREAD_MANAGER_STACK_SIZE  (2 * PAGE_SIZE - 32)
+#endif
+
+/* The base of the "array" of thread stacks.  The array will grow down from
+   here.  Defaults to the calculated bottom of the initial application
+   stack.  */
+#ifndef THREAD_STACK_START_ADDRESS
+#define THREAD_STACK_START_ADDRESS  __pthread_initial_thread_bos
+#endif
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#ifndef CURRENT_STACK_FRAME
+#define CURRENT_STACK_FRAME  ({ char __csf; &__csf; })
+#endif
+
+/* Recover thread descriptor for the current thread */
+
+static inline pthread_descr thread_self (void) __attribute__ ((const));
+static inline pthread_descr thread_self (void)
+{
+#ifdef THREAD_SELF
+  return THREAD_SELF;
+#else
+  char *sp = CURRENT_STACK_FRAME;
+  if (sp >= __pthread_initial_thread_bos)
+    return &__pthread_initial_thread;
+  else if (sp >= __pthread_manager_thread_bos
+          && sp < __pthread_manager_thread_tos)
+    return &__pthread_manager_thread;
+  else
+    return (pthread_descr)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1;
+#endif
+}
+
+/* Debugging */
+
+#ifdef DEBUG
+#include <assert.h>
+#define ASSERT assert
+#define MSG __pthread_message
+#else
+#define ASSERT(x)
+#define MSG(msg,arg...)
+#endif
+
+/* Internal global functions */
+
+void __pthread_destroy_specifics(void);
+void __pthread_perform_cleanup(void);
+void __pthread_sighandler(int sig);
+void __pthread_message(char * fmt, long arg, ...);
+int __pthread_manager(void *reqfd);
+void __pthread_manager_sighandler(int sig);
+void __pthread_reset_main_thread(void);
+void __fresetlockfiles(void);
+
+
+/* Prototypes for the function without cancelation support when the
+   normal version has it.  */
+extern int __libc_close (int fd);
+extern int __libc_nanosleep (const struct timespec *requested_time,
+                            struct timespec *remaining);
+extern int __libc_read (int fd, void *buf, size_t count);
+extern pid_t __libc_waitpid (pid_t pid, int *stat_loc, int options);
+extern int __libc_write (int fd, const void *buf, size_t count);
diff --git a/linuxthreads/join.c b/linuxthreads/join.c
new file mode 100644 (file)
index 0000000..2bdc189
--- /dev/null
@@ -0,0 +1,145 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Thread termination and joining */
+
+#include <errno.h>
+#include <sched.h>
+#include <unistd.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+
+void pthread_exit(void * retval)
+{
+  pthread_descr self = thread_self();
+  pthread_descr joining;
+  struct pthread_request request;
+
+  /* Reset the cancellation flag to avoid looping if the cleanup handlers
+     contain cancellation points */
+  self->p_canceled = 0;
+  /* Call cleanup functions and destroy the thread-specific data */
+  __pthread_perform_cleanup();
+  __pthread_destroy_specifics();
+  /* Store return value */
+  acquire(self->p_spinlock);
+  self->p_retval = retval;
+  /* Say that we've terminated */
+  self->p_terminated = 1;
+  /* See if someone is joining on us */
+  joining = self->p_joining;
+  release(self->p_spinlock);
+  /* Restart joining thread if any */
+  if (joining != NULL) restart(joining);
+  /* If this is the initial thread, block until all threads have terminated.
+     If another thread calls exit, we'll be terminated from our signal
+     handler. */
+  if (self == __pthread_main_thread && __pthread_manager_request >= 0) {
+    request.req_thread = self;
+    request.req_kind = REQ_MAIN_THREAD_EXIT;
+    __libc_write(__pthread_manager_request, (char *)&request, sizeof(request));
+    suspend(self);
+  }
+  /* Exit the process (but don't flush stdio streams, and don't run
+     atexit functions). */
+  _exit(0);
+}
+
+int pthread_join(pthread_t thread_id, void ** thread_return)
+{
+  volatile pthread_descr self = thread_self();
+  struct pthread_request request;
+  pthread_handle handle = thread_handle(thread_id);
+  pthread_descr th;
+
+  acquire(&handle->h_spinlock);
+  if (invalid_handle(handle, thread_id)) {
+    release(&handle->h_spinlock);
+    return ESRCH;
+  }
+  th = handle->h_descr;
+  if (th == self) {
+    release(&handle->h_spinlock);
+    return EDEADLK;
+  }
+  /* If detached or already joined, error */
+  if (th->p_detached || th->p_joining != NULL) {
+    release(&handle->h_spinlock);
+    return EINVAL;
+  }
+  /* If not terminated yet, suspend ourselves. */
+  if (! th->p_terminated) {
+    th->p_joining = self;
+    release(&handle->h_spinlock);
+    suspend_with_cancellation(self);
+    /* This is a cancellation point */
+    if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+      th->p_joining = NULL;
+      pthread_exit(PTHREAD_CANCELED);
+    }
+    acquire(&handle->h_spinlock);
+  }
+  /* Get return value */
+  if (thread_return != NULL) *thread_return = th->p_retval;
+  release(&handle->h_spinlock);
+  /* Send notification to thread manager */
+  if (__pthread_manager_request >= 0) {
+    request.req_thread = self;
+    request.req_kind = REQ_FREE;
+    request.req_args.free.thread = th;
+    __libc_write(__pthread_manager_request,
+                (char *) &request, sizeof(request));
+  }
+  return 0;
+}
+
+int pthread_detach(pthread_t thread_id)
+{
+  int terminated;
+  struct pthread_request request;
+  pthread_handle handle = thread_handle(thread_id);
+  pthread_descr th;
+
+  acquire(&handle->h_spinlock);
+  if (invalid_handle(handle, thread_id)) {
+    release(&handle->h_spinlock);
+    return ESRCH;
+  }
+  th = handle->h_descr;
+  /* If already detached, error */
+  if (th->p_detached) {
+    release(&handle->h_spinlock);
+    return EINVAL;
+  }
+  /* If already joining, don't do anything. */
+  if (th->p_joining != NULL) {
+    release(&handle->h_spinlock);
+    return 0;
+  }
+  /* Mark as detached */
+  th->p_detached = 1;
+  terminated = th->p_terminated;
+  release(&handle->h_spinlock);
+  /* If already terminated, notify thread manager to reclaim resources */
+  if (terminated && __pthread_manager_request >= 0) {
+    request.req_thread = thread_self();
+    request.req_kind = REQ_FREE;
+    request.req_args.free.thread = th;
+    __libc_write(__pthread_manager_request,
+                (char *) &request, sizeof(request));
+  }
+  return 0;
+}
diff --git a/linuxthreads/libpthread.map b/linuxthreads/libpthread.map
new file mode 100644 (file)
index 0000000..36767af
--- /dev/null
@@ -0,0 +1,62 @@
+GLIBC_2.0 {
+  global:
+    # Hidden entry point (through macros).
+    _pthread_cleanup_pop; _pthread_cleanup_pop_restore; _pthread_cleanup_push;
+    _pthread_cleanup_push_defer;
+
+    # Overwritten libc functions.
+    close; fcntl; fork; fsync; lseek; msync; nanosleep; open; pause; raise;
+    read; system; tcdrain; wait; waitpid; write;
+
+    # POSIX.1c extensions to libc.
+    flockfile; funlockfile; ftrylockfile;
+
+    # Non-standard POSIX1.x functions.
+    pthread_kill_other_threads_np; pthread_mutexattr_getkind_np;
+    pthread_mutexattr_setkind_np;
+
+    # Real POSIX.1c functions.
+    pthread_atfork; pthread_attr_destroy; pthread_attr_getdetachstate;
+    pthread_attr_getinheritsched; pthread_attr_getschedparam;
+    pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_init;
+    pthread_attr_setdetachstate; pthread_attr_setinheritsched;
+    pthread_attr_setschedparam; pthread_attr_setschedpolicy;
+    pthread_attr_setscope; pthread_cancel; pthread_cond_broadcast;
+    pthread_cond_destroy; pthread_cond_init; pthread_cond_signal;
+    pthread_cond_timedwait; pthread_cond_wait; pthread_condattr_destroy;
+    pthread_condattr_init; pthread_create; pthread_detach; pthread_equal;
+    pthread_exit; pthread_getschedparam; pthread_getspecific; pthread_join;
+    pthread_key_create; pthread_key_delete; pthread_kill;
+    pthread_mutex_destroy; pthread_mutex_init; pthread_mutex_lock;
+    pthread_mutex_trylock; pthread_mutex_unlock; pthread_mutexattr_destroy;
+    pthread_mutexattr_init; pthread_once; pthread_self; pthread_setcancelstate;
+    pthread_setcanceltype; pthread_setschedparam; pthread_setspecific;
+    pthread_sigmask; pthread_testcancel;
+
+    sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait;
+    sigwait;
+
+    # Protected names for functions used in other shared objects.
+    __pthread_getspecific; __pthread_initialize; __pthread_mutex_destroy;
+    __pthread_mutex_init; __pthread_mutex_lock; __pthread_mutex_trylock;
+    __pthread_mutex_unlock; __pthread_mutexattr_destroy;
+    __pthread_mutexattr_init; __pthread_mutexattr_setkind_np;
+    __pthread_setspecific;
+
+    # The error functions.
+    __errno_location; __h_errno_location;
+
+  local:
+    *;
+};
+
+
+GLIBC_2.1 {
+  global:
+    # Unix98 extensions.
+    pthread_rwlock_init; pthread_rwlock_destroy; pthread_rwlock_rdlock;
+    pthread_rwlock_tryrdlock; pthread_rwlock_wrlock; pthread_rwlock_trywrlock;
+    pthread_rwlock_unlock; pthread_rwlockattr_init; pthread_rwlockattr_destroy;
+    pthread_rwlockattr_getpshared; pthread_rwlockattr_setpshared;
+    pthread_rwlockattr_getkind_np; pthread_rwlockattr_setkind_np;
+} GLIBC_2.0;
diff --git a/linuxthreads/lockfile.c b/linuxthreads/lockfile.c
new file mode 100644 (file)
index 0000000..b0cb2ca
--- /dev/null
@@ -0,0 +1,87 @@
+/* lockfile - Handle locking and unlocking of stream.
+   Copyright (C) 1996 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <bits/libc-lock.h>
+#include <stdio.h>
+#include <pthread.h>
+
+#ifdef USE_IN_LIBIO
+#include "../libio/libioP.h"
+#endif
+
+void
+__flockfile (FILE *stream)
+{
+#ifdef USE_IN_LIBIO
+  __pthread_mutex_lock (stream->_lock);
+#else
+#endif
+}
+#ifdef USE_IN_LIBIO
+#undef _IO_flockfile
+strong_alias (__flockfile, _IO_flockfile)
+#endif
+weak_alias (__flockfile, flockfile);
+
+
+void
+__funlockfile (FILE *stream)
+{
+#ifdef USE_IN_LIBIO
+  __pthread_mutex_unlock (stream->_lock);
+#else
+#endif
+}
+#ifdef USE_IN_LIBIO
+#undef _IO_funlockfile
+strong_alias (__funlockfile, _IO_funlockfile)
+#endif
+weak_alias (__funlockfile, funlockfile);
+
+
+int
+__ftrylockfile (FILE *stream)
+{
+#ifdef USE_IN_LIBIO
+  return __pthread_mutex_trylock (stream->_lock);
+#else
+#endif
+}
+#ifdef USE_IN_LIBIO
+strong_alias (__ftrylockfile, _IO_ftrylockfile)
+#endif
+weak_alias (__ftrylockfile, ftrylockfile);
+
+
+void
+__fresetlockfiles (void)
+{
+#ifdef USE_IN_LIBIO
+  _IO_FILE *fp;
+  pthread_mutexattr_t attr;
+
+  __pthread_mutexattr_init (&attr);
+  __pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
+
+  for (fp = _IO_list_all; fp != NULL; fp = fp->_chain)
+    __pthread_mutex_init (fp->_lock, &attr);
+
+  __pthread_mutexattr_destroy (&attr);
+#endif
+}
diff --git a/linuxthreads/man/Makefile b/linuxthreads/man/Makefile
new file mode 100644 (file)
index 0000000..4875c3d
--- /dev/null
@@ -0,0 +1,31 @@
+SOURCES=pthread_atfork.man pthread_attr_init.man pthread_cancel.man \
+  pthread_cleanup_push.man pthread_cond_init.man \
+  pthread_condattr_init.man pthread_create.man pthread_detach.man \
+  pthread_equal.man pthread_exit.man pthread_join.man \
+  pthread_key_create.man pthread_mutex_init.man \
+  pthread_mutexattr_init.man pthread_once.man pthread_self.man \
+  pthread_setschedparam.man pthread_sigmask.man sem_init.man \
+  pthread_kill_other_threads_np.man
+
+MANPAGES=$(SOURCES:.man=.3thr)
+
+PREPRO=perl troffprepro
+
+MANDIR=/usr/man/man3
+
+all: $(MANPAGES)
+
+.SUFFIXES: .man .3thr
+
+.man.3thr:
+       $(PREPRO) $*.man $*.3thr
+
+$(MANPAGES): troffprepro
+
+clean:
+       rm -f *.3thr
+       rm -f *~
+
+install:
+       install *.3thr $(MANDIR)
+       @echo "*** Remember to run /usr/sbin/makewhatis `dirname $(MANDIR)` at some point"
diff --git a/linuxthreads/man/pthread_atfork.man b/linuxthreads/man/pthread_atfork.man
new file mode 100644 (file)
index 0000000..4d06a56
--- /dev/null
@@ -0,0 +1,58 @@
+.TH PTHREAD_ATFORK 3 LinuxThreads
+
+.SH NAME
+pthread_atfork \- register handlers to be called at fork(2) time
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
+
+.SH DESCRIPTION
+
+!pthread_atfork! registers handler functions to be called just before
+and just after a new process is created with !fork!(2). The |prepare|
+handler will be called from the parent process, just before the new
+process is created. The |parent| handler will be called from the parent
+process, just before !fork!(2) returns. The |child| handler will be
+called from the child process, just before !fork!(2) returns.
+
+One or several of the three handlers |prepare|, |parent| and |child|
+can be given as !NULL!, meaning that no handler needs to be called at
+the corresponding point.
+
+!pthread_atfork! can be called several times to install several sets
+of handlers. At !fork!(2) time, the |prepare| handlers are called in
+LIFO order (last added with !pthread_atfork!, first called before !fork!),
+while the |parent| and |child| handlers are called in FIFO order
+(first added, first called).
+
+To understand the purpose of !pthread_atfork!, recall that !fork!(2)
+duplicates the whole memory space, including mutexes in their current
+locking state, but only the calling thread: other threads are not
+running in the child process. Thus, if a mutex is locked by a thread
+other than the thread calling !fork!, that mutex will remain locked
+forever in the child process, possibly blocking the execution of the
+child process. To avoid this, install handlers with !pthread_atfork!
+as follows: the |prepare| handler locks the global mutexes (in locking
+order), and the |parent| and |child| handlers unlock them (in
+reverse order). Alternatively, |prepare| and |parent| can be set to
+!NULL! and |child| to a function that calls !pthread_mutex_init! on
+the global mutexes.
+
+.SH "RETURN VALUE"
+
+!pthread_atfork! returns 0 on success and a non-zero error code on error.
+
+.SH ERRORS
+.TP
+!ENOMEM!
+insufficient memory available to register the handlers.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!fork!(2),
+!pthread_mutex_lock!(3),
+!pthread_mutex_unlock!(3).
diff --git a/linuxthreads/man/pthread_attr_init.man b/linuxthreads/man/pthread_attr_init.man
new file mode 100644 (file)
index 0000000..bd5a169
--- /dev/null
@@ -0,0 +1,221 @@
+.TH PTHREAD_ATTR_INIT 3 LinuxThreads
+
+.XREF pthread_attr_destroy
+.XREF pthread_attr_setdetachstate
+.XREF pthread_attr_getdetachstate
+.XREF pthread_attr_setschedparam
+.XREF pthread_attr_getschedparam
+.XREF pthread_attr_setschedpolicy
+.XREF pthread_attr_getschedpolicy
+.XREF pthread_attr_setinheritsched
+.XREF pthread_attr_getinheritsched
+.XREF pthread_attr_setscope
+.XREF pthread_attr_getscope
+
+.SH NAME
+pthread_attr_init, pthread_attr_destroy, pthread_attr_setdetachstate, pthread_attr_getdetachstate, pthread_attr_setschedparam, pthread_attr_getschedparam, pthread_attr_setschedpolicy, pthread_attr_getschedpolicy, pthread_attr_setinheritsched, pthread_attr_getinheritsched, pthread_attr_setscope, pthread_attr_getscope \- thread creation attributes
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_attr_init(pthread_attr_t *attr);
+
+int pthread_attr_destroy(pthread_attr_t *attr);
+
+int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
+
+int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
+
+int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
+
+int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
+
+int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
+
+int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
+
+int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
+
+int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
+
+int pthread_attr_setscope(pthread_attr_t *attr, int scope);
+
+int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
+
+.SH DESCRIPTION
+
+Setting attributes for threads is achieved by filling a
+thread attribute object |attr| of type !pthread_attr_t!, then passing it as
+second argument to !pthread_create!(3). Passing !NULL! is equivalent to
+passing a thread attribute object with all attributes set to their
+default values.
+
+!pthread_attr_init! initializes the thread attribute object |attr| and
+fills it with default values for the attributes. (The default values
+are listed below for each attribute.)
+
+Each attribute |attrname| (see below for a list of all attributes) can
+be individually set using the function !pthread_attr_set!|attrname|
+and retrieved using the function !pthread_attr_get!|attrname|.
+
+!pthread_attr_destroy! destroys a thread attribute object, which
+must not be reused until it is reinitialized. !pthread_attr_destroy!
+does nothing in the LinuxThreads implementation. 
+
+Attribute objects are consulted only when creating a new thread. The
+same attribute object can be used for creating several
+threads. Modifying an attribute object after a call to
+!pthread_create! does not change the attributes of the thread
+previously created.
+
+The following thread attributes are supported:
+
+.SS detachstate
+
+Control whether the thread is created in the joinable state (value
+!PTHREAD_CREATE_JOINABLE!) or in the detached state
+(!PTHREAD_CREATE_DETACHED!). 
+
+Default value: !PTHREAD_CREATE_JOINABLE!.
+
+In the joinable state, another thread can synchronize on the thread
+termination and recover its termination code using !pthread_join!(3),
+but some of the thread resources are kept allocated after the thread
+terminates, and reclaimed only when another thread performs
+!pthread_join!(3) on that thread.
+
+In the detached state, the thread resources are immediately freed when
+it terminates, but !pthread_join!(3) cannot be used to synchronize on
+the thread termination.
+
+A thread created in the joinable state can later be put in the
+detached thread using !pthread_detach!(3).
+
+.SS schedpolicy
+
+Select the scheduling policy for the thread: one of
+!SCHED_OTHER! (regular, non-realtime scheduling),
+!SCHED_RR! (realtime, round-robin) or
+!SCHED_FIFO! (realtime, first-in first-out). See
+!sched_setpolicy!(2) for more information on scheduling policies.
+
+Default value: !SCHED_OTHER!.
+
+The realtime scheduling policies !SCHED_RR! and !SCHED_FIFO! are
+available only to processes with superuser privileges.
+
+The scheduling policy of a thread can be changed after creation with
+!pthread_setschedparam!(3).
+
+.SS schedparam
+
+Contain the scheduling parameters (essentially, the scheduling
+priority) for the thread. See !sched_setparam!(2) for more information
+on scheduling parameters. 
+
+Default value: priority is 0.
+
+This attribute is not significant if the scheduling policy is !SCHED_OTHER!;
+it only matters for the realtime policies !SCHED_RR! and !SCHED_FIFO!.
+
+The scheduling priority of a thread can be changed after creation with
+!pthread_setschedparam!(3).
+
+.SS inheritsched
+
+Indicate whether the scheduling policy and scheduling parameters for
+the newly created thread are determined by the values of the
+|schedpolicy| and |schedparam| attributes (value
+!PTHREAD_EXPLICIT_SCHED!) or are inherited from the parent thread
+(value !PTHREAD_INHERIT_SCHED!).
+
+Default value: !PTHREAD_EXPLICIT_SCHED!.
+
+.SS scope
+
+Define the scheduling contention scope for the created thread.  The
+only value supported in the LinuxThreads implementation is
+!PTHREAD_SCOPE_SYSTEM!, meaning that the threads contend for CPU time
+with all processes running on the machine. In particular, thread
+priorities are interpreted relative to the priorities of all other
+processes on the machine. The other value specified by the standard,
+!PTHREAD_SCOPE_PROCESS!, means that scheduling contention occurs only
+between the threads of the running process: thread priorities are
+interpreted relative to the priorities of the other threads of the
+process, regardless of the priorities of other processes.
+!PTHREAD_SCOPE_PROCESS! is not supported in LinuxThreads.
+
+Default value: !PTHREAD_SCOPE_SYSTEM!.
+
+.SH "RETURN VALUE"
+
+All functions return 0 on success and a non-zero error code on error.
+On success, the !pthread_attr_get!|attrname| functions also store the
+current value of the attribute |attrname| in the location pointed to
+by their second argument.
+
+.SH ERRORS
+
+The !pthread_attr_setdetachstate! function returns the following error
+codes on error:
+.RS
+.TP
+!EINVAL!
+the specified |detachstate| is not one of !PTHREAD_CREATE_JOINABLE! or
+!PTHREAD_CREATE_DETACHED!.
+.RE
+
+The !pthread_attr_setschedparam! function returns the following error
+codes on error:
+.RS
+.TP
+!EINVAL!
+the priority specified in |param| is outside the range of allowed
+priorities for the scheduling policy currently in |attr|
+(1 to 99 for !SCHED_FIFO! and !SCHED_RR!; 0 for !SCHED_OTHER!).
+.RE
+
+The !pthread_attr_setschedpolicy! function returns the following error
+codes on error:
+.RS
+.TP
+!EINVAL!
+the specified |policy| is not one of !SCHED_OTHER!, !SCHED_FIFO!, or
+!SCHED_RR!.
+
+.TP
+!ENOTSUP!
+|policy| is !SCHED_FIFO! or !SCHED_RR!, and the effective user of the
+calling process is not super-user.
+.RE
+
+The !pthread_attr_setinheritsched! function returns the following error
+codes on error:
+.RS
+.TP
+!EINVAL!
+the specified |inherit| is not one of !PTHREAD_INHERIT_SCHED! or
+!PTHREAD_EXPLICIT_SCHED!.
+.RE
+
+The !pthread_attr_setscope! function returns the following error
+codes on error:
+.RS
+.TP
+!EINVAL!
+the specified |scope| is not one of !PTHREAD_SCOPE_SYSTEM! or
+!PTHREAD_SCOPE_PROCESS!.
+
+.TP
+!ENOTSUP!
+the specified |scope| is !PTHREAD_SCOPE_PROCESS! (not supported).
+.RE
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_create!(3),
+!pthread_join!(3),
+!pthread_detach!(3),
+!pthread_setschedparam!(3).
diff --git a/linuxthreads/man/pthread_cancel.man b/linuxthreads/man/pthread_cancel.man
new file mode 100644 (file)
index 0000000..202d5c9
--- /dev/null
@@ -0,0 +1,155 @@
+.TH PTHREAD_CANCEL 3 LinuxThreads
+
+.XREF pthread_setcancelstate
+.XREF pthread_setcanceltype
+.XREF pthread_testcancel
+
+.SH NAME
+pthread_cancel, pthread_setcancelstate, pthread_setcanceltype, pthread_testcancel \- thread cancellation
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_cancel(pthread_t thread);
+
+int pthread_setcancelstate(int state, int *oldstate);
+
+int pthread_setcanceltype(int type, int *oldtype);
+
+void pthread_testcancel(void);
+
+.SH DESCRIPTION
+
+Cancellation is the mechanism by which a thread can terminate the
+execution of another thread. More precisely, a thread can send a
+cancellation request to another thread. Depending on its settings, the
+target thread can then either ignore the request, honor it
+immediately, or defer it till it reaches a cancellation point.
+
+When a thread eventually honors a cancellation request, it performs as
+if !pthread_exit(PTHREAD_CANCELED)! has been called at that point:
+all cleanup handlers are executed in reverse order, finalization
+functions for thread-specific data are called, and finally the thread
+stops executing with the return value !PTHREAD_CANCELED!. See
+!pthread_exit!(3) for more information.
+
+!pthread_cancel! sends a cancellation request to the thread denoted
+by the |thread| argument.
+
+!pthread_setcancelstate! changes the cancellation state for the
+calling thread -- that is, whether cancellation requests are ignored
+or not. The |state| argument is the new cancellation state: either
+!PTHREAD_CANCEL_ENABLE! to enable cancellation, or
+!PTHREAD_CANCEL_DISABLE! to disable cancellation (cancellation
+requests are ignored). If |oldstate| is not !NULL!, the previous
+cancellation state is stored in the location pointed to by |oldstate|,
+and can thus be restored later by another call to
+!pthread_setcancelstate!.
+
+!pthread_setcanceltype! changes the type of responses to cancellation
+requests for the calling thread: asynchronous (immediate) or deferred.
+The |type| argument is the new cancellation type: either
+!PTHREAD_CANCEL_ASYNCHRONOUS! to cancel the calling thread as soon as
+the cancellation request is received, or !PTHREAD_CANCEL_DEFERRED! to
+keep the cancellation request pending until the next cancellation
+point. If |oldtype| is not !NULL!, the previous
+cancellation state is stored in the location pointed to by |oldtype|,
+and can thus be restored later by another call to
+!pthread_setcanceltype!.
+
+Threads are always created by !pthread_create!(3) with cancellation
+enabled and deferred. That is, the initial cancellation state is
+!PTHREAD_CANCEL_ENABLE! and the initial type is
+!PTHREAD_CANCEL_DEFERRED!.
+
+Cancellation points are those points in the program execution where a
+test for pending cancellation requests is performed and cancellation
+is executed if positive. The following POSIX threads functions
+are cancellation points:
+
+!pthread_join!(3)
+.br
+!pthread_cond_wait!(3)
+.br
+!pthread_cond_timedwait!(3)
+.br
+!pthread_testcancel!(3)
+.br
+!sem_wait!(3)
+.br
+!sigwait!(3)
+
+All other POSIX threads functions are guaranteed not to be
+cancellation points. That is, they never perform cancellation in
+deferred cancellation mode.
+
+!pthread_testcancel! does nothing except testing for pending
+cancellation and executing it. Its purpose is to introduce explicit
+checks for cancellation in long sequences of code that do not call
+cancellation point functions otherwise.
+
+.SH "RETURN VALUE"
+
+!pthread_cancel!, !pthread_setcancelstate! and
+!pthread_setcanceltype! return 0 on success and a non-zero error code
+on error.
+
+.SH ERRORS
+!pthread_cancel! returns the following error code on error:
+.RS
+.TP
+!ESRCH!
+no thread could be found corresponding to that specified by the |thread| ID.
+.RE
+
+!pthread_setcancelstate! returns the following error code on error:
+.RS
+.TP
+!EINVAL!
+the |state| argument is not !PTHREAD_CANCEL_ENABLE! nor
+!PTHREAD_CANCEL_DISABLE!
+.RE
+
+!pthread_setcanceltype! returns the following error code on error:
+.RS
+.TP
+!EINVAL!
+the |type| argument is not !PTHREAD_CANCEL_DEFERRED! nor
+!PTHREAD_CANCEL_ASYNCHRONOUS!
+.RE
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_exit!(3),
+!pthread_cleanup_push!(3),
+!pthread_cleanup_pop!(3).
+
+.SH BUGS
+
+POSIX specifies that a number of system calls (basically, all
+system calls that may block, such as !read!(2), !write!(2), !wait!(2),
+etc.) and library functions that may call these system calls (e.g.
+!fprintf!(3)) are cancellation points.  LinuxThreads is not yet
+integrated enough with the C library to implement this, and thus none
+of the C library functions is a cancellation point.
+
+For system calls at least, there is a workaround. Cancellation
+requests are transmitted to the target thread by sending it a
+signal. That signal will interrupt all blocking system calls, causing
+them to return immediately with the !EINTR! error. So, checking for
+cancellation during a !read! system call, for instance, can be
+achieved as follows:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_testcancel();
+retcode = read(fd, buffer, length);
+pthread_testcancel();
+.ft
+.LP
+.RE
+.fi
diff --git a/linuxthreads/man/pthread_cleanup_push.man b/linuxthreads/man/pthread_cleanup_push.man
new file mode 100644 (file)
index 0000000..1591431
--- /dev/null
@@ -0,0 +1,194 @@
+.TH PTHREAD_CLEANUP 3 LinuxThreads
+
+.XREF pthread_cleanup_pop
+.XREF pthread_cleanup_push_defer_np
+.XREF pthread_cleanup_pop_restore_np
+
+.SH NAME
+pthread_cleanup_push, pthread_cleanup_pop, pthread_cleanup_push_defer_np, pthread_cleanup_pop_restore_np \- install and remove cleanup handlers
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+void pthread_cleanup_push(void (*routine) (void *), void *arg);
+
+void pthread_cleanup_pop(int execute);
+
+void pthread_cleanup_push_defer_np(void (*routine) (void *), void *arg);
+
+void pthread_cleanup_pop_restore_np(int execute);
+
+.SH DESCRIPTION
+
+Cleanup handlers are functions that get called when a thread
+terminates, either by calling !pthread_exit!(3) or because of
+cancellation. Cleanup handlers are installed and removed following a
+stack-like discipline.
+
+The purpose of cleanup handlers is to free the resources that a thread
+may hold at the time it terminates. In particular, if a thread
+exits or is cancelled while it owns a locked mutex, the mutex will
+remain locked forever and prevent other threads from executing
+normally. The best way to avoid this is, just before locking the
+mutex, to install a cleanup handler whose effect is to unlock the
+mutex. Cleanup handlers can be used similarly to free blocks allocated
+with !malloc!(3) or close file descriptors on thread termination.
+
+!pthread_cleanup_push! installs the |routine| function with argument
+|arg| as a cleanup handler. From this point on to the matching
+!pthread_cleanup_pop!, the function |routine| will be called with
+arguments |arg| when the thread terminates, either through !pthread_exit!(3)
+or by cancellation. If several cleanup handlers are active at that
+point, they are called in LIFO order: the most recently installed
+handler is called first.
+
+!pthread_cleanup_pop! removes the most recently installed cleanup
+handler. If the |execute| argument is not 0, it also executes the
+handler, by calling the |routine| function with arguments |arg|. If
+the |execute| argument is 0, the handler is only removed but not
+executed.
+
+Matching pairs of !pthread_cleanup_push! and !pthread_cleanup_pop!
+must occur in the same function, at the same level of block nesting.
+Actually, !pthread_cleanup_push! and !pthread_cleanup_pop! are macros,
+and the expansion of !pthread_cleanup_push! introduces an open brace !{!
+with the matching closing brace !}! being introduced by the expansion
+of the matching !pthread_cleanup_pop!.
+
+!pthread_cleanup_push_defer_np! is a non-portable extension that
+combines !pthread_cleanup_push! and !pthread_setcanceltype!(3).
+It pushes a cleanup handler just as !pthread_cleanup_push! does, but
+also saves the current cancellation type and sets it to deferred
+cancellation. This ensures that the cleanup mechanism is effective
+even if the thread was initially in asynchronous cancellation mode.
+
+!pthread_cleanup_pop_restore_np! pops a cleanup handler introduced by
+!pthread_cleanup_push_defer_np!, and restores the cancellation type to
+its value at the time !pthread_cleanup_push_defer_np! was called.
+
+!pthread_cleanup_push_defer_np! and !pthread_cleanup_pop_restore_np!
+must occur in matching pairs, at the same level of block nesting.
+
+The following sequence
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_cleanup_push_defer_np(routine, arg);
+...
+pthread_cleanup_pop_defer_np(execute);
+.ft
+.LP
+.RE
+.fi
+
+is functionally equivalent to (but more compact and more efficient than)
+
+.RS
+.ft 3
+.nf
+.sp
+{ int oldtype;
+  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
+  pthread_cleanup_push(routine, arg);
+  ...
+  pthread_cleanup_pop(execute);
+  pthread_setcanceltype(oldtype, NULL);
+}
+.ft
+.LP
+.RE
+.fi
+
+.SH "RETURN VALUE"
+
+None.
+
+.SH ERRORS
+
+None.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_exit!(3),
+!pthread_cancel!(3),
+!pthread_setcanceltype!(3).
+
+.SH EXAMPLE
+
+Here is how to lock a mutex |mut| in such a way that it will be
+unlocked if the thread is canceled while |mut| is locked:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
+pthread_mutex_lock(&mut);
+/* do some work */
+pthread_mutex_unlock(&mut);
+pthread_cleanup_pop(0);
+.ft
+.LP
+.RE
+.fi
+
+Equivalently, the last two lines can be replaced by
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_cleanup_pop(1);
+.ft
+.LP
+.RE
+.fi
+
+Notice that the code above is safe only in deferred cancellation mode
+(see !pthread_setcanceltype!(3)). In asynchronous cancellation mode,
+a cancellation can occur between !pthread_cleanup_push! and
+!pthread_mutex_lock!, or between !pthread_mutex_unlock! and
+!pthread_cleanup_pop!, resulting in both cases in the thread trying to
+unlock a mutex not locked by the current thread. This is the main
+reason why asynchronous cancellation is difficult to use.
+
+If the code above must also work in asynchronous cancellation mode,
+then it must switch to deferred mode for locking and unlocking the
+mutex:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
+pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
+pthread_mutex_lock(&mut);
+/* do some work */
+pthread_cleanup_pop(1);
+pthread_setcanceltype(oldtype, NULL);
+.ft
+.LP
+.RE
+.fi
+
+The code above can be rewritten in a more compact and more
+efficient way, using the non-portable functions
+!pthread_cleanup_push_defer_np! and !pthread_cleanup_pop_restore_np!:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_cleanup_push_restore_np(pthread_mutex_unlock, (void *) &mut);
+pthread_mutex_lock(&mut);
+/* do some work */
+pthread_cleanup_pop_restore_np(1);
+.ft
+.LP
+.RE
+.fi
+
diff --git a/linuxthreads/man/pthread_cond_init.man b/linuxthreads/man/pthread_cond_init.man
new file mode 100644 (file)
index 0000000..b803f08
--- /dev/null
@@ -0,0 +1,235 @@
+.TH PTHREAD_COND 3 LinuxThreads
+
+.XREF pthread_cond_signal
+.XREF pthread_cond_broadcast
+.XREF pthread_cond_wait
+.XREF pthread_cond_timedwait
+.XREF pthread_cond_destroy
+
+.SH NAME
+pthread_cond_init, pthread_cond_destroy, pthread_cond_signal, pthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait \- operations on conditions
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
+
+int pthread_cond_signal(pthread_cond_t *cond);
+
+int pthread_cond_broadcast(pthread_cond_t *cond);
+
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
+
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
+
+int pthread_cond_destroy(pthread_cond_t *cond);
+
+.SH DESCRIPTION
+
+A condition (short for ``condition variable'') is a synchronization
+device that allows threads to suspend execution and relinquish the
+processors until some predicate on shared data is satisfied. The basic
+operations on conditions are: signal the condition (when the
+predicate becomes true), and wait for the condition, suspending the
+thread execution until another thread signals the condition.
+
+A condition variable must always be associated with a mutex, to avoid
+the race condition where a thread prepares to wait on a condition
+variable and another thread signals the condition just before the
+first thread actually waits on it.
+
+!pthread_cond_init! initializes the condition variable |cond|, using the
+condition attributes specified in |cond_attr|, or default attributes
+if |cond_attr| is !NULL!. The LinuxThreads implementation supports no
+attributes for conditions, hence the |cond_attr| parameter is actually
+ignored.
+
+Variables of type !pthread_cond_t! can also be initialized
+statically, using the constant !PTHREAD_COND_INITIALIZER!.
+
+!pthread_cond_signal! restarts one of the threads that are waiting on
+the condition variable |cond|. If no threads are waiting on |cond|,
+nothing happens. If several threads are waiting on |cond|, exactly one
+is restarted, but it is not specified which.
+
+!pthread_cond_broadcast! restarts all the threads that are waiting on
+the condition variable |cond|. Nothing happens if no threads are
+waiting on |cond|.
+
+!pthread_cond_wait! atomically unlocks the |mutex| (as per
+!pthread_unlock_mutex!) and waits for the condition variable |cond| to
+be signaled. The thread execution is suspended and does not consume
+any CPU time until the condition variable is signaled. The |mutex|
+must be locked by the calling thread on entrance to
+!pthread_cond_wait!. Before returning to the calling thread,
+!pthread_cond_wait! re-acquires |mutex| (as per !pthread_lock_mutex!).
+
+Unlocking the mutex and suspending on the condition variable is done
+atomically. Thus, if all threads always acquire the mutex before
+signaling the condition, this guarantees that the condition cannot be
+signaled (and thus ignored) between the time a thread locks the mutex
+and the time it waits on the condition variable.
+
+!pthread_cond_timedwait! atomically unlocks |mutex| and waits on
+|cond|, as !pthread_cond_wait! does, but it also bounds the duration
+of the wait. If |cond| has not been signaled within the amount of time
+specified by |abstime|, the mutex |mutex| is re-acquired and
+!pthread_cond_timedwait! returns the error !ETIMEDOUT!. 
+The |abstime| parameter specifies an absolute time, with the same
+origin as !time!(2) and !gettimeofday!(2): an |abstime| of 0
+corresponds to 00:00:00 GMT, January 1, 1970.
+
+!pthread_cond_destroy! destroys a condition variable, freeing the
+resources it might hold. No threads must be waiting on the condition
+variable on entrance to !pthread_cond_destroy!. In the LinuxThreads
+implementation, no resources are associated with condition variables,
+thus !pthread_cond_destroy! actually does nothing except checking that
+the condition has no waiting threads.
+
+.SH CANCELLATION
+
+!pthread_cond_wait! and !pthread_cond_timedwait! are cancellation
+points. If a thread is cancelled while suspended in one of these
+functions, the thread immediately resumes execution, then locks again
+the |mutex| argument to !pthread_cond_wait! and
+!pthread_cond_timedwait!, and finally executes the cancellation.
+Consequently, cleanup handlers are assured that |mutex| is locked when
+they are called.
+
+.SH "ASYNC-SIGNAL SAFETY"
+
+The condition functions are not async-signal safe, and should not be
+called from a signal handler. In particular, calling
+!pthread_cond_signal! or !pthread_cond_broadcast! from a signal
+handler may deadlock the calling thread.
+
+.SH "RETURN VALUE"
+
+All condition variable functions return 0 on success and a non-zero
+error code on error.
+
+.SH ERRORS
+
+!pthread_cond_init!, !pthread_cond_signal!, !pthread_cond_broadcast!,
+and !pthread_cond_wait! never return an error code.
+
+The !pthread_cond_timedwait! function returns the following error codes
+on error:
+.RS
+.TP
+!ETIMEDOUT!
+the condition variable was not signaled until the timeout specified by
+|abstime|
+
+.TP
+!EINTR!
+!pthread_cond_timedwait! was interrupted by a signal
+.RE
+
+The !pthread_cond_destroy! function returns the following error code
+on error:
+.RS
+.TP
+!EBUSY!
+some threads are currently waiting on |cond|.
+.RE
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_condattr_init!(3),
+!pthread_mutex_lock!(3),
+!pthread_mutex_unlock!(3),
+!gettimeofday!(2),
+!nanosleep!(2).
+
+.SH EXAMPLE
+
+Consider two shared variables |x| and |y|, protected by the mutex |mut|,
+and a condition variable |cond| that is to be signaled whenever |x|
+becomes greater than |y|.
+
+.RS
+.ft 3
+.nf
+.sp
+int x,y;
+pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+.ft
+.LP
+.RE
+.fi
+
+Waiting until |x| is greater than |y| is performed as follows:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_mutex_lock(&mut);
+while (x <= y) {
+        pthread_cond_wait(&cond, &mut);
+}
+/* operate on x and y */
+pthread_mutex_unlock(&mut);
+.ft
+.LP
+.RE
+.fi
+
+Modifications on |x| and |y| that may cause |x| to become greater than
+|y| should signal the condition if needed:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_mutex_lock(&mut);
+/* modify x and y */
+if (x > y) pthread_mutex_broadcast(&cond);
+pthread_mutex_unlock(&mut);
+.ft
+.LP
+.RE
+.fi
+
+If it can be proved that at most one waiting thread needs to be waken
+up (for instance, if there are only two threads communicating through
+|x| and |y|), !pthread_cond_signal! can be used as a slightly more
+efficient alternative to !pthread_cond_broadcast!. In doubt, use
+!pthread_cond_broadcast!.
+
+To wait for |x| to becomes greater than |y| with a timeout of 5
+seconds, do:
+
+.RS
+.ft 3
+.nf
+.sp
+struct timeval now;
+struct timespec timeout;
+int retcode;
+
+pthread_mutex_lock(&mut);
+gettimeofday(&now);
+timeout.tv_sec = now.tv_sec + 5;
+timeout.tv_nsec = now.tv_usec * 1000;
+retcode = 0;
+while (x <= y && retcode != ETIMEDOUT) {
+        retcode = pthread_cond_timedwait(&cond, &mut, &timeout);
+}
+if (retcode == ETIMEDOUT) {
+        /* timeout occurred */
+} else {
+        /* operate on x and y */
+}
+pthread_mutex_unlock(&mut);
+.ft
+.LP
+.RE
+.fi
+
diff --git a/linuxthreads/man/pthread_condattr_init.man b/linuxthreads/man/pthread_condattr_init.man
new file mode 100644 (file)
index 0000000..f491cbe
--- /dev/null
@@ -0,0 +1,39 @@
+.TH PTHREAD_CONDATTR 3 LinuxThreads
+
+.XREF pthread_condattr_destroy
+
+.SH NAME
+pthread_condattr_init, pthread_condattr_destroy \- condition creation attributes
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_condattr_init(pthread_condattr_t *attr);
+
+int pthread_condattr_destroy(pthread_condattr_t *attr);
+
+.SH DESCRIPTION
+
+Condition attributes can be specified at condition creation time, by passing a
+condition attribute object as second argument to !pthread_cond_init!(3).
+Passing !NULL! is equivalent to passing a condition attribute object with
+all attributes set to their default values.
+
+The LinuxThreads implementation supports no attributes for
+conditions. The functions on condition attributes are included only
+for compliance with the POSIX standard.
+
+!pthread_condattr_init! initializes the condition attribute object
+|attr| and fills it with default values for the attributes.
+!pthread_condattr_destroy! destroys a condition attribute object,
+which must not be reused until it is reinitialized. Both functions do
+nothing in the LinuxThreads implementation.
+
+.SH "RETURN VALUE"
+!pthread_condattr_init! and !pthread_condattr_destroy! always return 0.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_cond_init!(3).
diff --git a/linuxthreads/man/pthread_create.man b/linuxthreads/man/pthread_create.man
new file mode 100644 (file)
index 0000000..a940047
--- /dev/null
@@ -0,0 +1,46 @@
+.TH PTHREAD_CREATE 3 LinuxThreads
+
+.SH NAME
+pthread_create \- create a new thread
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
+
+.SH DESCRIPTION
+!pthread_create! creates a new thread of control that executes
+concurrently with the calling thread. The new thread applies the
+function |start_routine| passing it |arg| as first argument. The new
+thread terminates either explicitly, by calling !pthread_exit!(3),
+or implicitly, by returning from the |start_routine| function. The
+latter case is equivalent to calling !pthread_exit!(3) with the result
+returned by |start_routine| as exit code.
+
+The |attr| argument specifies thread attributes to be applied to the
+new thread. See !pthread_attr_init!(3) for a complete list of thread
+attributes. The |attr| argument can also be !NULL!, in which case
+default attributes are used: the created thread is joinable (not
+detached) and has default (non real-time) scheduling policy.
+
+.SH "RETURN VALUE"
+On success, the identifier of the newly created thread is stored in
+the location pointed by the |thread| argument, and a 0 is returned. On
+error, a non-zero error code is returned.
+
+.SH ERRORS
+.TP
+!EAGAIN!
+not enough system resources to create a process for the new thread.
+.TP
+!EAGAIN!
+more than !PTHREAD_THREADS_MAX! threads are already active.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_exit!(3),
+!pthread_join!(3),
+!pthread_detach!(3),
+!pthread_attr_init!(3).
diff --git a/linuxthreads/man/pthread_detach.man b/linuxthreads/man/pthread_detach.man
new file mode 100644 (file)
index 0000000..7b43f45
--- /dev/null
@@ -0,0 +1,44 @@
+.TH PTHREAD_DETACH 3 LinuxThreads
+
+.SH NAME
+pthread_detach \- put a running thread in the detached state
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_detach(pthread_t th);
+
+.SH DESCRIPTION
+!pthread_detach! put the thread |th| in the detached state. This
+guarantees that the memory resources consumed by |th| will be freed
+immediately when |th| terminates. However, this prevents other threads
+from synchronizing on the termination of |th| using !pthread_join!.
+
+A thread can be created initially in the detached state, using the
+!detachstate! attribute to !pthread_create!(3). In contrast,
+!pthread_detach! applies to threads created in the joinable state, and
+which need to be put in the detached state later.
+
+After !pthread_detach! completes, subsequent attempts to perform
+!pthread_join! on |th| will fail. If another thread is already joining
+the thread |th| at the time !pthread_detach! is called,
+!pthread_detach! does nothing and leaves |th| in the joinable state.
+
+.SH "RETURN VALUE"
+On success, 0 is returned. On error, a non-zero error code is returned.
+
+.SH ERRORS
+.TP
+!ESRCH!
+No thread could be found corresponding to that specified by |th|
+.TP
+!EINVAL!
+the thread |th| is already in the detached state
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_create!(3),
+!pthread_join!(3),
+!pthread_attr_setdetachstate!(3).
\ No newline at end of file
diff --git a/linuxthreads/man/pthread_equal.man b/linuxthreads/man/pthread_equal.man
new file mode 100644 (file)
index 0000000..1a03965
--- /dev/null
@@ -0,0 +1,23 @@
+.TH PTHREAD_EQUAL 3 LinuxThreads
+
+.SH NAME
+pthread_equal \- compare two thread identifiers
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_equal(pthread_t thread1, pthread_t thread2);
+
+.SH DESCRIPTION
+!pthread_equal! determines if two thread identifiers refer to the same
+thread.
+
+.SH "RETURN VALUE"
+A non-zero value is returned if |thread1| and |thread2| refer to the
+same thread. Otherwise, 0 is returned.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_self!(3).
diff --git a/linuxthreads/man/pthread_exit.man b/linuxthreads/man/pthread_exit.man
new file mode 100644 (file)
index 0000000..54751e9
--- /dev/null
@@ -0,0 +1,32 @@
+.TH PTHREAD_EXIT 3 LinuxThreads
+
+.SH NAME
+pthread_exit \- terminate the calling thread
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+void pthread_exit(void *retval);
+
+.SH DESCRIPTION
+!pthread_exit! terminates the execution of the calling thread.
+All cleanup handlers that have been set for the calling thread with
+!pthread_cleanup_push!(3) are executed in reverse order (the most
+recently pushed handler is executed first). Finalization functions for
+thread-specific data are then called for all keys that have non-!NULL!
+values associated with them in the calling thread (see
+!pthread_key_create!(3)). Finally, execution of the calling thread is
+stopped.
+
+The |retval| argument is the return value of the thread. It can be
+consulted from another thread using !pthread_join!(3).
+
+.SH "RETURN VALUE"
+The !pthread_exit! function never returns.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_create!(3),
+!pthread_join!(3).
diff --git a/linuxthreads/man/pthread_join.man b/linuxthreads/man/pthread_join.man
new file mode 100644 (file)
index 0000000..d587093
--- /dev/null
@@ -0,0 +1,70 @@
+.TH PTHREAD_JOIN 3 LinuxThreads
+
+.SH NAME
+pthread_join \- wait for termination of another thread
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_join(pthread_t th, void **thread_return);
+
+.SH DESCRIPTION
+!pthread_join! suspends the execution of the calling thread until the
+thread identified by |th| terminates, either by calling !pthread_exit!(3)
+or by being cancelled.
+
+If |thread_return| is not !NULL!, the return value of |th| is stored
+in the location pointed to by |thread_return|.  The return value of
+|th| is either the argument it gave to !pthread_exit!(3), or
+!PTHREAD_CANCELED! if |th| was cancelled.
+
+The joined thread !th! must be in the joinable state: it must not have
+been detached using !pthread_detach!(3) or the
+!PTHREAD_CREATE_DETACHED! attribute to !pthread_create!(3).
+
+When a joinable thread terminates, its memory resources (thread
+descriptor and stack) are not deallocated until another thread
+performs !pthread_join! on it. Therefore, !pthread_join! must be
+called once for each joinable thread created to avoid memory leaks.
+
+At most one thread can wait for the termination of a given
+thread. Calling !pthread_join! on a thread |th| on which another
+thread is already waiting for termination returns an error.
+
+.SH CANCELLATION
+
+!pthread_join! is a cancellation point. If a thread is canceled while
+suspended in !pthread_join!, the thread execution resumes immediately
+and the cancellation is executed without waiting for the |th| thread
+to terminate. If cancellation occurs during !pthread_join!, the |th|
+thread remains not joined.
+
+.SH "RETURN VALUE"
+On success, the return value of |th| is stored in the location pointed
+to by |thread_return|, and 0 is returned. On error, a non-zero error
+code is returned.
+
+.SH ERRORS
+.TP
+!ESRCH!
+No thread could be found corresponding to that specified by |th|.
+.TP
+!EINVAL!
+The |th| thread has been detached.
+.TP
+!EINVAL!
+Another thread is already waiting on termination of |th|.
+.TP
+!EDEADLK!
+The |th| argument refers to the calling thread.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_exit!(3),
+!pthread_detach!(3),
+!pthread_create!(3),
+!pthread_attr_setdetachstate!(3),
+!pthread_cleanup_push!(3),
+!pthread_key_create!(3).
diff --git a/linuxthreads/man/pthread_key_create.man b/linuxthreads/man/pthread_key_create.man
new file mode 100644 (file)
index 0000000..6823e30
--- /dev/null
@@ -0,0 +1,151 @@
+.TH PTHREAD_SPECIFIC 3 LinuxThreads
+
+.SH NAME
+pthread_key_create, pthread_key_delete, pthread_setspecific, pthread_getspecific \- management of thread-specific data
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
+
+int pthread_key_delete(pthread_key_t key);
+
+int pthread_setspecific(pthread_key_t key, const void *pointer);
+
+void * pthread_getspecific(pthread_key_t key);
+
+.SH DESCRIPTION
+
+Programs often need global or static variables that have different
+values in different threads. Since threads share one memory space,
+this cannot be achieved with regular variables. Thread-specific data
+is the POSIX threads answer to this need.
+
+Each thread possesses a private memory block, the thread-specific data
+area, or TSD area for short. This area is indexed by TSD keys. The TSD
+area associates values of type !void *! to TSD keys. TSD keys are
+common to all threads, but the value associated with a given TSD key
+can be different in each thread.
+
+For concreteness, the TSD areas can be viewed as arrays of !void *!
+pointers, TSD keys as integer indices into these arrays, and the value
+of a TSD key as the value of the corresponding array element in the
+calling thread.
+
+When a thread is created, its TSD area initially associates !NULL!
+with all keys.
+
+!pthread_key_create! allocates a new TSD key. The key is stored in the
+location pointed to by |key|. There is a limit of !PTHREAD_KEYS_MAX!
+on the number of keys allocated at a given time. The value initially
+associated with the returned key is !NULL! in all currently executing
+threads.
+
+The |destr_function| argument, if not !NULL!, specifies a destructor
+function associated with the key. When a thread terminates via
+!pthread_exit! or by cancellation, |destr_function| is called with
+arguments the value associated with the key in that thread. The
+|destr_function| is not called if that value is !NULL!. The order in
+which destructor functions are called at thread termination time is
+unspecified.
+
+Before the destructor function is called, the !NULL! value is
+associated with the key in the current thread.  A destructor function
+might, however, re-associate non-!NULL! values to that key or some
+other key.  To deal with this, if after all the destructors have been
+called for all non-!NULL! values, there are still some non-!NULL!
+values with associated destructors, then the process is repeated.  The
+LinuxThreads implementation stops the process after
+!PTHREAD_DESTRUCTOR_ITERATIONS! iterations, even if some non-!NULL!
+values with associated descriptors remain.  Other implementations may
+loop indefinitely.
+
+!pthread_key_delete! deallocates a TSD key. It does not check whether
+non-!NULL! values are associated with that key in the currently
+executing threads, nor call the destructor function associated with
+the key.
+
+!pthread_setspecific! changes the value associated with |key| in the
+calling thread, storing the given |pointer| instead.
+
+!pthread_getspecific! returns the value currently associated with
+|key| in the calling thread.
+
+.SH "RETURN VALUE"
+
+!pthread_key_create!, !pthread_key_delete!, and !pthread_setspecific!
+return 0 on success and a non-zero error code on failure. If
+successful, !pthread_key_create! stores the newly allocated key in the
+location pointed to by its |key| argument.
+
+!pthread_getspecific! returns the value associated with |key| on
+success, and !NULL! on error.
+
+.SH ERRORS
+!pthread_key_create! returns the following error code on error:
+.RS
+.TP
+!EAGAIN!
+!PTHREAD_KEYS_MAX! keys are already allocated
+.RE
+
+!pthread_key_delete! and !pthread_setspecific! return the following
+error code on error:
+.RS
+.TP
+!EINVAL!
+|key| is not a valid, allocated TSD key
+.RE
+
+!pthread_getspecific! returns !NULL! if |key| is not a valid,
+allocated TSD key.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+pthread_create(3), pthread_exit(3), pthread_testcancel(3).
+
+.SH EXAMPLE
+
+The following code fragment allocates a thread-specific array of 100
+characters, with automatic reclaimation at thread exit:
+
+.RS
+.ft 3
+.nf
+.sp
+/* Key for the thread-specific buffer */
+static pthread_key_t buffer_key;
+
+/* Once-only initialisation of the key */
+static pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;
+
+/* Allocate the thread-specific buffer */
+void buffer_alloc(void)
+{
+  pthread_once(&buffer_key_once, buffer_key_alloc);
+  pthread_setspecific(buffer_key, malloc(100));
+}
+
+/* Return the thread-specific buffer */
+char * get_buffer(void)
+{
+  return (char *) pthread_getspecific(buffer_key);
+}
+
+/* Allocate the key */
+static void buffer_key_alloc()
+{
+  pthread_key_create(&buffer_key, buffer_destroy);
+}
+
+/* Free the thread-specific buffer */
+static void buffer_destroy(void * buf)
+{
+  free(buf);
+}
+.ft
+.LP
+.RE
+.fi
diff --git a/linuxthreads/man/pthread_kill_other_threads_np.man b/linuxthreads/man/pthread_kill_other_threads_np.man
new file mode 100644 (file)
index 0000000..0de42d5
--- /dev/null
@@ -0,0 +1,40 @@
+.TH PTHREAD_KILL_OTHER_THREADS_NP 3 LinuxThreads
+
+.SH NAME
+pthread_kill_other_threads_np \- terminate all threads in program except calling thread
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+void pthread_kill_other_threads_np(void);
+
+.SH DESCRIPTION
+!pthread_kill_other_threads_np! is a non-portable LinuxThreads extension.
+It causes all threads in the program to terminate immediately, except
+the calling thread which proceeds normally. It is intended to be
+called just before a thread calls one of the !exec! functions,
+e.g. !execve!(2).
+
+Termination of the other threads is not performed through
+!pthread_cancel!(3) and completely bypasses the cancellation
+mechanism. Hence, the current settings for cancellation state and
+cancellation type are ignored, and the cleanup handlers are not
+executed in the terminated threads.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!execve!(2),
+!pthread_setcancelstate!(3),
+!pthread_setcanceltype!(3),
+!pthread_cancel!(3).
+
+.SH BUGS
+
+According to POSIX 1003.1c, a successful !exec*! in one of the threads
+should terminate automatically all other threads in the program.
+This behavior is not yet implemented in LinuxThreads.
+Calling !pthread_kill_other_threads_np! before !exec*! achieves much
+of the same behavior, except that if !exec*! ultimately fails, then
+all other threads are already killed.
diff --git a/linuxthreads/man/pthread_mutex_init.man b/linuxthreads/man/pthread_mutex_init.man
new file mode 100644 (file)
index 0000000..bda4ec6
--- /dev/null
@@ -0,0 +1,213 @@
+.TH PTHREAD_MUTEX 3 LinuxThreads
+
+.XREF pthread_mutex_lock
+.XREF pthread_mutex_unlock
+.XREF pthread_mutex_trylock
+.XREF pthread_mutex_destroy
+
+.SH NAME
+pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destroy \- operations on mutexes
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
+
+int pthread_mutex_lock(pthread_mutex_t *mutex));
+
+int pthread_mutex_trylock(pthread_mutex_t *mutex);
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex);
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex);
+
+.SH DESCRIPTION
+A mutex is a MUTual EXclusion device, and is useful for protecting
+shared data structures from concurrent modifications, and implementing
+critical sections and monitors.
+
+A mutex has two possible states: unlocked (not owned by any thread),
+and locked (owned by one thread). A mutex can never be owned by two
+different threads simultaneously. A thread attempting to lock a mutex
+that is already locked by another thread is suspended until the owning
+thread unlocks the mutex first.
+
+!pthread_mutex_init! initializes the mutex object pointed to by
+|mutex| according to the mutex attributes specified in |mutexattr|.
+If |mutexattr| is !NULL!, default attributes are used instead.
+
+The LinuxThreads implementation supports only one mutex attributes,
+the |mutex kind|, which is either ``fast'', ``recursive'', or
+``error checking''. The kind of a mutex determines whether
+it can be locked again by a thread that already owns it.
+The default kind is ``fast''. See !pthread_mutexattr_init!(3) for more
+information on mutex attributes.
+
+Variables of type !pthread_mutex_t! can also be initialized
+statically, using the constants !PTHREAD_MUTEX_INITIALIZER! (for fast
+mutexes), !PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP! (for recursive
+mutexes), and !PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP! (for error checking
+mutexes).
+
+!pthread_mutex_lock! locks the given mutex. If the mutex is currently
+unlocked, it becomes locked and owned by the calling thread, and
+!pthread_mutex_lock! returns immediately. If the mutex is already
+locked by another thread, !pthread_mutex_lock! suspends the calling
+thread until the mutex is unlocked.
+
+If the mutex is already locked by the calling thread, the behavior of
+!pthread_mutex_lock! depends on the kind of the mutex. If the mutex is
+of the ``fast'' kind, the calling thread is suspended until the mutex
+is unlocked, thus effectively causing the calling thread to
+deadlock. If the mutex is of the ``error checking'' kind,
+!pthread_mutex_lock! returns immediately with the error code !EDEADLK!.
+If the mutex is of the ``recursive'' kind, !pthread_mutex_lock!
+succeeds and returns immediately, recording the number of times the
+calling thread has locked the mutex. An equal number of
+!pthread_mutex_unlock! operations must be performed before the mutex
+returns to the unlocked state.
+
+!pthread_mutex_trylock! behaves identically to !pthread_mutex_lock!,
+except that it does not block the calling thread if the mutex is
+already locked by another thread (or by the calling thread in the case
+of a ``fast'' mutex). Instead, !pthread_mutex_trylock! returns
+immediately with the error code !EBUSY!.
+
+!pthread_mutex_unlock! unlocks the given mutex. The mutex is assumed
+to be locked and owned by the calling thread on entrance to
+!pthread_mutex_unlock!. If the mutex is of the ``fast'' kind,
+!pthread_mutex_unlock! always returns it to the unlocked state. If it
+is of the ``recursive'' kind, it decrements the locking count of the
+mutex (number of !pthread_mutex_lock! operations performed on it by
+the calling thread), and only when this count reaches zero is the
+mutex actually unlocked.
+
+On ``error checking'' mutexes, !pthread_mutex_unlock! actually checks
+at run-time that the mutex is locked on entrance, and that it was
+locked by the same thread that is now calling !pthread_mutex_unlock!.
+If these conditions are not met, an error code is returned and the
+mutex remains unchanged.  ``Fast'' and ``recursive'' mutexes perform
+no such checks, thus allowing a locked mutex to be unlocked by a
+thread other than its owner. This is non-portable behavior and must
+not be relied upon.
+
+!pthread_mutex_destroy! destroys a mutex object, freeing the resources
+it might hold. The mutex must be unlocked on entrance. In the
+LinuxThreads implementation, no resources are associated with mutex
+objects, thus !pthread_mutex_destroy! actually does nothing except
+checking that the mutex is unlocked.
+
+.SH CANCELLATION
+
+None of the mutex functions is a cancellation point, not even
+!pthread_mutex_lock!, in spite of the fact that it can suspend a
+thread for arbitrary durations. This way, the status of mutexes at
+cancellation points is predictable, allowing cancellation handlers to
+unlock precisely those mutexes that need to be unlocked before the
+thread stops executing. Consequently, threads using deferred
+cancellation should never hold a mutex for extended periods of time.
+
+.SH "ASYNC-SIGNAL SAFETY"
+
+The mutex functions are not async-signal safe. What this means is that
+they should not be called from a signal handler. In particular,
+calling !pthread_mutex_lock! or !pthread_mutex_unlock! from a signal
+handler may deadlock the calling thread.
+
+.SH "RETURN VALUE"
+
+!pthread_mutex_init! always returns 0. The other mutex functions
+return 0 on success and a non-zero error code on error.
+
+.SH ERRORS
+
+The !pthread_mutex_lock! function returns the following error code
+on error:
+.RS
+.TP
+!EINVAL!
+the mutex has not been properly initialized.
+
+.TP
+!EDEADLK!
+the mutex is already locked by the calling thread
+(``error checking'' mutexes only).
+.RE
+
+The !pthread_mutex_trylock! function returns the following error codes
+on error:
+.RS
+.TP
+!EBUSY!
+the mutex could not be acquired because it was currently locked.
+
+.TP
+!EINVAL!
+the mutex has not been properly initialized.
+.RE
+
+The !pthread_mutex_unlock! function returns the following error code
+on error:
+.RS
+.TP
+!EINVAL!
+the mutex has not been properly initialized.
+
+.TP
+!EPERM!
+the calling thread does not own the mutex (``error checking'' mutexes only).
+.RE
+
+The !pthread_mutex_destroy! function returns the following error code
+on error:
+.RS
+.TP
+!EBUSY!
+the mutex is currently locked.
+.RE
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_mutexattr_init!(3),
+!pthread_mutexattr_setkind_np!(3),
+!pthread_cancel!(3).
+
+.SH EXAMPLE
+
+A shared global variable |x| can be protected by a mutex as follows:
+
+.RS
+.ft 3
+.nf
+.sp
+int x;
+pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+.ft
+.LP
+.RE
+.fi
+
+All accesses and modifications to |x| should be bracketed by calls to
+!pthread_mutex_lock! and !pthread_mutex_unlock! as follows:
+
+.RS
+.ft 3
+.nf
+.sp
+pthread_mutex_lock(&mut);
+/* operate on x */
+pthread_mutex_unlock(&mut);
+.ft
+.LP
+.RE
+.fi
+
+
diff --git a/linuxthreads/man/pthread_mutexattr_init.man b/linuxthreads/man/pthread_mutexattr_init.man
new file mode 100644 (file)
index 0000000..5ceefdb
--- /dev/null
@@ -0,0 +1,84 @@
+.TH PTHREAD_MUTEXATTR 3 LinuxThreads
+
+.XREF pthread_mutexattr_destroy
+.XREF pthread_mutexattr_setkind_np
+.XREF pthread_mutexattr_getkind_np
+
+.SH NAME
+pthread_mutexattr_init, pthread_mutexattr_destroy, pthread_mutexattr_setkind_np, pthread_mutexattr_getkind_np \- mutex creation attributes
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_mutexattr_init(pthread_mutexattr_t *attr);
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
+
+int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
+
+int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind);
+
+.SH DESCRIPTION
+
+Mutex attributes can be specified at mutex creation time, by passing a
+mutex attribute object as second argument to !pthread_mutex_init!(3).
+Passing !NULL! is equivalent to passing a mutex attribute object with
+all attributes set to their default values.
+
+!pthread_mutexattr_init! initializes the mutex attribute object |attr|
+and fills it with default values for the attributes.
+
+!pthread_mutexattr_destroy! destroys a mutex attribute object, which
+must not be reused until it is reinitialized. !pthread_mutexattr_destroy!
+does nothing in the LinuxThreads implementation. 
+
+LinuxThreads supports only one mutex attribute: the mutex kind, which
+is either !PTHREAD_MUTEX_FAST_NP! for ``fast'' mutexes,
+!PTHREAD_MUTEX_RECURSIVE_NP! for ``recursive'' mutexes,
+or !PTHREAD_MUTEX_ERRORCHECK_NP! for ``error checking'' mutexes.
+As the !NP! suffix indicates, this is a non-portable extension to the
+POSIX standard and should not be employed in portable programs.
+
+The mutex kind determines what happens if a thread attempts to lock a
+mutex it already owns with !pthread_mutex_lock!(3). If the mutex is of
+the ``fast'' kind, !pthread_mutex_lock!(3) simply suspends the calling
+thread forever.  If the mutex is of the ``error checking'' kind,
+!pthread_mutex_lock!(3) returns immediately with the error code
+!EDEADLK!.  If the mutex is of the ``recursive'' kind, the call to
+!pthread_mutex_lock!(3) returns immediately with a success return
+code. The number of times the thread owning the mutex has locked it is
+recorded in the mutex. The owning thread must call
+!pthread_mutex_unlock!(3) the same number of times before the mutex
+returns to the unlocked state.
+
+The default mutex kind is ``fast'', that is, !PTHREAD_MUTEX_FAST_NP!.
+
+!pthread_mutexattr_setkind_np! sets the mutex kind attribute in |attr|
+to the value specified by |kind|.
+
+!pthread_mutexattr_getkind_np! retrieves the current value of the
+mutex kind attribute in |attr| and stores it in the location pointed
+to by |kind|.
+
+.SH "RETURN VALUE"
+!pthread_mutexattr_init!, !pthread_mutexattr_destroy! and
+!pthread_mutexattr_getkind_np! always return 0.
+
+!pthread_mutexattr_setkind_np! returns 0 on success and a non-zero
+error code on error.
+
+.SH ERRORS
+
+On error, !pthread_mutexattr_setkind_np! returns the following error code:
+.TP
+!EINVAL!
+|kind| is neither !PTHREAD_MUTEX_FAST_NP! nor !PTHREAD_MUTEX_RECURSIVE_NP!
+nor !PTHREAD_MUTEX_ERRORCHECK_NP!
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_mutex_init!(3),
+!pthread_mutex_lock!(3),
+!pthread_mutex_unlock!(3).
diff --git a/linuxthreads/man/pthread_once.man b/linuxthreads/man/pthread_once.man
new file mode 100644 (file)
index 0000000..e9d117b
--- /dev/null
@@ -0,0 +1,34 @@
+.TH PTHREAD_ONCE 3 LinuxThreads
+
+.SH NAME
+pthread_once \- once-only initialization
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+pthread_once_t once_control = PTHREAD_ONCE_INIT;
+
+int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));
+
+.SH DESCRIPTION
+
+The purpose of !pthread_once! is to ensure that a piece of
+initialization code is executed at most once. The |once_control|
+argument points to a static or extern variable statically initialized
+to !PTHREAD_ONCE_INIT!.
+
+The first time !pthread_once! is called with a given |once_control|
+argument, it calls |init_routine| with no argument and changes the
+value of the |once_control| variable to record that initialization has
+been performed. Subsequent calls to !pthread_once! with the same
+!once_control! argument do nothing. 
+
+.SH "RETURN VALUE"
+!pthread_once! always returns 0.
+
+.SH ERRORS
+None.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
diff --git a/linuxthreads/man/pthread_self.man b/linuxthreads/man/pthread_self.man
new file mode 100644 (file)
index 0000000..3aa4a00
--- /dev/null
@@ -0,0 +1,23 @@
+.TH PTHREAD_SELF 3 LinuxThreads
+
+.SH NAME
+pthread_self \- return identifier of current thread
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+pthread_t pthread_self(void);
+
+.SH DESCRIPTION
+!pthread_self! return the thread identifier for the calling thread.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_equal!(3),
+!pthread_join!(3),
+!pthread_detach!(3),
+!pthread_setschedparam!(3),
+!pthread_getschedparam!(3).
+
diff --git a/linuxthreads/man/pthread_setschedparam.man b/linuxthreads/man/pthread_setschedparam.man
new file mode 100644 (file)
index 0000000..3992927
--- /dev/null
@@ -0,0 +1,79 @@
+.TH PTHREAD_SETSCHEDPARAM 3 LinuxThreads
+
+.XREF pthread_getschedparam
+
+.SH NAME
+pthread_setschedparam, pthread_getschedparam \- control thread scheduling parameters
+
+.SH SYNOPSIS
+#include <pthread.h>
+
+int pthread_setschedparam(pthread_t target_thread, int policy, const struct sched_param *param);
+
+int pthread_getschedparam(pthread_t target_thread, int *policy, struct sched_param *param);
+
+.SH DESCRIPTION
+
+!pthread_setschedparam! sets the scheduling parameters for the thread
+|target_thread| as indicated by |policy| and |param|. |policy| can be
+either !SCHED_OTHER! (regular, non-realtime scheduling), !SCHED_RR!
+(realtime, round-robin) or !SCHED_FIFO! (realtime, first-in
+first-out). |param| specifies the scheduling priority for the two
+realtime policies.  See !sched_setpolicy!(2) for more information on
+scheduling policies.
+
+The realtime scheduling policies !SCHED_RR! and !SCHED_FIFO! are
+available only to processes with superuser privileges.
+
+!pthread_getschedparam! retrieves the scheduling policy and scheduling
+parameters for the thread |target_thread| and store them in the
+locations pointed to by |policy| and |param|, respectively.
+
+.SH "RETURN VALUE"
+!pthread_setschedparam! and !pthread_getschedparam! return 0 on
+success and a non-zero error code on error.
+
+.SH ERRORS
+On error, !pthread_setschedparam! returns the following error codes:
+.RS
+.TP
+!EINVAL!
+|policy| is not one of !SCHED_OTHER!, !SCHED_RR!, !SCHED_FIFO!
+
+.TP
+!EINVAL!
+the priority value specified by |param| is not valid for the specified policy
+
+.TP
+!EPERM!
+the calling process does not have superuser permissions
+
+.TP
+!ESRCH!
+the |target_thread| is invalid or has already terminated
+
+.TP
+!EFAULT!
+|param| points outside the process memory space
+.RE
+
+On error, !pthread_getschedparam! returns the following error codes:
+.RS
+.TP
+!ESRCH!
+the |target_thread| is invalid or has already terminated
+
+.TP
+!EFAULT!
+|policy| or |param| point outside the process memory space
+.RE
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!sched_setscheduler!(2),
+!sched_getscheduler!(2),
+!sched_getparam!(2),
+!pthread_attr_setschedpolicy!(3),
+!pthread_attr_setschedparam!(3).
diff --git a/linuxthreads/man/pthread_sigmask.man b/linuxthreads/man/pthread_sigmask.man
new file mode 100644 (file)
index 0000000..784161d
--- /dev/null
@@ -0,0 +1,123 @@
+.TH PTHREAD_SIGNAL 3 LinuxThreads
+
+.XREF pthread_kill
+.XREF sigwait
+
+.SH NAME
+pthread_sigmask, pthread_kill, sigwait \- handling of signals in threads
+
+.SH SYNOPSIS
+#include <pthread.h>
+.br
+#include <signal.h>
+
+int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask);
+
+int pthread_kill(pthread_t thread, int signo);
+
+int sigwait(const sigset_t *set, int *sig);
+
+.SH DESCRIPTION
+
+!pthread_sigmask! changes the signal mask for the calling thread as
+described by the |how| and |newmask| arguments. If |oldmask| is not
+!NULL!, the previous signal mask is stored in the location pointed to
+by |oldmask|. 
+
+The meaning of the |how| and |newmask| arguments is the same as for
+!sigprocmask!(2). If |how| is !SIG_SETMASK!, the signal mask is set to
+|newmask|. If |how| is !SIG_BLOCK!, the signals specified to |newmask|
+are added to the current signal mask.  If |how| is !SIG_UNBLOCK!, the
+signals specified to |newmask| are removed from the current signal
+mask.
+
+Recall that signal masks are set on a per-thread basis, but signal
+actions and signal handlers, as set with !sigaction!(2), are shared
+between all threads.
+
+!pthread_kill! send signal number |signo| to the thread
+|thread|. The signal is delivered and handled as described in
+!kill!(2).
+
+!sigwait! suspends the calling thread until one of the signals
+in |set| is delivered to the calling thread. It then stores the number
+of the signal received in the location pointed to by |sig| and
+returns. The signals in |set| must be blocked and not ignored on
+entrance to !sigwait!. If the delivered signal has a signal handler
+function attached, that function is |not| called.
+
+.SH CANCELLATION
+
+!sigwait! is a cancellation point.
+
+.SH "RETURN VALUE"
+
+On success, 0 is returned. On failure, a non-zero error code is returned.
+
+.SH ERRORS
+
+The !pthread_sigmask! function returns the following error codes
+on error:
+.RS
+.TP
+!EINVAL!
+|how| is not one of !SIG_SETMASK!, !SIG_BLOCK!, or !SIG_UNBLOCK!
+
+.TP
+!EFAULT!
+|newmask| or |oldmask| point to invalid addresses
+.RE
+
+The !pthread_kill! function returns the following error codes
+on error:
+.RS
+.TP
+!EINVAL!
+|signo| is not a valid signal number
+
+.TP
+!ESRCH!
+the thread |thread| does not exist (e.g. it has already terminated)
+.RE
+
+The !sigwait! function never returns an error.
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!sigprocmask!(2),
+!kill!(2),
+!sigaction!(2),
+!sigsuspend!(2).
+
+.SH NOTES
+
+For !sigwait! to work reliably, the signals being waited for must be
+blocked in all threads, not only in the calling thread, since
+otherwise the POSIX semantics for signal delivery do not guarantee
+that it's the thread doing the !sigwait! that will receive the signal.
+The best way to achieve this is block those signals before any threads
+are created, and never unblock them in the program other than by
+calling !sigwait!.
+
+.SH BUGS
+
+Signal handling in LinuxThreads departs significantly from the POSIX
+standard. According to the standard, ``asynchronous'' (external)
+signals are addressed to the whole process (the collection of all
+threads), which then delivers them to one particular thread. The
+thread that actually receives the signal is any thread that does
+not currently block the signal.
+
+In LinuxThreads, each thread is actually a kernel process with its own
+PID, so external signals are always directed to one particular thread.
+If, for instance, another thread is blocked in !sigwait! on that
+signal, it will not be restarted.
+
+The LinuxThreads implementation of !sigwait! installs dummy signal
+handlers for the signals in |set| for the duration of the wait. Since
+signal handlers are shared between all threads, other threads must not
+attach their own signal handlers to these signals, or alternatively
+they should all block these signals (which is recommended anyway --
+see the Notes section).
diff --git a/linuxthreads/man/sem_init.man b/linuxthreads/man/sem_init.man
new file mode 100644 (file)
index 0000000..e3a1a63
--- /dev/null
@@ -0,0 +1,132 @@
+.TH SEMAPHORES 3 LinuxThreads
+
+.XREF sem_wait
+.XREF sem_trywait
+.XREF sem_post
+.XREF sem_getvalue
+.XREF sem_destroy
+
+.SH NAME
+sem_init, sem_wait, sem_trywait, sem_post, sem_getvalue, sem_destroy \- operations on semaphores
+
+.SH SYNOPSIS
+#include <semaphore.h>
+
+int sem_init(sem_t *sem, int pshared, unsigned int value);
+
+int sem_wait(sem_t * sem);
+
+int sem_trywait(sem_t * sem);
+
+int sem_post(sem_t * sem);
+
+int sem_getvalue(sem_t * sem, int * sval);
+
+int sem_destroy(sem_t * sem);
+
+.SH DESCRIPTION
+This manual page documents POSIX 1003.1b semaphores, not to be
+confused with SystemV semaphores as described in !ipc!(5), !semctl!(2)
+and !semop!(2).
+
+Semaphores are counters for resources shared between threads. The
+basic operations on semaphores are: increment the counter atomically,
+and wait until the counter is non-null and decrement it atomically.
+
+!sem_init! initializes the semaphore object pointed to by |sem|. The
+count associated with the semaphore is set initially to |value|. The
+|pshared| argument indicates whether the semaphore is local to the
+current process (|pshared| is zero) or is to be shared between several
+processes (|pshared| is not zero). LinuxThreads currently does not
+support process-shared semaphores, thus !sem_init! always returns with
+error !ENOSYS! if |pshared| is not zero.
+
+!sem_wait! suspends the calling thread until the semaphore pointed to
+by |sem| has non-zero count. It then atomically decreases the
+semaphore count.
+
+!sem_trywait! is a non-blocking variant of !sem_wait!. If the
+semaphore pointed to by |sem| has non-zero count, the count is
+atomically decreased and !sem_trywait! immediately returns 0.
+If the semaphore count is zero, !sem_trywait! immediately returns with
+error !EAGAIN!.
+
+!sem_post! atomically increases the count of the semaphore pointed to
+by |sem|. This function never blocks and can safely be used in
+asynchronous signal handlers.
+
+!sem_getvalue! stores in the location pointed to by |sval| the current
+count of the semaphore |sem|.
+
+!sem_destroy! destroys a semaphore object, freeing the resources it
+might hold. No threads should be waiting on the semaphore at the time
+!sem_destroy! is called. In the LinuxThreads implementation, no
+resources are associated with semaphore objects, thus !sem_destroy!
+actually does nothing except checking that no thread is waiting on the
+semaphore.
+
+.SH CANCELLATION
+
+!sem_wait! is a cancellation point.
+
+.SH "ASYNC-SIGNAL SAFETY"
+
+On processors supporting atomic compare-and-swap (Intel 486, Pentium
+and later, Alpha, PowerPC, MIPS II, Motorola 68k), the !sem_post!
+function is async-signal safe and can therefore be
+called from signal handlers. This is the only thread synchronization
+function provided by POSIX threads that is async-signal safe.
+
+On the Intel 386 and the Sparc, the current LinuxThreads
+implementation of !sem_post! is not async-signal safe by lack of the
+required atomic operations.
+
+.SH "RETURN VALUE"
+
+The !sem_wait! and !sem_getvalue! functions always return 0.
+All other semaphore functions return 0 on success and -1 on error, in
+addition to writing an error code in !errno!.
+
+.SH ERRORS
+
+The !sem_init! function sets !errno! to the following codes on error:
+.RS
+.TP
+!EINVAL!
+|value| exceeds the maximal counter value !SEM_VALUE_MAX!
+.TP
+!ENOSYS!
+|pshared| is not zero
+.RE
+
+The !sem_trywait! function sets !errno! to the following error code on error:
+.RS
+.TP
+!EAGAIN!
+the semaphore count is currently 0
+.RE
+
+The !sem_post! function sets !errno! to the following error code on error:
+.RS
+.TP
+!ERANGE!
+after incrementation, the semaphore value would exceed !SEM_VALUE_MAX!
+(the semaphore count is left unchanged in this case)
+.RE
+
+The !sem_destroy! function sets !errno! to the following error code on error:
+.RS
+.TP
+!EBUSY!
+some threads are currently blocked waiting on the semaphore.
+.RE
+
+.SH AUTHOR
+Xavier Leroy <Xavier.Leroy@inria.fr>
+
+.SH "SEE ALSO"
+!pthread_mutex_init!(3),
+!pthread_cond_init!(3),
+!pthread_cancel!(3),
+!ipc!(5).
+
diff --git a/linuxthreads/man/troffprepro b/linuxthreads/man/troffprepro
new file mode 100755 (executable)
index 0000000..ba564fe
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/local/bin/perl
+
+$insynopsis = 0;
+
+open(INPUT, $ARGV[0]) || die("cannot open $ARGV[0]");
+open(OUTPUT, "> $ARGV[1]") || die("cannot create $ARGV[1]");
+
+select(OUTPUT);
+
+line:
+while(<INPUT>) {
+  if (/^\.XREF (.*)$/) {
+    $xref = $1;
+    $_ = $ARGV[1];
+    m/^.*\.(([1-8]).*)$/;
+    $suffix = $1;
+    $extension = $2;
+    open(XREF, "> $xref.$suffix");
+    print XREF ".so man$extension/$ARGV[1]\n";
+    close(XREF);
+    next line;
+  }
+  if (/^\.SH/) {
+    $insynopsis = /SYNOPSIS/;
+    print $_;
+    next;
+  }
+  if ($insynopsis) {
+    if (/^#/) {
+      print ".B ", $_;
+    }
+    elsif (/^[a-z]/) {    
+      chop;
+#      if (m/^([a-zA-Z][a-zA-Z0-9_]*\s+[a-zA-Z][a-zA-Z0-9_]*)\(/) {
+#          print ".B \"", $1, "\"\n";
+#          $_ = '(' . $';
+#      }
+#      s/([a-zA-Z][a-zA-Z0-9_]*)(\s*[,()=])/" \1 "\2/g;
+      s/([ *])([a-zA-Z][a-zA-Z0-9_]*)(\s*[,)=])/\1" \2 "\3/g;
+      print ".BI \"", $_, "\"\n";
+    }
+    else {
+      print $_;
+    }
+    next;
+  }
+  chop;
+  s/!([^!]+)!\|([^|]+)\|([^\s]*)\s*/\n.BI "\1" "\2\3"\n/g;
+  s/([!|])([^!|]+)\1([^\s]*)\s*/do subst($1,$2,$3)/eg;
+  s/^\n+//;
+  s/\n+$//;
+  s/\n\n+/\n/g;
+  print $_, "\n";
+}
+
+close(INPUT);
+close(OUTPUT);
+
+sub subst {
+  local ($a, $b, $c) = @_;
+  if ($c) {
+    "\n" . ($a eq "!" ? ".BR " : ".IR ") . "\"$b\" $c\n"
+  } else {
+    "\n" . ($a eq "!" ? ".B " : ".I ") . "\"$b\"\n"
+  }
+}
+
+  
diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c
new file mode 100644 (file)
index 0000000..325955d
--- /dev/null
@@ -0,0 +1,400 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* The "thread manager" thread: manages creation and termination of threads */
+
+#include <errno.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/select.h>                /* for select */
+#include <sys/mman.h>           /* for mmap */
+#include <sys/time.h>
+#include <sys/wait.h>           /* for waitpid macros */
+#include <linux/tasks.h>
+
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+
+/* Array of active threads. Entry 0 is reserved for the initial thread. */
+
+struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] =
+{ { 0, &__pthread_initial_thread}, /* All NULLs */ };
+
+/* Mapping from stack segment to thread descriptor. */
+/* Stack segment numbers are also indices into the __pthread_handles array. */
+/* Stack segment number 0 is reserved for the initial thread. */
+
+static inline pthread_descr thread_segment(int seg)
+{
+  return (pthread_descr)(THREAD_STACK_START_ADDRESS - (seg - 1) * STACK_SIZE)
+         - 1;
+}
+
+/* Flag set in signal handler to record child termination */
+
+static volatile int terminated_children = 0;
+
+/* Flag set when the initial thread is blocked on pthread_exit waiting
+   for all other threads to terminate */
+
+static int main_thread_exiting = 0;
+
+/* Counter used to generate unique thread identifier.
+   Thread identifier is pthread_threads_counter + segment. */
+
+static pthread_t pthread_threads_counter = 0;
+
+/* Forward declarations */
+
+static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
+                                 void * (*start_routine)(void *), void *arg,
+                                 sigset_t *mask, int father_pid);
+static void pthread_handle_free(pthread_descr th);
+static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode);
+static void pthread_reap_children(void);
+static void pthread_kill_all_threads(int sig, int main_thread_also);
+
+/* The server thread managing requests for thread creation and termination */
+
+int __pthread_manager(void *arg)
+{
+  int reqfd = (int)arg;
+  sigset_t mask;
+  fd_set readfds;
+  struct timeval timeout;
+  int n;
+  struct pthread_request request;
+
+  /* If we have special thread_self processing, initialize it.  */
+#ifdef INIT_THREAD_SELF
+  INIT_THREAD_SELF(&__pthread_manager_thread);
+#endif
+  /* Set the error variable.  */
+  __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno;
+  __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno;
+  /* Block all signals except PTHREAD_SIG_RESTART */
+  sigfillset(&mask);
+  sigdelset(&mask, PTHREAD_SIG_RESTART);
+  sigprocmask(SIG_SETMASK, &mask, NULL);
+  /* Enter server loop */
+  while(1) {
+    FD_ZERO(&readfds);
+    FD_SET(reqfd, &readfds);
+    timeout.tv_sec = 2;
+    timeout.tv_usec = 0;
+    n = __select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
+    /* Check for termination of the main thread */
+    if (getppid() == 1) {
+      pthread_kill_all_threads(SIGKILL, 0);
+      _exit(0);
+    }
+    /* Check for dead children */
+    if (terminated_children) {
+      terminated_children = 0;
+      pthread_reap_children();
+    }
+    /* Read and execute request */
+    if (n == 1 && FD_ISSET(reqfd, &readfds)) {
+      n = __libc_read(reqfd, (char *)&request, sizeof(request));
+      ASSERT(n == sizeof(request));
+      switch(request.req_kind) {
+      case REQ_CREATE:
+        request.req_thread->p_retcode =
+          pthread_handle_create((pthread_t *) &request.req_thread->p_retval,
+                                request.req_args.create.attr,
+                                request.req_args.create.fn,
+                                request.req_args.create.arg,
+                                &request.req_args.create.mask,
+                                request.req_thread->p_pid);
+        restart(request.req_thread);
+        break;
+      case REQ_FREE:
+        pthread_handle_free(request.req_args.free.thread);
+        break;
+      case REQ_PROCESS_EXIT:
+        pthread_handle_exit(request.req_thread,
+                            request.req_args.exit.code);
+        break;
+      case REQ_MAIN_THREAD_EXIT:
+        main_thread_exiting = 1;
+        if (__pthread_main_thread->p_nextlive == __pthread_main_thread) {
+          restart(__pthread_main_thread);
+          return 0;
+        }
+        break;
+      }
+    }
+  }
+}
+
+/* Process creation */
+
+static int pthread_start_thread(void *arg)
+{
+  pthread_descr self = (pthread_descr) arg;
+  void * outcome;
+  /* Initialize special thread_self processing, if any.  */
+#ifdef INIT_THREAD_SELF
+  INIT_THREAD_SELF(self);
+#endif
+  /* Make sure our pid field is initialized, just in case we get there
+     before our father has initialized it. */
+  self->p_pid = __getpid();
+  /* Initial signal mask is that of the creating thread. (Otherwise,
+     we'd just inherit the mask of the thread manager.) */
+  sigprocmask(SIG_SETMASK, &self->p_start_args.mask, NULL);
+  /* Set the scheduling policy and priority for the new thread, if needed */
+  if (self->p_start_args.schedpolicy >= 0)
+    __sched_setscheduler(self->p_pid, self->p_start_args.schedpolicy,
+                         &self->p_start_args.schedparam);
+  /* Run the thread code */
+  outcome = self->p_start_args.start_routine(self->p_start_args.arg);
+  /* Exit with the given return value */
+  pthread_exit(outcome);
+  return 0;
+}
+
+static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
+                                 void * (*start_routine)(void *), void *arg,
+                                 sigset_t * mask, int father_pid)
+{
+  size_t sseg;
+  int pid;
+  pthread_descr new_thread;
+  pthread_t new_thread_id;
+  int i;
+
+  /* Find a free stack segment for the current stack */
+  for (sseg = 1; ; sseg++) {
+    if (sseg >= PTHREAD_THREADS_MAX) return EAGAIN;
+    if (__pthread_handles[sseg].h_descr != NULL) continue;
+    new_thread = thread_segment(sseg);
+    /* Allocate space for stack and thread descriptor. */
+    if (mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
+            INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+            MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0)
+        != MAP_FAILED) break;
+    /* It seems part of this segment is already mapped. Try the next. */
+  }
+  /* Allocate new thread identifier */
+  pthread_threads_counter += PTHREAD_THREADS_MAX;
+  new_thread_id = sseg + pthread_threads_counter;
+  /* Initialize the thread descriptor */
+  new_thread->p_nextwaiting = NULL;
+  new_thread->p_tid = new_thread_id;
+  new_thread->p_priority = 0;
+  new_thread->p_spinlock = &(__pthread_handles[sseg].h_spinlock);
+  new_thread->p_signal = 0;
+  new_thread->p_signal_jmp = NULL;
+  new_thread->p_cancel_jmp = NULL;
+  new_thread->p_terminated = 0;
+  new_thread->p_detached = attr == NULL ? 0 : attr->detachstate;
+  new_thread->p_exited = 0;
+  new_thread->p_retval = NULL;
+  new_thread->p_joining = NULL;
+  new_thread->p_cleanup = NULL;
+  new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE;
+  new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED;
+  new_thread->p_canceled = 0;
+  new_thread->p_errnop = &new_thread->p_errno;
+  new_thread->p_errno = 0;
+  new_thread->p_h_errnop = &new_thread->p_h_errno;
+  new_thread->p_h_errno = 0;
+  for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
+    new_thread->p_specific[i] = NULL;
+  /* Initialize the thread handle */
+  __pthread_handles[sseg].h_spinlock = 0; /* should already be 0 */
+  __pthread_handles[sseg].h_descr = new_thread;
+  /* Determine scheduling parameters for the thread */
+  new_thread->p_start_args.schedpolicy = -1;
+  if (attr != NULL) {
+    switch(attr->inheritsched) {
+    case PTHREAD_EXPLICIT_SCHED:
+      new_thread->p_start_args.schedpolicy = attr->schedpolicy;
+      new_thread->p_start_args.schedparam = attr->schedparam;
+      break;
+    case PTHREAD_INHERIT_SCHED:
+      /* schedpolicy doesn't need to be set, only get priority */
+      __sched_getparam(father_pid, &new_thread->p_start_args.schedparam);
+      break;
+    }
+    new_thread->p_priority =
+      new_thread->p_start_args.schedparam.sched_priority;
+  }
+  /* Finish setting up arguments to pthread_start_thread */
+  new_thread->p_start_args.start_routine = start_routine;
+  new_thread->p_start_args.arg = arg;
+  new_thread->p_start_args.mask = *mask;
+  /* Do the cloning */
+  pid = __clone(pthread_start_thread, (void **) new_thread,
+                CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
+                PTHREAD_SIG_RESTART,
+                new_thread);
+  /* Check if cloning succeeded */
+  if (pid == -1) {
+    /* Free the stack */
+    munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
+           INITIAL_STACK_SIZE);
+    __pthread_handles[sseg].h_descr = NULL;
+    return errno;
+  }
+  /* Insert new thread in doubly linked list of active threads */
+  new_thread->p_prevlive = __pthread_main_thread;
+  new_thread->p_nextlive = __pthread_main_thread->p_nextlive;
+  __pthread_main_thread->p_nextlive->p_prevlive = new_thread;
+  __pthread_main_thread->p_nextlive = new_thread;
+  /* Set pid field of the new thread, in case we get there before the
+     child starts. */
+  new_thread->p_pid = pid;
+  /* We're all set */
+  *thread = new_thread_id;
+  return 0;
+}
+
+/* Free the resources of a thread. */
+
+static void pthread_free(pthread_descr th)
+{
+  pthread_handle handle;
+  ASSERT(th->p_exited);
+  /* Make the handle invalid */
+  handle =  thread_handle(th->p_tid);
+  acquire(&handle->h_spinlock);
+  handle->h_descr = NULL;
+  release(&handle->h_spinlock);
+  /* If initial thread, nothing to free */
+  if (th == &__pthread_initial_thread) return;
+  /* Free the stack and thread descriptor area */
+  munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE);
+}
+
+/* Handle threads that have exited */
+
+static void pthread_exited(pid_t pid)
+{
+  pthread_descr th;
+  int detached;
+  /* Find thread with that pid */
+  for (th = __pthread_main_thread->p_nextlive;
+       th != __pthread_main_thread;
+       th = th->p_nextlive) {
+    if (th->p_pid == pid) {
+      /* Remove thread from list of active threads */
+      th->p_nextlive->p_prevlive = th->p_prevlive;
+      th->p_prevlive->p_nextlive = th->p_nextlive;
+      /* Mark thread as exited, and if detached, free its resources */
+      acquire(th->p_spinlock);
+      th->p_exited = 1;
+      detached = th->p_detached;
+      release(th->p_spinlock);
+      if (detached) pthread_free(th);
+      break;
+    }
+  }
+  /* If all threads have exited and the main thread is pending on a
+     pthread_exit, wake up the main thread and terminate ourselves. */
+  if (main_thread_exiting &&
+      __pthread_main_thread->p_nextlive == __pthread_main_thread) {
+    restart(__pthread_main_thread);
+    _exit(0);
+  }
+}
+
+static void pthread_reap_children(void)
+{
+  pid_t pid;
+  int status;
+
+  while ((pid = __libc_waitpid(-1, &status, WNOHANG | __WCLONE)) > 0) {
+    pthread_exited(pid);
+    if (WIFSIGNALED(status)) {
+      /* If a thread died due to a signal, send the same signal to
+         all other threads, including the main thread. */
+      pthread_kill_all_threads(WTERMSIG(status), 1);
+      _exit(0);
+    }
+  }
+}
+
+/* Free the resources of a thread */
+
+static void pthread_handle_free(pthread_descr th)
+{
+  acquire(th->p_spinlock);
+  if (th->p_exited) {
+    release(th->p_spinlock);
+    pthread_free(th);
+  } else {
+    /* The Unix process of the thread is still running.
+       Mark the thread as detached so that the thread manager will
+       deallocate its resources when the Unix process exits. */
+    th->p_detached = 1;
+    release(th->p_spinlock);
+  }
+}
+
+/* Send a signal to all running threads */
+
+static void pthread_kill_all_threads(int sig, int main_thread_also)
+{
+  pthread_descr th;
+  for (th = __pthread_main_thread->p_nextlive;
+       th != __pthread_main_thread;
+       th = th->p_nextlive) {
+    kill(th->p_pid, sig);
+  }
+  if (main_thread_also) {
+    kill(__pthread_main_thread->p_pid, sig);
+  }
+}
+
+/* Process-wide exit() */
+
+static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode)
+{
+  pthread_descr th;
+  __pthread_exit_requested = 1;
+  __pthread_exit_code = exitcode;
+  /* Send the CANCEL signal to all running threads, including the main
+     thread, but excluding the thread from which the exit request originated
+     (that thread must complete the exit, e.g. calling atexit functions
+     and flushing stdio buffers). */
+  for (th = issuing_thread->p_nextlive;
+       th != issuing_thread;
+       th = th->p_nextlive) {
+    kill(th->p_pid, PTHREAD_SIG_CANCEL);
+  }
+  /* Now, wait for all these threads, so that they don't become zombies
+     and their times are properly added to the thread manager's times. */
+  for (th = issuing_thread->p_nextlive;
+       th != issuing_thread;
+       th = th->p_nextlive) {
+    waitpid(th->p_pid, NULL, __WCLONE);
+  }
+  restart(issuing_thread);
+  _exit(0);
+}
+
+/* Handler for PTHREAD_SIG_RESTART in thread manager thread */
+
+void __pthread_manager_sighandler(int sig)
+{
+  terminated_children = 1;
+}
diff --git a/linuxthreads/mutex.c b/linuxthreads/mutex.c
new file mode 100644 (file)
index 0000000..d4ebcb8
--- /dev/null
@@ -0,0 +1,234 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Mutexes */
+
+#include <errno.h>
+#include <sched.h>
+#include <stddef.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "queue.h"
+#include "restart.h"
+
+int __pthread_mutex_init(pthread_mutex_t * mutex,
+                       const pthread_mutexattr_t * mutex_attr)
+{
+  mutex->m_spinlock = 0;
+  mutex->m_count = 0;
+  mutex->m_owner = NULL;
+  mutex->m_kind =
+    mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->mutexkind;
+  queue_init(&mutex->m_waiting);
+  return 0;
+}
+weak_alias (__pthread_mutex_init, pthread_mutex_init)
+
+int __pthread_mutex_destroy(pthread_mutex_t * mutex)
+{
+  int count;
+  acquire(&mutex->m_spinlock);
+  count = mutex->m_count;
+  release(&mutex->m_spinlock);
+  if (count > 0) return EBUSY;
+  return 0;
+}
+weak_alias (__pthread_mutex_destroy, pthread_mutex_destroy)
+
+int __pthread_mutex_trylock(pthread_mutex_t * mutex)
+{
+  pthread_descr self;
+
+  acquire(&mutex->m_spinlock);
+  switch(mutex->m_kind) {
+  case PTHREAD_MUTEX_FAST_NP:
+    if (mutex->m_count == 0) {
+      mutex->m_count = 1;
+      release(&mutex->m_spinlock);
+      return 0;
+    }
+    break;
+  case PTHREAD_MUTEX_RECURSIVE_NP:
+    self = thread_self();
+    if (mutex->m_count == 0 || mutex->m_owner == self) {
+      mutex->m_count++;
+      mutex->m_owner = self;
+      release(&mutex->m_spinlock);
+      return 0;
+    }
+    break;
+  case PTHREAD_MUTEX_ERRORCHECK_NP:
+    self = thread_self();
+    if (mutex->m_count == 0) {
+      mutex->m_count = 1;
+      mutex->m_owner = self;
+      release(&mutex->m_spinlock);
+      return 0;
+    }
+    break;
+  default:
+    release(&mutex->m_spinlock);
+    return EINVAL;
+  }
+  release(&mutex->m_spinlock);
+  return EBUSY;
+}
+weak_alias (__pthread_mutex_trylock, pthread_mutex_trylock)
+
+int __pthread_mutex_lock(pthread_mutex_t * mutex)
+{
+  pthread_descr self;
+
+  while(1) {
+    acquire(&mutex->m_spinlock);
+    switch(mutex->m_kind) {
+    case PTHREAD_MUTEX_FAST_NP:
+      if (mutex->m_count == 0) {
+        mutex->m_count = 1;
+        release(&mutex->m_spinlock);
+        return 0;
+      }
+      self = thread_self();
+      break;
+    case PTHREAD_MUTEX_RECURSIVE_NP:
+      self = thread_self();
+      if (mutex->m_count == 0 || mutex->m_owner == self) {
+        mutex->m_count++;
+        mutex->m_owner = self;
+        release(&mutex->m_spinlock);
+        return 0;
+      }
+      break;
+    case PTHREAD_MUTEX_ERRORCHECK_NP:
+      self = thread_self();
+      if (mutex->m_count == 0) {
+       mutex->m_count = 1;
+       mutex->m_owner = self;
+       release(&mutex->m_spinlock);
+       return 0;
+      } else if (mutex->m_owner == self) {
+       release(&mutex->m_spinlock);
+       return EDEADLK;
+      }
+      break;
+    default:
+      release(&mutex->m_spinlock);
+      return EINVAL;
+    }
+    /* Suspend ourselves, then try again */
+    enqueue(&mutex->m_waiting, self);
+    release(&mutex->m_spinlock);
+    suspend(self); /* This is not a cancellation point */
+  }
+}
+weak_alias (__pthread_mutex_lock, pthread_mutex_lock)
+
+int __pthread_mutex_unlock(pthread_mutex_t * mutex)
+{
+  pthread_descr th;
+
+  acquire(&mutex->m_spinlock);
+  switch (mutex->m_kind) {
+  case PTHREAD_MUTEX_FAST_NP:
+    mutex->m_count = 0;
+    break;
+  case PTHREAD_MUTEX_RECURSIVE_NP:
+    mutex->m_count--;
+    if (mutex->m_count > 0) {
+      release(&mutex->m_spinlock);
+      return 0;
+    }
+    mutex->m_count = 0; /* so that excess unlocks do not break everything */
+    break;
+  case PTHREAD_MUTEX_ERRORCHECK_NP:
+    if (mutex->m_count == 0 || mutex->m_owner != thread_self()) {
+      release(&mutex->m_spinlock);
+      return EPERM;
+    }
+    mutex->m_count = 0;
+    break;
+  default:
+    release(&mutex->m_spinlock);
+    return EINVAL;
+  }
+  th = dequeue(&mutex->m_waiting);
+  release(&mutex->m_spinlock);
+  if (th != NULL) restart(th);
+  return 0;
+}
+weak_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
+
+int __pthread_mutexattr_init(pthread_mutexattr_t *attr)
+{
+  attr->mutexkind = PTHREAD_MUTEX_FAST_NP;
+  return 0;
+}
+weak_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
+
+int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
+{
+  return 0;
+}
+weak_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
+
+int __pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind)
+{
+  if (kind != PTHREAD_MUTEX_FAST_NP
+      && kind != PTHREAD_MUTEX_RECURSIVE_NP
+      && kind != PTHREAD_MUTEX_ERRORCHECK_NP)
+    return EINVAL;
+  attr->mutexkind = kind;
+  return 0;
+}
+weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
+
+int __pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind)
+{
+  *kind = attr->mutexkind;
+  return 0;
+}
+weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np)
+
+/* Once-only execution */
+
+static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER;
+
+enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 };
+
+int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void))
+{
+  /* Test without locking first for speed */
+  if (*once_control == DONE) return 0;
+  /* Lock and test again */
+  pthread_mutex_lock(&once_masterlock);
+  /* If init_routine is being called from another routine, wait until
+     it completes. */
+  while (*once_control == IN_PROGRESS) {
+    pthread_cond_wait(&once_finished, &once_masterlock);
+  }
+  /* Here *once_control is stable and either NEVER or DONE. */
+  if (*once_control == NEVER) {
+    *once_control = IN_PROGRESS;
+    pthread_mutex_unlock(&once_masterlock);
+    init_routine();
+    pthread_mutex_lock(&once_masterlock);
+    *once_control = DONE;
+    pthread_cond_broadcast(&once_finished);
+  }
+  pthread_mutex_unlock(&once_masterlock);
+  return 0;
+}
+weak_alias (__pthread_once, pthread_once)
diff --git a/linuxthreads/ptfork.c b/linuxthreads/ptfork.c
new file mode 100644 (file)
index 0000000..8a7b797
--- /dev/null
@@ -0,0 +1,97 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* The "atfork" stuff */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pthread.h"
+#include "internals.h"
+
+struct handler_list {
+  void (*handler)(void);
+  struct handler_list * next;
+};
+
+static pthread_mutex_t pthread_atfork_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct handler_list * pthread_atfork_prepare = NULL;
+static struct handler_list * pthread_atfork_parent = NULL;
+static struct handler_list * pthread_atfork_child = NULL;
+
+static void pthread_insert_list(struct handler_list ** list,
+                                void (*handler)(void),
+                                struct handler_list * newlist,
+                                int at_end)
+{
+  if (handler == NULL) return;
+  if (at_end) {
+    while(*list != NULL) list = &((*list)->next);
+  }
+  newlist->handler = handler;
+  newlist->next = *list;
+  *list = newlist;
+}
+
+struct handler_list_block {
+  struct handler_list prepare, parent, child;
+};
+
+int __pthread_atfork(void (*prepare)(void),
+                    void (*parent)(void),
+                    void (*child)(void))
+{
+  struct handler_list_block * block =
+    (struct handler_list_block *) malloc(sizeof(struct handler_list_block));
+  if (block == NULL) return ENOMEM;
+  pthread_mutex_lock(&pthread_atfork_lock);
+  /* "prepare" handlers are called in LIFO */
+  pthread_insert_list(&pthread_atfork_prepare, prepare, &block->prepare, 0);
+  /* "parent" handlers are called in FIFO */
+  pthread_insert_list(&pthread_atfork_parent, parent, &block->parent, 1);
+  /* "child" handlers are called in FIFO */
+  pthread_insert_list(&pthread_atfork_child, child, &block->child, 1);
+  pthread_mutex_unlock(&pthread_atfork_lock);
+  return 0;
+}
+weak_alias (__pthread_atfork, pthread_atfork)
+
+static inline void pthread_call_handlers(struct handler_list * list)
+{
+  for (/*nothing*/; list != NULL; list = list->next) (list->handler)();
+}
+
+extern int __fork(void);
+
+int fork(void)
+{
+  int pid;
+  struct handler_list * prepare, * child, * parent;
+
+  pthread_mutex_lock(&pthread_atfork_lock);
+  prepare = pthread_atfork_prepare;
+  child = pthread_atfork_child;
+  parent = pthread_atfork_parent;
+  pthread_mutex_unlock(&pthread_atfork_lock);
+  pthread_call_handlers(prepare);
+  pid = __fork();
+  if (pid == 0) {
+    __pthread_reset_main_thread();
+    __fresetlockfiles();
+    pthread_call_handlers(child);
+  } else {
+    pthread_call_handlers(parent);
+  }
+  return pid;
+}
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
new file mode 100644 (file)
index 0000000..994233e
--- /dev/null
@@ -0,0 +1,445 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Thread creation, initialization, and basic low-level routines */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+
+/* Descriptor of the initial thread */
+
+struct _pthread_descr_struct __pthread_initial_thread = {
+  &__pthread_initial_thread,  /* pthread_descr p_nextlive */
+  &__pthread_initial_thread,  /* pthread_descr p_prevlive */
+  NULL,                       /* pthread_descr p_nextwaiting */
+  PTHREAD_THREADS_MAX,        /* pthread_t p_tid */
+  0,                          /* int p_pid */
+  0,                          /* int p_priority */
+  &__pthread_handles[0].h_spinlock, /* int * p_spinlock */
+  0,                          /* int p_signal */
+  NULL,                       /* sigjmp_buf * p_signal_buf */
+  NULL,                       /* sigjmp_buf * p_cancel_buf */
+  0,                          /* char p_terminated */
+  0,                          /* char p_detached */
+  0,                          /* char p_exited */
+  NULL,                       /* void * p_retval */
+  0,                          /* int p_retval */
+  NULL,                       /* pthread_descr p_joining */
+  NULL,                       /* struct _pthread_cleanup_buffer * p_cleanup */
+  0,                          /* char p_cancelstate */
+  0,                          /* char p_canceltype */
+  0,                          /* char p_canceled */
+  NULL,                       /* int *p_errnop */
+  0,                          /* int p_errno */
+  NULL,                       /* int *p_h_errnop */
+  0,                          /* int p_h_errno */
+  PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
+  {NULL}                      /* void * p_specific[PTHREAD_KEYS_MAX] */
+};
+
+/* Descriptor of the manager thread; none of this is used but the error
+   variables and the address for identification.  */
+
+struct _pthread_descr_struct __pthread_manager_thread = {
+  NULL,                       /* pthread_descr p_nextlive */
+  NULL,                       /* pthread_descr p_prevlive */
+  NULL,                       /* pthread_descr p_nextwaiting */
+  0,                          /* int p_tid */
+  0,                          /* int p_pid */
+  0,                          /* int p_priority */
+  NULL,                       /* int * p_spinlock */
+  0,                          /* int p_signal */
+  NULL,                       /* sigjmp_buf * p_signal_buf */
+  NULL,                       /* sigjmp_buf * p_cancel_buf */
+  0,                          /* char p_terminated */
+  0,                          /* char p_detached */
+  0,                          /* char p_exited */
+  NULL,                       /* void * p_retval */
+  0,                          /* int p_retval */
+  NULL,                       /* pthread_descr p_joining */
+  NULL,                       /* struct _pthread_cleanup_buffer * p_cleanup */
+  0,                          /* char p_cancelstate */
+  0,                          /* char p_canceltype */
+  0,                          /* char p_canceled */
+  NULL,                       /* int *p_errnop */
+  0,                          /* int p_errno */
+  NULL,                       /* int *p_h_errnop */
+  0,                          /* int p_h_errno */
+  PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
+  {NULL}                      /* void * p_specific[PTHREAD_KEYS_MAX] */
+};
+
+/* Pointer to the main thread (the father of the thread manager thread) */
+/* Originally, this is the initial thread, but this changes after fork() */
+
+pthread_descr __pthread_main_thread = &__pthread_initial_thread;
+
+/* Limit between the stack of the initial thread (above) and the
+   stacks of other threads (below). Aligned on a STACK_SIZE boundary. */
+
+char *__pthread_initial_thread_bos = NULL;
+
+/* File descriptor for sending requests to the thread manager. */
+/* Initially -1, meaning that the thread manager is not running. */
+
+int __pthread_manager_request = -1;
+
+/* Other end of the pipe for sending requests to the thread manager. */
+
+int __pthread_manager_reader;
+
+/* PID of thread manager */
+
+static int __pthread_manager_pid;
+
+/* Limits of the thread manager stack */
+
+char *__pthread_manager_thread_bos = NULL;
+char *__pthread_manager_thread_tos = NULL;
+
+/* For process-wide exit() */
+
+int __pthread_exit_requested = 0;
+int __pthread_exit_code = 0;
+
+/* Signal numbers used for the communication.  */
+int __pthread_sig_restart;
+int __pthread_sig_cancel;
+
+/* These variables are used by the setup code.  */
+extern int _errno;
+extern int _h_errno;
+
+/* Forward declarations */
+
+static void pthread_exit_process(int retcode, void *arg);
+static void pthread_handle_sigcancel(int sig);
+
+/* Initialize the pthread library.
+   Initialization is split in two functions:
+   - a constructor function that blocks the PTHREAD_SIG_RESTART signal
+     (must do this very early, since the program could capture the signal
+      mask with e.g. sigsetjmp before creating the first thread);
+   - a regular function called from pthread_create when needed. */
+
+static void pthread_initialize(void) __attribute__((constructor));
+
+static void pthread_initialize(void)
+{
+  struct sigaction sa;
+  sigset_t mask;
+
+  /* If already done (e.g. by a constructor called earlier!), bail out */
+  if (__pthread_initial_thread_bos != NULL) return;
+  /* For the initial stack, reserve at least STACK_SIZE bytes of stack
+     below the current stack address, and align that on a
+     STACK_SIZE boundary. */
+  __pthread_initial_thread_bos =
+    (char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));
+  /* Update the descriptor for the initial thread. */
+  __pthread_initial_thread.p_pid = __getpid();
+  /* If we have special thread_self processing, initialize that for the
+     main thread now.  */
+#ifdef INIT_THREAD_SELF
+  INIT_THREAD_SELF(&__pthread_initial_thread);
+#endif
+  /* The errno/h_errno variable of the main thread are the global ones.  */
+  __pthread_initial_thread.p_errnop = &_errno;
+  __pthread_initial_thread.p_h_errnop = &_h_errno;
+  /* Allocate the signals used.  */
+  __pthread_sig_restart = __libc_allocate_rtsig (1);
+  __pthread_sig_cancel = __libc_allocate_rtsig (1);
+  if (__pthread_sig_restart < 0 || __pthread_sig_cancel < 0)
+    {
+      /* The kernel does not support real-time signals.  Use as before
+        the available signals in the fixed set.  */
+      __pthread_sig_restart = SIGUSR1;
+      __pthread_sig_cancel = SIGUSR2;
+    }
+  /* Setup signal handlers for the initial thread.
+     Since signal handlers are shared between threads, these settings
+     will be inherited by all other threads. */
+  sa.sa_handler = __pthread_sighandler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_RESTART; /* does not matter for regular threads, but
+                               better for the thread manager */
+  sigaction(PTHREAD_SIG_RESTART, &sa, NULL);
+  sa.sa_handler = pthread_handle_sigcancel;
+  sa.sa_flags = 0;
+  sigaction(PTHREAD_SIG_CANCEL, &sa, NULL);
+
+  /* Initially, block PTHREAD_SIG_RESTART. Will be unblocked on demand. */
+  sigemptyset(&mask);
+  sigaddset(&mask, PTHREAD_SIG_RESTART);
+  sigprocmask(SIG_BLOCK, &mask, NULL);
+  /* Register an exit function to kill all other threads. */
+  /* Do it early so that user-registered atexit functions are called
+     before pthread_exit_process. */
+  __on_exit(pthread_exit_process, NULL);
+}
+
+static int pthread_initialize_manager(void)
+{
+  int manager_pipe[2];
+
+  /* If basic initialization not done yet (e.g. we're called from a
+     constructor run before our constructor), do it now */
+  if (__pthread_initial_thread_bos == NULL) pthread_initialize();
+  /* Setup stack for thread manager */
+  __pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE);
+  if (__pthread_manager_thread_bos == NULL) return -1;
+  __pthread_manager_thread_tos =
+    __pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE;
+  /* Setup pipe to communicate with thread manager */
+  if (pipe(manager_pipe) == -1) {
+    free(__pthread_manager_thread_bos);
+    return -1;
+  }
+  __pthread_manager_request = manager_pipe[1]; /* writing end */
+  __pthread_manager_reader = manager_pipe[0]; /* reading end */
+  /* Start the thread manager */
+  __pthread_manager_pid =
+    __clone(__pthread_manager, (void **) __pthread_manager_thread_tos,
+           CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
+           (void *)(long)manager_pipe[0]);
+  if (__pthread_manager_pid == -1) {
+    free(__pthread_manager_thread_bos);
+    __libc_close(manager_pipe[0]);
+    __libc_close(manager_pipe[1]);
+    __pthread_manager_request = -1;
+    return -1;
+  }
+  return 0;
+}
+
+/* Thread creation */
+
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+                   void * (*start_routine)(void *), void *arg)
+{
+  pthread_descr self = thread_self();
+  struct pthread_request request;
+  if (__pthread_manager_request < 0) {
+    if (pthread_initialize_manager() < 0) return EAGAIN;
+  }
+  request.req_thread = self;
+  request.req_kind = REQ_CREATE;
+  request.req_args.create.attr = attr;
+  request.req_args.create.fn = start_routine;
+  request.req_args.create.arg = arg;
+  sigprocmask(SIG_SETMASK, (const sigset_t *) NULL,
+              &request.req_args.create.mask);
+  __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
+  suspend(self);
+  if (self->p_retcode == 0) *thread = (pthread_t) self->p_retval;
+  return self->p_retcode;
+}
+
+/* Simple operations on thread identifiers */
+
+pthread_t pthread_self(void)
+{
+  pthread_descr self = thread_self();
+  return self->p_tid;
+}
+
+int pthread_equal(pthread_t thread1, pthread_t thread2)
+{
+  return thread1 == thread2;
+}
+
+/* Thread scheduling */
+
+int pthread_setschedparam(pthread_t thread, int policy,
+                          const struct sched_param *param)
+{
+  pthread_handle handle = thread_handle(thread);
+  pthread_descr th;
+
+  acquire(&handle->h_spinlock);
+  if (invalid_handle(handle, thread)) {
+    release(&handle->h_spinlock);
+    return ESRCH;
+  }
+  th = handle->h_descr;
+  if (__sched_setscheduler(th->p_pid, policy, param) == -1) {
+    release(&handle->h_spinlock);
+    return errno;
+  }
+  th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority;
+  release(&handle->h_spinlock);
+  return 0;
+}
+
+int pthread_getschedparam(pthread_t thread, int *policy,
+                          struct sched_param *param)
+{
+  pthread_handle handle = thread_handle(thread);
+  int pid, pol;
+
+  acquire(&handle->h_spinlock);
+  if (invalid_handle(handle, thread)) {
+    release(&handle->h_spinlock);
+    return ESRCH;
+  }
+  pid = handle->h_descr->p_pid;
+  release(&handle->h_spinlock);
+  pol = __sched_getscheduler(pid);
+  if (pol == -1) return errno;
+  if (__sched_getparam(pid, param) == -1) return errno;
+  *policy = pol;
+  return 0;
+}
+
+/* Process-wide exit() request */
+
+static void pthread_exit_process(int retcode, void *arg)
+{
+  struct pthread_request request;
+  pthread_descr self = thread_self();
+
+  if (__pthread_manager_request >= 0) {
+    request.req_thread = self;
+    request.req_kind = REQ_PROCESS_EXIT;
+    request.req_args.exit.code = retcode;
+    __libc_write(__pthread_manager_request,
+                (char *) &request, sizeof(request));
+    suspend(self);
+    /* Main thread should accumulate times for thread manager and its
+       children, so that timings for main thread account for all threads. */
+    if (self == __pthread_main_thread)
+      waitpid(__pthread_manager_pid, NULL, __WCLONE);
+  }
+}
+
+/* The handler for the RESTART signal just records the signal received
+   in the thread descriptor, and optionally performs a siglongjmp
+   (for pthread_cond_timedwait). Also used in sigwait.
+   For the thread manager thread, redirect the signal to
+   __pthread_manager_sighandler. */
+
+void __pthread_sighandler(int sig)
+{
+  pthread_descr self = thread_self();
+  if (self == &__pthread_manager_thread) {
+    __pthread_manager_sighandler(sig);
+  } else {
+    self->p_signal = sig;
+    if (self->p_signal_jmp != NULL) siglongjmp(*self->p_signal_jmp, 1);
+  }
+}
+
+/* The handler for the CANCEL signal checks for cancellation
+   (in asynchronous mode) and for process-wide exit and exec requests. */
+
+static void pthread_handle_sigcancel(int sig)
+{
+  pthread_descr self = thread_self();
+  sigjmp_buf * jmpbuf;
+
+  if (__pthread_exit_requested) {
+    /* Main thread should accumulate times for thread manager and its
+       children, so that timings for main thread account for all threads. */
+    if (self == __pthread_main_thread)
+      waitpid(__pthread_manager_pid, NULL, __WCLONE);
+    _exit(__pthread_exit_code);
+  }
+  if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+    if (self->p_canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)
+      pthread_exit(PTHREAD_CANCELED);
+    jmpbuf = self->p_cancel_jmp;
+    if (jmpbuf != NULL) {
+      self->p_cancel_jmp = NULL;
+      siglongjmp(*jmpbuf, 1);
+    }
+  }
+}
+
+/* Reset the state of the thread machinery after a fork().
+   Close the pipe used for requests and set the main thread to the forked
+   thread.
+   Notice that we can't free the stack segments, as the forked thread
+   may hold pointers into them. */
+
+void __pthread_reset_main_thread()
+{
+  pthread_descr self = thread_self();
+
+  if (__pthread_manager_request != -1) {
+    /* Free the thread manager stack */
+    free(__pthread_manager_thread_bos);
+    __pthread_manager_thread_bos = __pthread_manager_thread_tos = NULL;
+    /* Close the two ends of the pipe */
+    __libc_close(__pthread_manager_request);
+    __libc_close(__pthread_manager_reader);
+    __pthread_manager_request = __pthread_manager_reader = -1;
+  }
+  /* Update the pid of the main thread */
+  self->p_pid = __getpid();
+  /* Make the forked thread the main thread */
+  __pthread_main_thread = self;
+  self->p_nextlive = self;
+  self->p_prevlive = self;
+  /* Now this thread modifies the global variables.  */
+  self->p_errnop = &_errno;
+  self->p_h_errnop = &_h_errno;
+}
+
+/* Process-wide exec() request */
+
+void __pthread_kill_other_threads_np(void)
+{
+  /* Terminate all other threads and thread manager */
+  pthread_exit_process(0, NULL);
+  /* Make current thread the main thread in case the calling thread
+     changes its mind, does not exec(), and creates new threads instead. */
+  __pthread_reset_main_thread();
+}
+weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np)
+
+/* Debugging aid */
+
+#ifdef DEBUG
+#include <stdarg.h>
+
+void __pthread_message(char * fmt, long arg)
+{
+  char buffer[1024];
+  va_list args;
+  sprintf(buffer, "%05d : ", __getpid());
+  va_start(args, fmt);
+  vsnprintf(buffer + 8, sizeof(buffer) - 8, fmt, args);
+  va_end(args);
+  __libc_write(2, buffer, strlen(buffer));
+}
+
+#endif
+
+
+#ifndef PIC
+/* We need a hook to force the cancelation wrappers to be linked in when
+   static libpthread is used.  */
+extern const int __pthread_provide_wrappers;
+static const int *const __pthread_require_wrappers =
+  &__pthread_provide_wrappers;
+#endif
diff --git a/linuxthreads/queue.h b/linuxthreads/queue.h
new file mode 100644 (file)
index 0000000..60039cc
--- /dev/null
@@ -0,0 +1,62 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Waiting queues */
+
+typedef struct _pthread_queue pthread_queue;
+
+static inline void queue_init(pthread_queue * q)
+{
+  q->head = q->tail = NULL;
+}
+
+static inline void enqueue(pthread_queue * q, pthread_descr th)
+{
+  int prio;
+  pthread_descr * elt;
+
+  ASSERT(th->p_nextwaiting == NULL);
+  if (q->tail == NULL) {
+    q->head = th;
+    q->tail = th;
+    return;
+  }
+  prio = th->p_priority;
+  if (prio > 0) {
+    /* Insert in queue according to priority order */
+    for (elt = &(q->head); *elt != NULL; elt = &((*elt)->p_nextwaiting)) {
+      if (prio > (*elt)->p_priority) {
+        th->p_nextwaiting = *elt;
+        *elt = th;
+        return;
+      }
+    }
+  }
+  /* Priority is no greater than any thread in the queue.
+     Insert at end of queue */
+  q->tail->p_nextwaiting = th;
+  q->tail = th;
+}
+
+static inline pthread_descr dequeue(pthread_queue * q)
+{
+  pthread_descr th;
+  th = q->head;
+  if (th != NULL) {
+    q->head = th->p_nextwaiting;
+    if (q->head == NULL) q->tail = NULL;
+    th->p_nextwaiting = NULL;
+  }
+  return th;
+}
diff --git a/linuxthreads/restart.h b/linuxthreads/restart.h
new file mode 100644 (file)
index 0000000..4b4a1d7
--- /dev/null
@@ -0,0 +1,57 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+#include <signal.h>
+
+/* Primitives for controlling thread execution */
+
+static inline void restart(pthread_descr th)
+{
+  kill(th->p_pid, PTHREAD_SIG_RESTART);
+}
+
+static inline void suspend(pthread_descr self)
+{
+  sigset_t mask;
+
+  sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
+  sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */
+  do {
+    self->p_signal = 0;
+    sigsuspend(&mask);                   /* Wait for signal */
+  } while (self->p_signal != PTHREAD_SIG_RESTART);
+}
+
+static inline void suspend_with_cancellation(pthread_descr self)
+{
+  sigset_t mask;
+  sigjmp_buf jmpbuf;
+
+  sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */
+  sigdelset(&mask, PTHREAD_SIG_RESTART); /* Unblock the restart signal */
+  /* No need to save the signal mask, we'll restore it ourselves */
+  if (sigsetjmp(jmpbuf, 0) == 0) {
+    self->p_cancel_jmp = &jmpbuf;
+    if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
+      do {
+       self->p_signal = 0;
+        sigsuspend(&mask);               /* Wait for a signal */
+      } while (self->p_signal != PTHREAD_SIG_RESTART);
+    }
+    self->p_cancel_jmp = NULL;
+  } else {
+    sigaddset(&mask, PTHREAD_SIG_RESTART); /* Reblock the restart signal */
+    sigprocmask(SIG_SETMASK, &mask, NULL);
+  }
+}
diff --git a/linuxthreads/rwlock.c b/linuxthreads/rwlock.c
new file mode 100644 (file)
index 0000000..c6b2815
--- /dev/null
@@ -0,0 +1,276 @@
+/* Read-write lock implementation.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Xavier Leroy <Xavier.Leroy@inria.fr>
+   and Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include "internals.h"
+#include "queue.h"
+#include "restart.h"
+#include "spinlock.h"
+
+int
+pthread_rwlock_init (pthread_rwlock_t *rwlock,
+                    const pthread_rwlockattr_t *attr)
+{
+  rwlock->rw_spinlock = 0;
+  rwlock->rw_readers = 0;
+  rwlock->rw_writer = NULL;
+
+  queue_init(&rwlock->rw_read_waiting);
+  queue_init(&rwlock->rw_write_waiting);
+
+  if (attr == NULL)
+    {
+      rwlock->rw_kind = PTHREAD_RWLOCK_DEFAULT_NP;
+      rwlock->rw_pshared = PTHREAD_PROCESS_PRIVATE;
+    }
+  else
+    {
+      rwlock->rw_kind = attr->lockkind;
+      rwlock->rw_pshared = attr->pshared;
+    }
+
+  return 0;
+}
+
+
+int
+pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
+{
+  int readers;
+  _pthread_descr writer;
+
+  acquire (&rwlock->rw_spinlock);
+  readers = rwlock->rw_readers;
+  writer = rwlock->rw_writer;
+  release (&rwlock->rw_spinlock);
+
+  if (readers > 0 || writer != NULL)
+    return EBUSY;
+
+  return 0;
+}
+
+
+int
+pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
+{
+  pthread_descr self;
+
+  while (1)
+    {
+      acquire (&rwlock->rw_spinlock);
+      if (rwlock->rw_writer == NULL
+         || (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
+             && rwlock->rw_readers != 0))
+       /* We can add a reader lock.  */
+       break;
+
+      /* Suspend ourselves, then try again */
+      self = thread_self ();
+      enqueue (&rwlock->rw_read_waiting, self);
+      release (&rwlock->rw_spinlock);
+      suspend (self); /* This is not a cancellation point */
+    }
+
+  ++rwlock->rw_readers;
+  release (&rwlock->rw_spinlock);
+
+  return 0;
+}
+
+
+int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
+{
+  int result = EBUSY;
+
+  acquire (&rwlock->rw_spinlock);
+  if (rwlock->rw_writer == NULL
+      || (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
+         && rwlock->rw_readers != 0))
+    {
+      ++rwlock->rw_readers;
+      result = 0;
+    }
+  release (&rwlock->rw_spinlock);
+
+  return result;
+}
+
+
+int
+pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
+{
+  pthread_descr self = thread_self ();
+
+  while(1)
+    {
+      acquire (&rwlock->rw_spinlock);
+      if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL)
+       {
+         rwlock->rw_writer = self;
+         release (&rwlock->rw_spinlock);
+         return 0;
+       }
+
+      /* Suspend ourselves, then try again */
+      enqueue (&rwlock->rw_write_waiting, self);
+      release (&rwlock->rw_spinlock);
+      suspend (self); /* This is not a cancellation point */
+    }
+}
+
+
+int
+pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
+{
+  int result = EBUSY;
+
+  acquire (&rwlock->rw_spinlock);
+  if (rwlock->rw_readers == 0 && rwlock->rw_writer == NULL)
+    {
+      rwlock->rw_writer = thread_self ();
+      result = 0;
+    }
+  release (&rwlock->rw_spinlock);
+
+  return result;
+}
+
+
+int
+pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
+{
+  struct _pthread_queue torestart;
+  pthread_descr th;
+
+  acquire (&rwlock->rw_spinlock);
+  if (rwlock->rw_writer != NULL)
+    {
+      /* Unlocking a write lock.  */
+      if (rwlock->rw_writer != thread_self ())
+       {
+         release (&rwlock->rw_spinlock);
+         return EPERM;
+       }
+      rwlock->rw_writer = NULL;
+
+      if (rwlock->rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP
+         || (th = dequeue (&rwlock->rw_write_waiting)) == NULL)
+       {
+         /* Restart all waiting readers.  */
+         torestart = rwlock->rw_read_waiting;
+         queue_init (&rwlock->rw_read_waiting);
+         release (&rwlock->rw_spinlock);
+         while ((th = dequeue (&torestart)) != NULL)
+           restart (th);
+       }
+      else
+       {
+         /* Restart one waiting writer.  */
+         release (&rwlock->rw_spinlock);
+         restart (th);
+       }
+    }
+  else
+    {
+      /* Unlocking a read lock.  */
+      if (rwlock->rw_readers == 0)
+       {
+         release (&rwlock->rw_spinlock);
+         return EPERM;
+       }
+
+      --rwlock->rw_readers;
+      if (rwlock->rw_readers == 0)
+       /* Restart one waiting writer, if any.  */
+       th = dequeue (&rwlock->rw_write_waiting);
+      else
+       th = NULL;
+
+      release (&rwlock->rw_spinlock);
+      if (th != NULL)
+       restart (th);
+    }
+
+  return 0;
+}
+
+
+
+int
+pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
+{
+  attr->lockkind = 0;
+  attr->pshared = 0;
+
+  return 0;
+}
+
+
+int
+pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr)
+{
+  return 0;
+}
+
+
+int
+pthread_rwlockattr_getpshared (const pthread_rwlockattr_t *attr, int *pshared)
+{
+  *pshared = attr->pshared;
+  return 0;
+}
+
+
+int
+pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared)
+{
+  if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
+    return EINVAL;
+
+  attr->pshared = pshared;
+
+  return 0;
+}
+
+
+int
+pthread_rwlockattr_getkind_np (const pthread_rwlockattr_t *attr, int *pref)
+{
+  *pref = attr->lockkind;
+  return 0;
+}
+
+
+int
+pthread_rwlockattr_setkind_np (pthread_rwlockattr_t *attr, int pref)
+{
+  if (pref != PTHREAD_RWLOCK_PREFER_READER_NP
+      && pref != PTHREAD_RWLOCK_PREFER_WRITER_NP
+      && pref != PTHREAD_RWLOCK_DEFAULT_NP)
+    return EINVAL;
+
+  attr->lockkind = pref;
+
+  return 0;
+}
diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c
new file mode 100644 (file)
index 0000000..bd07439
--- /dev/null
@@ -0,0 +1,236 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Semaphores a la POSIX 1003.1b */
+
+#include "pthread.h"
+#include "semaphore.h"
+#include "internals.h"
+#include "restart.h"
+
+
+#if !defined HAS_COMPARE_AND_SWAP && !defined TEST_FOR_COMPARE_AND_SWAP
+/* If we have no atomic compare and swap, fake it using an extra spinlock.  */
+
+#include "spinlock.h"
+static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
+{
+  int ret;
+  acquire(&sem->sem_spinlock);
+  ret = (sem->sem_status == oldval);
+  if (ret) sem->sem_status = newval;
+  release(&sem->sem_spinlock);
+  return ret;
+}
+
+#elif defined TEST_FOR_COMPARE_AND_SWAP
+
+#include "spinlock.h"
+static int has_compare_and_swap = -1; /* to be determined at run-time */
+
+static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
+{
+  int ret;
+
+  if (has_compare_and_swap == 1)
+    return __compare_and_swap(&sem->sem_status, oldval, newval);
+
+  acquire(&sem->sem_spinlock);
+  ret = (sem->sem_status == oldval);
+  if (ret) sem->sem_status = newval;
+  release(&sem->sem_spinlock);
+  return ret;
+}
+
+#else
+/* But if we do have an atomic compare and swap, use it!  */
+
+static inline int sem_compare_and_swap(sem_t *sem, long oldval, long newval)
+{
+  return __compare_and_swap(&sem->sem_status, oldval, newval);
+}
+
+#endif
+
+
+/* The state of a semaphore is represented by a long int encoding
+   either the semaphore count if >= 0 and no thread is waiting on it,
+   or the head of the list of threads waiting for the semaphore.
+   To distinguish the two cases, we encode the semaphore count N
+   as 2N+1, so that it has the lowest bit set.
+
+   A sequence of sem_wait operations on a semaphore initialized to N
+   result in the following successive states:
+     2N+1, 2N-1, ..., 3, 1, &first_waiting_thread, &second_waiting_thread, ...
+*/
+
+static void sem_restart_list(pthread_descr waiting);
+
+int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+  if ((long)value > SEM_VALUE_MAX) {
+    errno = EINVAL;
+    return -1;
+  }
+  if (pshared) {
+    errno = ENOSYS;
+    return -1;
+  }
+#ifdef TEST_FOR_COMPARE_AND_SWAP
+  if (has_compare_and_swap == -1) {
+    has_compare_and_swap = compare_and_swap_is_available();
+  }
+#endif
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+  sem->sem_spinlock = 0;
+#endif
+  sem->sem_status = ((long)value << 1) + 1;
+  return 0;
+}
+
+int sem_wait(sem_t * sem)
+{
+  long oldstatus, newstatus;
+  volatile pthread_descr self = thread_self();
+  pthread_descr * th;
+
+  while (1) {
+    do {
+      oldstatus = sem->sem_status;
+      if ((oldstatus & 1) && (oldstatus != 1))
+        newstatus = oldstatus - 2;
+      else {
+        newstatus = (long) self;
+        self->p_nextwaiting = (pthread_descr) oldstatus;
+      }
+    }
+    while (! sem_compare_and_swap(sem, oldstatus, newstatus));
+    if (newstatus & 1)
+      /* We got the semaphore. */
+      return 0;
+    /* Wait for sem_post or cancellation */
+    suspend_with_cancellation(self);
+    /* This is a cancellation point */
+    if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
+      /* Remove ourselves from the waiting list if we're still on it */
+      /* First check if we're at the head of the list. */
+      do {
+        oldstatus = sem->sem_status;
+        if (oldstatus != (long) self) break;
+        newstatus = (long) self->p_nextwaiting;
+      }
+      while (! sem_compare_and_swap(sem, oldstatus, newstatus));
+      /* Now, check if we're somewhere in the list.
+         There's a race condition with sem_post here, but it does not matter:
+         the net result is that at the time pthread_exit is called,
+         self is no longer reachable from sem->sem_status. */
+      if (oldstatus != (long) self && (oldstatus & 1) == 0) {
+       for (th = &(((pthread_descr) oldstatus)->p_nextwaiting);
+            *th != NULL && *th != (pthread_descr) 1;
+            th = &((*th)->p_nextwaiting)) {
+          if (*th == self) {
+            *th = self->p_nextwaiting;
+            break;
+          }
+        }
+      }
+      pthread_exit(PTHREAD_CANCELED);
+    }
+  }
+}
+
+int sem_trywait(sem_t * sem)
+{
+  long oldstatus, newstatus;
+
+  do {
+    oldstatus = sem->sem_status;
+    if ((oldstatus & 1) == 0 || (oldstatus == 1)) {
+      errno = EAGAIN;
+      return -1;
+    }
+    newstatus = oldstatus - 2;
+  }
+  while (! sem_compare_and_swap(sem, oldstatus, newstatus));
+  return 0;
+}
+
+int sem_post(sem_t * sem)
+{
+  long oldstatus, newstatus;
+
+  do {
+    oldstatus = sem->sem_status;
+    if ((oldstatus & 1) == 0)
+      newstatus = 3;
+    else {
+      if (oldstatus >= SEM_VALUE_MAX) {
+        /* Overflow */
+        errno = ERANGE;
+        return -1;
+      }
+      newstatus = oldstatus + 2;
+    }
+  }
+  while (! sem_compare_and_swap(sem, oldstatus, newstatus));
+  if ((oldstatus & 1) == 0)
+    sem_restart_list((pthread_descr) oldstatus);
+  return 0;
+}
+
+int sem_getvalue(sem_t * sem, int * sval)
+{
+  long status = sem->sem_status;
+  if (status & 1)
+    *sval = (int)((unsigned long) status >> 1);
+  else
+    *sval = 0;
+  return 0;
+}
+
+int sem_destroy(sem_t * sem)
+{
+  if ((sem->sem_status & 1) == 0) {
+    errno = EBUSY;
+    return -1;
+  }
+  return 0;
+}
+
+/* Auxiliary function for restarting all threads on a waiting list,
+   in priority order. */
+
+static void sem_restart_list(pthread_descr waiting)
+{
+  pthread_descr th, towake, *p;
+
+  /* Sort list of waiting threads by decreasing priority (insertion sort) */
+  towake = NULL;
+  while (waiting != (pthread_descr) 1) {
+    th = waiting;
+    waiting = waiting->p_nextwaiting;
+    p = &towake;
+    while (*p != NULL && th->p_priority < (*p)->p_priority)
+      p = &((*p)->p_nextwaiting);
+    th->p_nextwaiting = *p;
+    *p = th;
+  }
+  /* Wake up threads in priority order */
+  while (towake != NULL) {
+    th = towake;
+    towake = towake->p_nextwaiting;
+    th->p_nextwaiting = NULL;
+    restart(th);
+  }
+}
diff --git a/linuxthreads/semaphore.h b/linuxthreads/semaphore.h
new file mode 100644 (file)
index 0000000..9f01a7f
--- /dev/null
@@ -0,0 +1,38 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+#ifndef _SEMAPHORE_H
+#define _SEMAPHORE_H    1
+
+#include <features.h>
+
+#include <limits.h>
+
+#define SEM_VALUE_MAX INT_MAX
+
+/* Get the semaphore structure definition.  */
+#include <bits/semaphore.h>
+
+__BEGIN_DECLS
+
+extern int sem_init __P((sem_t *__sem, int __pshared, unsigned int __value));
+extern int sem_destroy __P((sem_t *__sem));
+extern int sem_wait __P((sem_t *__sem));
+extern int sem_trywait __P((sem_t *__sem));
+extern int sem_post __P((sem_t *__sem));
+extern int sem_getvalue __P((sem_t *__sem, int *__sval));
+
+__END_DECLS
+
+#endif  /* semaphore.h */
diff --git a/linuxthreads/shlib-versions b/linuxthreads/shlib-versions
new file mode 100644 (file)
index 0000000..99b0ef1
--- /dev/null
@@ -0,0 +1,2 @@
+# Xavier Leroy's Linux clone based thread library.
+.*-.*-linux.*          libpthread=0
diff --git a/linuxthreads/signals.c b/linuxthreads/signals.c
new file mode 100644 (file)
index 0000000..905e11e
--- /dev/null
@@ -0,0 +1,148 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Handling of signals */
+
+#include <errno.h>
+#include <signal.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+
+int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
+{
+  sigset_t mask;
+
+  if (newmask != NULL) {
+    mask = *newmask;
+    /* Don't allow PTHREAD_SIG_RESTART to be unmasked.
+       Don't allow PTHREAD_SIG_CANCEL to be masked. */
+    switch(how) {
+    case SIG_SETMASK:
+      sigaddset(&mask, PTHREAD_SIG_RESTART);
+      sigdelset(&mask, PTHREAD_SIG_CANCEL);
+      break;
+    case SIG_BLOCK:
+      sigdelset(&mask, PTHREAD_SIG_CANCEL);
+      break;
+    case SIG_UNBLOCK:
+      sigdelset(&mask, PTHREAD_SIG_RESTART);
+      break;
+    }
+    newmask = &mask;
+  }
+  if (sigprocmask(how, newmask, oldmask) == -1)
+    return errno;
+  else
+    return 0;
+}
+
+int pthread_kill(pthread_t thread, int signo)
+{
+  pthread_handle handle = thread_handle(thread);
+  int pid;
+
+  acquire(&handle->h_spinlock);
+  if (invalid_handle(handle, thread)) {
+    release(&handle->h_spinlock);
+    return ESRCH;
+  }
+  pid = handle->h_descr->p_pid;
+  release(&handle->h_spinlock);
+  if (kill(pid, signo) == -1)
+    return errno;
+  else
+    return 0;
+}
+
+/* The set of signals on which some thread is doing a sigwait */
+static sigset_t sigwaited;
+static pthread_mutex_t sigwaited_mut = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t sigwaited_changed = PTHREAD_COND_INITIALIZER;
+
+int sigwait(const sigset_t * set, int * sig)
+{
+  volatile pthread_descr self = thread_self();
+  sigset_t mask;
+  int s;
+  struct sigaction action, saved_signals[NSIG];
+  sigjmp_buf jmpbuf;
+
+  pthread_mutex_lock(&sigwaited_mut);
+  /* Make sure no other thread is waiting on our signals */
+test_again:
+  for (s = 1; s < NSIG; s++) {
+    if (sigismember(set, s) && sigismember(&sigwaited, s)) {
+      pthread_cond_wait(&sigwaited_changed, &sigwaited_mut);
+      goto test_again;
+    }
+  }
+  /* Get ready to block all signals except those in set
+     and the cancellation signal */
+  sigfillset(&mask);
+  sigdelset(&mask, PTHREAD_SIG_CANCEL);
+  /* Signals in set are assumed blocked on entrance */
+  /* Install our signal handler on all signals in set,
+     and unblock them in mask.
+     Also mark those signals as being sigwaited on */
+  for (s = 1; s < NSIG; s++) {
+    if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
+      sigdelset(&mask, s);
+      action.sa_handler = __pthread_sighandler;
+      sigemptyset(&action.sa_mask);
+      action.sa_flags = 0;
+      sigaction(s, &action, &(saved_signals[s]));
+      sigaddset(&sigwaited, s);
+    }
+  }
+  pthread_mutex_unlock(&sigwaited_mut);
+
+  /* Test for cancellation */
+  if (sigsetjmp(jmpbuf, 1) == 0) {
+    self->p_cancel_jmp = &jmpbuf;
+    if (! (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE)) {
+      /* Reset the signal count */
+      self->p_signal = 0;
+      /* Unblock the signals and wait for them */
+      sigsuspend(&mask);
+    }
+  }
+  self->p_cancel_jmp = NULL;
+  /* The signals are now reblocked. Restore the sighandlers. */
+  pthread_mutex_lock(&sigwaited_mut);
+  for (s = 1; s < NSIG; s++) {
+    if (sigismember(set, s) && s != PTHREAD_SIG_CANCEL) {
+      sigaction(s, &(saved_signals[s]), NULL);
+      sigdelset(&sigwaited, s);
+    }
+  }
+  pthread_cond_broadcast(&sigwaited_changed);
+  pthread_mutex_unlock(&sigwaited_mut);
+  /* Check for cancellation */
+  pthread_testcancel();
+  /* We should have self->p_signal != 0 and equal to the signal received */
+  *sig = self->p_signal;
+  return 0;
+}
+
+int raise (int sig)
+{
+  int retcode = pthread_kill(pthread_self(), sig);
+  if (retcode == 0)
+    return 0;
+  else {
+    errno = retcode;
+    return -1;
+  }
+}
diff --git a/linuxthreads/specific.c b/linuxthreads/specific.c
new file mode 100644 (file)
index 0000000..71f1ede
--- /dev/null
@@ -0,0 +1,174 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Thread-specific data */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include "pthread.h"
+#include "internals.h"
+
+typedef void (*destr_function)(void *);
+
+/* Table of keys. */
+
+struct pthread_key_struct {
+  int in_use;                   /* already allocated? */
+  destr_function destr;         /* destruction routine */
+};
+
+static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] =
+  { { 0, NULL } };
+
+/* Mutex to protect access to pthread_keys */
+
+static pthread_mutex_t pthread_keys_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Create a new key */
+
+int __pthread_key_create(pthread_key_t * key, destr_function destr)
+{
+  int i;
+
+  pthread_mutex_lock(&pthread_keys_mutex);
+  for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
+    if (! pthread_keys[i].in_use) {
+      /* Mark key in use */
+      pthread_keys[i].in_use = 1;
+      pthread_keys[i].destr = destr;
+      pthread_mutex_unlock(&pthread_keys_mutex);
+      *key = i;
+      return 0;
+    }
+  }
+  pthread_mutex_unlock(&pthread_keys_mutex);
+  return EAGAIN;
+}
+weak_alias (__pthread_key_create, pthread_key_create)
+
+/* Delete a key */
+
+int pthread_key_delete(pthread_key_t key)
+{
+  pthread_descr self = thread_self();
+  pthread_descr th;
+  unsigned int idx1st, idx2nd;
+
+  pthread_mutex_lock(&pthread_keys_mutex);
+  if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use) {
+    pthread_mutex_unlock(&pthread_keys_mutex);
+    return EINVAL;
+  }
+  pthread_keys[key].in_use = 0;
+  pthread_keys[key].destr = NULL;
+  /* Set the value of the key to NULL in all running threads, so that
+     if the key is reallocated later by pthread_key_create, its
+     associated values will be NULL in all threads. */
+  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
+  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
+  th = self;
+  do {
+    if (th->p_specific[idx1st] != NULL)
+      th->p_specific[idx1st][idx2nd] = NULL;
+    th = th->p_nextlive;
+  } while (th != self);
+  pthread_mutex_unlock(&pthread_keys_mutex);
+  return 0;
+}
+
+/* Set the value of a key */
+
+int __pthread_setspecific(pthread_key_t key, const void * pointer)
+{
+  pthread_descr self = thread_self();
+  unsigned int idx1st, idx2nd;
+
+  if (key >= PTHREAD_KEYS_MAX || !pthread_keys[key].in_use)
+    return EINVAL;
+  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
+  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
+  if (self->p_specific[idx1st] == NULL) {
+    self->p_specific[idx1st] =
+      calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *));
+    if (self->p_specific[idx1st] == NULL)
+      return ENOMEM;
+  }
+  self->p_specific[idx1st][idx2nd] = (void *) pointer;
+  return 0;
+}
+weak_alias (__pthread_setspecific, pthread_setspecific)
+
+/* Get the value of a key */
+
+void * __pthread_getspecific(pthread_key_t key)
+{
+  pthread_descr self = thread_self();
+  unsigned int idx1st, idx2nd;
+
+  if (key >= PTHREAD_KEYS_MAX)
+    return NULL;
+  idx1st = key / PTHREAD_KEY_2NDLEVEL_SIZE;
+  idx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE;
+  if (self->p_specific[idx1st] == NULL || !pthread_keys[key].in_use)
+    return NULL;
+  return self->p_specific[idx1st][idx2nd];
+}
+weak_alias (__pthread_getspecific, pthread_getspecific)
+
+/* Call the destruction routines on all keys */
+
+void __pthread_destroy_specifics()
+{
+  pthread_descr self = thread_self();
+  int i, j, round, found_nonzero;
+  destr_function destr;
+  void * data;
+
+  for (round = 0, found_nonzero = 1;
+       found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
+       round++) {
+    found_nonzero = 0;
+    for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
+      if (self->p_specific[i] != NULL)
+        for (j = 0; j < PTHREAD_KEY_2NDLEVEL_SIZE; j++) {
+          destr = pthread_keys[i * PTHREAD_KEY_2NDLEVEL_SIZE + j].destr;
+          data = self->p_specific[i][j];
+          if (destr != NULL && data != NULL) {
+            self->p_specific[i][j] = NULL;
+            destr(data);
+            found_nonzero = 1;
+          }
+        }
+  }
+  for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
+    if (self->p_specific[i] != NULL) free(self->p_specific[i]);
+  }
+}
+
+/* Thread-specific data for libc. */
+
+int __libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer)
+{
+  pthread_descr self = thread_self();
+
+  self->p_libc_specific[key] = (void *) pointer;
+  return 0;
+}
+
+void * __libc_internal_tsd_get(enum __libc_tsd_key_t key)
+{
+  pthread_descr self = thread_self();
+
+  return self->p_libc_specific[key];
+}
diff --git a/linuxthreads/spinlock.h b/linuxthreads/spinlock.h
new file mode 100644 (file)
index 0000000..d324abb
--- /dev/null
@@ -0,0 +1,30 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr) and          */
+/* Richard Henderson (rth@tamu.edu)                                     */
+/*                                                                      */
+/* This program is free software; you can redistribute it and/or        */
+/* modify it under the terms of the GNU Library General Public License  */
+/* as published by the Free Software Foundation; either version 2       */
+/* of the License, or (at your option) any later version.               */
+/*                                                                      */
+/* This program is distributed in the hope that it will be useful,      */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
+/* GNU Library General Public License for more details.                 */
+
+/* Spin locks */
+
+static inline void acquire(int * spinlock)
+{
+  while (testandset(spinlock)) __sched_yield();
+}
+
+static inline void release(int * spinlock)
+{
+#ifndef RELEASE
+  *spinlock = 0;
+#else
+  RELEASE(spinlock);
+#endif
+}
diff --git a/linuxthreads/sysdeps/alpha/bits/semaphore.h b/linuxthreads/sysdeps/alpha/bits/semaphore.h
new file mode 100644 (file)
index 0000000..323fea1
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _SEMAPHORE_H
+# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
+#endif
+
+
+/* Due to the implementation of the load-locked/store-conditional
+   instructions, we cannot pack semaphores closer than a cache line
+   or risk threads deadlocking on unrelated semaphores.  */
+
+typedef struct {
+  long int sem_status;
+  long int sem_reserved[3];
+} sem_t;
diff --git a/linuxthreads/sysdeps/alpha/pt-machine.h b/linuxthreads/sysdeps/alpha/pt-machine.h
new file mode 100644 (file)
index 0000000..a0c7cc7
--- /dev/null
@@ -0,0 +1,102 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   Alpha version.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson <rth@tamu.edu>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc.,  59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <asm/pal.h>
+
+
+/* Spinlock implementation; required.  */
+extern inline long testandset(int *spinlock)
+{
+  long ret, temp;
+
+  __asm__ __volatile__(
+       "/* Inline spinlock test & set */\n"
+       "1:\t"
+       "ldl_l %0,%3\n\t"
+       "bne %0,2f\n\t"
+       "or $31,1,%1\n\t"
+       "stl_c %1,%2\n\t"
+       "beq %1,1b\n"
+       "2:\tmb\n"
+       "/* End spinlock test & set */"
+       : "=&r"(ret), "=&r"(temp), "=m"(*spinlock)
+       : "m"(*spinlock)
+        : "memory");
+
+  return ret;
+}
+
+/* Spinlock release; default is just set to zero.  */
+#define RELEASE(spinlock) \
+  __asm__ __volatile__("mb" : : : "memory"); \
+  *spinlock = 0
+
+
+/* Begin allocating thread stacks at this address.  Default is to allocate
+   them just below the initial program stack.  */
+#define THREAD_STACK_START_ADDRESS  0x40000000000
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char *stack_pointer __asm__("$30");
+
+
+/* Return the thread descriptor for the current thread.  */
+#define THREAD_SELF \
+({                                                                           \
+  register pthread_descr __self __asm__("$0");                               \
+  __asm__ ("call_pal %1" : "=r"(__self) : "i"(PAL_rduniq) : "$0");           \
+  __self;                                                                    \
+})
+
+/* Initialize the thread-unique value.  */
+#define INIT_THREAD_SELF(descr) \
+{                                                                            \
+  register pthread_descr __self __asm__("$16") = (descr);                    \
+  __asm__ __volatile__ ("call_pal %1" : : "r"(__self), "i"(PAL_wruniq));      \
+}
+
+
+/* Compare-and-swap for semaphores. */
+
+#define HAS_COMPARE_AND_SWAP
+extern inline int __compare_and_swap(long * p, long oldval, long newval)
+{
+  long ret;
+
+  __asm__ __volatile__ (
+       "/* Inline compare & swap */\n"
+       "1:\t"
+       "ldq_l %0,%4\n\t"
+       "cmpeq %0,%2,%0\n\t"
+       "beq %0,2f\n\t"
+       "mov %3,%0\n\t"
+       "stq_c %0,%1\n\t"
+       "beq %0,1b\n\t"
+       "2:\tmb\n"
+       "/* End compare & swap */"
+       : "=&r"(ret), "=m"(*p)
+       : "r"(oldval), "r"(newval), "m"(*p));
+
+  return ret;
+}
diff --git a/linuxthreads/sysdeps/arm/Implies b/linuxthreads/sysdeps/arm/Implies
new file mode 100644 (file)
index 0000000..7edcd7e
--- /dev/null
@@ -0,0 +1 @@
+pthread/no-cmpxchg
diff --git a/linuxthreads/sysdeps/arm/pt-machine.h b/linuxthreads/sysdeps/arm/pt-machine.h
new file mode 100644 (file)
index 0000000..0b9bc01
--- /dev/null
@@ -0,0 +1,44 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   ARM version.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Philip Blundell <philb@gnu.org>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+
+/* This will not work on ARM1 or ARM2 because SWP is lacking on those
+   machines.  Unfortunately we have no way to detect this at compile
+   time; let's hope nobody tries to use one.  */
+
+/* Spinlock implementation; required.  */
+extern inline int
+testandset (int *spinlock)
+{
+  register unsigned int ret;
+
+  __asm__ __volatile__("swp %0, %1, [%2]"
+                      : "=r"(ret)
+                      : "0"(1), "r"(spinlock));
+
+  return ret;
+}
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char * stack_pointer __asm__ ("sp");
diff --git a/linuxthreads/sysdeps/i386/Implies b/linuxthreads/sysdeps/i386/Implies
new file mode 100644 (file)
index 0000000..7edcd7e
--- /dev/null
@@ -0,0 +1 @@
+pthread/no-cmpxchg
diff --git a/linuxthreads/sysdeps/i386/pt-machine.h b/linuxthreads/sysdeps/i386/pt-machine.h
new file mode 100644 (file)
index 0000000..ef4df2a
--- /dev/null
@@ -0,0 +1,93 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   i386 version.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson <rth@tamu.edu>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+
+/* Spinlock implementation; required.  */
+extern inline int
+testandset (int *spinlock)
+{
+  int ret;
+
+  __asm__ __volatile__("xchgl %0, %1"
+       : "=r"(ret), "=m"(*spinlock)
+       : "0"(1), "m"(*spinlock));
+
+  return ret;
+}
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char * stack_pointer __asm__ ("%esp");
+
+
+/* Compare-and-swap for semaphores.
+   Available on the 486 and above, but not on the 386.
+   We test dynamically whether it's available or not. */
+
+#define HAS_COMPARE_AND_SWAP
+#define TEST_FOR_COMPARE_AND_SWAP
+
+extern inline int
+__compare_and_swap (long int *p, long int oldval, long int newval)
+{
+  char ret;
+  long int readval;
+
+  __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
+                       : "=q" (ret), "=m" (*p), "=a" (readval)
+                       : "r" (newval), "m" (*p), "a" (oldval));
+  return ret;
+}
+
+
+extern inline int
+get_eflags (void)
+{
+  int res;
+  __asm__ __volatile__ ("pushfl; popl %0" : "=r" (res) : );
+  return res;
+}
+
+
+extern inline void
+set_eflags (int newflags)
+{
+  __asm__ __volatile__ ("pushl %0; popfl" : : "r" (newflags) : "cc");
+}
+
+
+extern inline int
+compare_and_swap_is_available (void)
+{
+  int oldflags = get_eflags ();
+  int changed;
+  /* Flip AC bit in EFLAGS.  */
+  set_eflags (oldflags ^ 0x40000);
+  /* See if bit changed.  */
+  changed = (get_eflags () ^ oldflags) & 0x40000;
+  /* Restore EFLAGS.  */
+  set_eflags (oldflags);
+  /* If the AC flag did not change, it's a 386 and it lacks cmpxchg.
+     Otherwise, it's a 486 or above and it has cmpxchg.  */
+  return changed != 0;
+}
diff --git a/linuxthreads/sysdeps/m68k/Implies b/linuxthreads/sysdeps/m68k/Implies
new file mode 100644 (file)
index 0000000..81e9366
--- /dev/null
@@ -0,0 +1 @@
+pthread/cmpxchg
diff --git a/linuxthreads/sysdeps/m68k/pt-machine.h b/linuxthreads/sysdeps/m68k/pt-machine.h
new file mode 100644 (file)
index 0000000..c5c6cab
--- /dev/null
@@ -0,0 +1,58 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   m68k version.
+   Copyright (C) 1996 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson <rth@tamu.edu>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+/* Spinlock implementation; required.  */
+extern inline int
+testandset (int *spinlock)
+{
+  char ret;
+
+  __asm__ __volatile__("tas %1; sne %0"
+       : "=dm"(ret), "=m"(*spinlock)
+       : "m"(*spinlock)
+       : "cc");
+
+  return ret;
+}
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char * stack_pointer __asm__ ("%sp");
+
+
+/* Compare-and-swap for semaphores. */
+
+#define HAS_COMPARE_AND_SWAP
+extern inline int
+__compare_and_swap (long int *p, long int oldval, long int newval)
+{
+  char ret;
+  long int readval;
+
+  __asm__ __volatile__ ("casl %2, %3, %1; seq %0"
+                       : "=dm" (ret), "=m" (*p), "=d" (readval)
+                       : "d" (newval), "m" (*p), "2" (oldval));
+
+  return ret;
+}
diff --git a/linuxthreads/sysdeps/mips/Implies b/linuxthreads/sysdeps/mips/Implies
new file mode 100644 (file)
index 0000000..81e9366
--- /dev/null
@@ -0,0 +1 @@
+pthread/cmpxchg
diff --git a/linuxthreads/sysdeps/mips/pt-machine.h b/linuxthreads/sysdeps/mips/pt-machine.h
new file mode 100644 (file)
index 0000000..d15da75
--- /dev/null
@@ -0,0 +1,84 @@
+/* Machine-dependent pthreads configuration and inline functions.
+
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>.
+   Based on the Alpha version by Richard Henderson <rth@tamu.edu>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   TODO: This version makes use of MIPS ISA 2 features.  It won't
+   work on ISA 1.  These machines will have to take the overhead of
+   a sysmips(MIPS_ATOMIC_SET, ...) syscall which isn't implemented
+   yet correctly.  There is however a better solution for R3000
+   uniprocessor machines possible.  */
+
+
+/* Spinlock implementation; required.  */
+extern inline long testandset(int *spinlock)
+{
+  long ret, temp;
+
+  __asm__ __volatile__(
+       "# Inline spinlock test & set\n\t"
+       ".set\tmips2\n"
+       "1:\tll\t%0,%3\n\t"
+       "bnez\t%0,2f\n\t"
+       ".set\tnoreorder\n\t"
+       "li\t%1,1\n\t"
+       ".set\treorder\n\t"
+       "sc\t%1,%2\n\t"
+       "beqz\t%1,1b\n"
+       "2:\t.set\tmips0\n\t"
+       "/* End spinlock test & set */"
+       : "=&r"(ret), "=&r" (temp), "=m"(*spinlock)
+       : "m"(*spinlock)
+       : "memory");
+
+  return ret;
+}
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char * stack_pointer __asm__ ("$29");
+
+
+/* Compare-and-swap for semaphores. */
+
+#define HAS_COMPARE_AND_SWAP
+extern inline int __compare_and_swap(long * p, long oldval, long newval)
+{
+  long ret;
+
+  __asm__ __volatile__ (
+       "/* Inline compare & swap */\n\t"
+       ".set\tmips2\n"
+       "1:\tll\t%0,%4\n\t"
+       ".set\tnoreorder\n\t"
+       "bne\t%0,%2,2f\n\t"
+       "move\t%0,%3\n\t"
+       ".set\treorder\n\t"
+       "sc\t%0,%1\n\t"
+       "beqz\t%0,1b\n"
+       "2:\t.set\tmips0\n\t"
+       "/* End compare & swap */"
+       : "=&r"(ret), "=m"(*p)
+       : "r"(oldval), "r"(newval), "m"(*p));
+
+  return ret;
+}
diff --git a/linuxthreads/sysdeps/powerpc/Implies b/linuxthreads/sysdeps/powerpc/Implies
new file mode 100644 (file)
index 0000000..81e9366
--- /dev/null
@@ -0,0 +1 @@
+pthread/cmpxchg
diff --git a/linuxthreads/sysdeps/powerpc/bits/semaphore.h b/linuxthreads/sysdeps/powerpc/bits/semaphore.h
new file mode 100644 (file)
index 0000000..3770eed
--- /dev/null
@@ -0,0 +1,32 @@
+/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _SEMAPHORE_H
+# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
+#endif
+
+
+/* Due to the implementation of the load-locked/store-conditional
+   instructions, we cannot pack semaphores closer than a cache line
+   or risk threads deadlocking on unrelated semaphores.  */
+
+typedef struct
+  {
+    int sem_status;
+    int sem_reserved[7];
+  } sem_t;
diff --git a/linuxthreads/sysdeps/powerpc/pt-machine.h b/linuxthreads/sysdeps/powerpc/pt-machine.h
new file mode 100644 (file)
index 0000000..a08828b
--- /dev/null
@@ -0,0 +1,84 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   powerpc version.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Richard Henderson <rth@tamu.edu>.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* These routines are from Appendix G of the 'PowerPC 601 RISC Microprocessor
+   User's Manual', by IBM and Motorola.  */
+
+/* For multiprocessor systems, we want to ensure all memory accesses
+   are completed before we reset a lock.  */
+#if 0
+/* on non multiprocessor systems, you can just: */
+#define sync() /* nothing */
+#else
+#define sync() __asm__ __volatile__ ("sync")
+#endif
+
+/* Spinlock implementation; required.  */
+extern inline int
+testandset (int *spinlock)
+{
+  int ret;
+
+  sync();
+  __asm__ __volatile__(
+                      "0:    lwarx %0,0,%1 ;"
+                      "      cmpwi %0,0;"
+                      "      bne 1f;"
+                      "      stwcx. %2,0,%1;"
+                      "      bne- 0b;"
+                      "1:    "
+       : "=&r"(ret)
+       : "r"(spinlock), "r"(1)
+       : "cr0", "memory");
+  sync();
+
+  return ret != 0;
+}
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char * stack_pointer __asm__ ("r1");
+
+/* Compare-and-swap for semaphores. */
+/* note that test-and-set(x) is the same as compare-and-swap(x, 0, 1) */
+
+#define HAS_COMPARE_AND_SWAP
+extern inline int
+__compare_and_swap (int *p, int oldval, int newval)
+{
+  int ret;
+
+  sync();
+  __asm__ __volatile__(
+                      "0:    lwarx %0,0,%1 ;"
+                      "      xor. %0,%3,%0;"
+                      "      bne 1f;"
+                      "      stwcx. %2,0,%1;"
+                      "      bne- 0b;"
+                      "1:    "
+       : "=&r"(ret)
+       : "r"(p), "r"(newval), "r"(oldval)
+       : "cr0", "memory");
+  sync();
+  return ret == 0;
+}
diff --git a/linuxthreads/sysdeps/pthread/Makefile b/linuxthreads/sysdeps/pthread/Makefile
new file mode 100644 (file)
index 0000000..419650c
--- /dev/null
@@ -0,0 +1,3 @@
+ifeq ($(subdir),libio)
+sysdep_headers += bits/stdio-lock.h
+endif
diff --git a/linuxthreads/sysdeps/pthread/bits/libc-lock.h b/linuxthreads/sysdeps/pthread/bits/libc-lock.h
new file mode 100644 (file)
index 0000000..530d48c
--- /dev/null
@@ -0,0 +1,208 @@
+/* libc-internal interface for mutex locks.  LinuxThreads version.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _BITS_LIBC_LOCK_H
+#define _BITS_LIBC_LOCK_H 1
+
+#include <pthread.h>
+
+/* Mutex type.  */
+#ifdef _LIBC
+typedef pthread_mutex_t __libc_lock_t;
+#else
+typedef struct __libc_lock_opaque__ __libc_lock_t;
+#endif
+
+/* Type for key to thread-specific data.  */
+typedef pthread_key_t __libc_key_t;
+
+/* Define a lock variable NAME with storage class CLASS.  The lock must be
+   initialized with __libc_lock_init before it can be used (or define it
+   with __libc_lock_define_initialized, below).  Use `extern' for CLASS to
+   declare a lock defined in another module.  In public structure
+   definitions you must use a pointer to the lock structure (i.e., NAME
+   begins with a `*'), because its storage size will not be known outside
+   of libc.  */
+#define __libc_lock_define(CLASS,NAME) \
+  CLASS __libc_lock_t NAME;
+
+/* Define an initialized lock variable NAME with storage class CLASS.  */
+#define __libc_lock_define_initialized(CLASS,NAME) \
+  CLASS __libc_lock_t NAME = PTHREAD_MUTEX_INITIALIZER;
+
+/* Define an initialized recursive lock variable NAME with storage
+   class CLASS.  */
+#define __libc_lock_define_initialized_recursive(CLASS,NAME) \
+  CLASS __libc_lock_t NAME = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+/* Initialize the named lock variable, leaving it in a consistent, unlocked
+   state.  */
+#define __libc_lock_init(NAME) \
+  (__pthread_mutex_init != NULL ? __pthread_mutex_init (&(NAME), NULL) : 0);
+
+/* Same as last but this time we initialize a recursive mutex.  */
+#define __libc_lock_init_recursive(NAME) \
+  do {                                                                       \
+    if (__pthread_mutex_init != NULL)                                        \
+      {                                                                              \
+       pthread_mutexattr_t __attr;                                           \
+       __pthread_mutexattr_init (&__attr);                                   \
+       __pthread_mutexattr_setkind_np (&__attr, PTHREAD_MUTEX_RECURSIVE_NP); \
+       __pthread_mutex_init (&(NAME), &__attr);                              \
+       __pthread_mutexattr_destroy (&__attr);                                \
+      }                                                                              \
+  } while (0);
+
+/* Finalize the named lock variable, which must be locked.  It cannot be
+   used again until __libc_lock_init is called again on it.  This must be
+   called on a lock variable before the containing storage is reused.  */
+#define __libc_lock_fini(NAME) \
+  (__pthread_mutex_destroy != NULL ? __pthread_mutex_destroy (&(NAME)) : 0);
+
+/* Finalize recursive named lock.  */
+#define __libc_lock_fini_recursive(NAME) __libc_lock_fini (NAME)
+
+/* Lock the named lock variable.  */
+#define __libc_lock_lock(NAME) \
+  (__pthread_mutex_lock != NULL ? __pthread_mutex_lock (&(NAME)) : 0);
+
+/* Lock the recursive named lock variable.  */
+#define __libc_lock_lock_recursive(NAME) __libc_lock_lock (NAME)
+
+/* Try to lock the named lock variable.  */
+#define __libc_lock_trylock(NAME) \
+  (__pthread_mutex_trylock != NULL ? __pthread_mutex_trylock (&(NAME)) : 0)
+
+/* Try to lock the recursive named lock variable.  */
+#define __libc_lock_trylock_recursive(NAME) __libc_lock_trylock (NAME)
+
+/* Unlock the named lock variable.  */
+#define __libc_lock_unlock(NAME) \
+  (__pthread_mutex_unlock != NULL ? __pthread_mutex_unlock (&(NAME)) : 0);
+
+/* Unlock the recursive named lock variable.  */
+#define __libc_lock_unlock_recursive(NAME) __libc_lock_unlock (NAME)
+
+
+/* Define once control variable.  */
+#define __libc_once_define(CLASS, NAME) \
+  CLASS pthread_once_t NAME = PTHREAD_ONCE_INIT
+
+/* Call handler iff the first call.  */
+#define __libc_once(ONCE_CONTROL, INIT_FUNCTION) \
+  do {                                                                       \
+    if (__pthread_once != NULL)                                                      \
+      __pthread_once (&(ONCE_CONTROL), (INIT_FUNCTION));                     \
+    else if ((ONCE_CONTROL) == 0) {                                          \
+      INIT_FUNCTION ();                                                              \
+      (ONCE_CONTROL) = 1;                                                    \
+    }                                                                        \
+  } while (0)
+
+
+/* Start critical region with cleanup.  */
+#define __libc_cleanup_region_start(FCT, ARG) \
+  { struct _pthread_cleanup_buffer _buffer;                                  \
+    if (_pthread_cleanup_push_defer != NULL) {                               \
+      _pthread_cleanup_push_defer (&_buffer, (FCT), (ARG));                  \
+    }
+
+/* End critical region with cleanup.  */
+#define __libc_cleanup_region_end(DOIT) \
+    if (_pthread_cleanup_push_defer != NULL) {                               \
+      _pthread_cleanup_pop_restore (&_buffer, (DOIT));                       \
+    }                                                                        \
+  }
+
+/* Create thread-specific key.  */
+#define __libc_key_create(KEY, DESTRUCTOR) \
+  (__pthread_key_create != NULL ? __pthread_key_create (KEY, DESTRUCTOR) : 1)
+
+/* Get thread-specific data.  */
+#define __libc_getspecific(KEY) \
+  (__pthread_getspecific != NULL ? __pthread_getspecific (KEY) : NULL)
+
+/* Set thread-specific data.  */
+#define __libc_setspecific(KEY, VALUE) \
+  (__pthread_setspecific != NULL ? __pthread_setspecific (KEY, VALUE) : 0)
+
+#ifdef _LIBC
+
+/* Fast thread-specific data internal to libc.  */
+enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, _LIBC_TSD_KEY_N };
+
+extern void *__libc_internal_tsd_get __P ((enum __libc_tsd_key_t));
+extern int __libc_internal_tsd_set __P ((enum __libc_tsd_key_t,
+                                        __const void *));
+
+#endif
+
+
+/* Register handlers to execute before and after `fork'.  */
+#define __libc_atfork(PREPARE, PARENT, CHILD) \
+  (__pthread_atfork != NULL ? __pthread_atfork (PREPARE, PARENT, CHILD) : 0)
+
+
+/* Make the pthread functions weak so that we can elide them from
+   single-threaded processes.  */
+#ifdef weak_extern
+weak_extern (__pthread_mutex_init)
+weak_extern (__pthread_mutex_destroy)
+weak_extern (__pthread_mutex_lock)
+weak_extern (__pthread_mutex_trylock)
+weak_extern (__pthread_mutex_unlock)
+weak_extern (__pthread_mutexattr_init)
+weak_extern (__pthread_mutexattr_destroy)
+weak_extern (__pthread_mutexattr_setkind_np)
+weak_extern (__pthread_key_create)
+weak_extern (__pthread_setspecific)
+weak_extern (__pthread_getspecific)
+weak_extern (__libc_internal_tsd_get)
+weak_extern (__libc_internal_tsd_set)
+weak_extern (__pthread_once)
+weak_extern (__pthread_initialize)
+weak_extern (__pthread_atfork)
+weak_extern (_pthread_cleanup_push_defer)
+weak_extern (_pthread_cleanup_pop_restore)
+#else
+# pragma weak __pthread_mutex_init
+# pragma weak __pthread_mutex_destroy
+# pragma weak __pthread_mutex_lock
+# pragma weak __pthread_mutex_trylock
+# pragma weak __pthread_mutex_unlock
+# pragma weak __pthread_mutexattr_init
+# pragma weak __pthread_mutexattr_destroy
+# pragma weak __pthread_mutexattr_setkind_np
+# pragma weak __pthread_key_create
+# pragma weak __pthread_setspecific
+# pragma weak __pthread_getspecific
+# pragma weak __libc_internal_tsd_get
+# pragma weak __libc_internal_tsd_set
+# pragma weak __pthread_once
+# pragma weak __pthread_initialize
+# pragma weak __pthread_atfork
+# pragma weak _pthread_cleanup_push_defer
+# pragma weak _pthread_cleanup_pop_restore
+#endif
+
+/* We need portable names for some functions.  E.g., when they are
+   used as argument to __libc_cleanup_region_start.  */
+#define __libc_mutex_unlock __pthread_mutex_unlock
+
+#endif /* bits/libc-lock.h */
diff --git a/linuxthreads/sysdeps/pthread/bits/stdio-lock.h b/linuxthreads/sysdeps/pthread/bits/stdio-lock.h
new file mode 100644 (file)
index 0000000..23ebf40
--- /dev/null
@@ -0,0 +1,35 @@
+/* Thread package specific definitions of stream lock type.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <pthread.h>
+
+typedef pthread_mutex_t _IO_lock_t;
+
+/* We need recursive (counting) mutexes.  */
+#define _IO_lock_initializer PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+
+
+#define _IO_cleanup_region_start(_fct, _fp) \
+     __libc_cleanup_region_start (_fct, _fp)
+#define _IO_cleanup_region_end(_doit) \
+     __libc_cleanup_region_end (_doit)
+#define _IO_lock_init(_name) \
+     __libc_lock_init_recursive (_name)
+#define _IO_lock_fini(_name) \
+     __libc_lock_fini_recursive (_name)
diff --git a/linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h b/linuxthreads/sysdeps/pthread/cmpxchg/bits/semaphore.h
new file mode 100644 (file)
index 0000000..0cdbc05
--- /dev/null
@@ -0,0 +1,26 @@
+/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _SEMAPHORE_H
+# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
+#endif
+
+
+typedef struct {
+  long int sem_status;
+} sem_t;
diff --git a/linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h b/linuxthreads/sysdeps/pthread/no-cmpxchg/bits/semaphore.h
new file mode 100644 (file)
index 0000000..4d801a2
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _SEMAPHORE_H
+# error "Never include <bits/semaphore.h> directly; use <semaphore.h> instead."
+#endif
+
+
+typedef struct {
+  long int sem_status;
+  int sem_spinlock;
+} sem_t;
diff --git a/linuxthreads/sysdeps/pthread/pthread.h b/linuxthreads/sysdeps/pthread/pthread.h
new file mode 100644 (file)
index 0000000..b62706a
--- /dev/null
@@ -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)