OSDN Git Service

Merge in the pthread library. This is the linuxthreads library taken from
authorEric Andersen <andersen@codepoet.org>
Wed, 20 Feb 2002 09:18:50 +0000 (09:18 -0000)
committerEric Andersen <andersen@codepoet.org>
Wed, 20 Feb 2002 09:18:50 +0000 (09:18 -0000)
glibc 2.1.3 and ported to work with uClibc by Stefan Soucek and Erik Andersen
(me).  Stefan has hacked things up such that linuxthreads runs on MMU-less
systems (tested only on arm-nommu).  Erik cleaned things up and made it work
properly as a shared library.
 -Erik

124 files changed:
Makefile
Rules.mak
include/features.h
include/semaphore.h [new file with mode: 0644]
libc/misc/Makefile
libc/misc/internals/.cvsignore [new file with mode: 0644]
libc/misc/internals/Makefile
libc/misc/internals/__errno_location.c [new file with mode: 0644]
libc/misc/internals/__uClibc_main.c
libc/misc/internals/errno.c [new file with mode: 0644]
libc/misc/pthread/Makefile [new file with mode: 0644]
libc/misc/pthread/no-tsd.c [new file with mode: 0644]
libc/misc/pthread/weaks.c [new file with mode: 0644]
libc/sysdeps/linux/common/Makefile
libc/sysdeps/linux/common/errno.c [deleted file]
libpthread/Makefile
libpthread/linuxthreads/ChangeLog [new file with mode: 0644]
libpthread/linuxthreads/Changes [new file with mode: 0644]
libpthread/linuxthreads/FAQ.html [new file with mode: 0644]
libpthread/linuxthreads/LICENSE [new file with mode: 0644]
libpthread/linuxthreads/Makefile [new file with mode: 0644]
libpthread/linuxthreads/README [new file with mode: 0644]
libpthread/linuxthreads/README.Xfree3.2 [new file with mode: 0644]
libpthread/linuxthreads/Versions [new file with mode: 0644]
libpthread/linuxthreads/attr.c [new file with mode: 0644]
libpthread/linuxthreads/cancel.c [new file with mode: 0644]
libpthread/linuxthreads/condvar.c [new file with mode: 0644]
libpthread/linuxthreads/configure [new file with mode: 0644]
libpthread/linuxthreads/debug.h [new file with mode: 0644]
libpthread/linuxthreads/errno.c [new file with mode: 0644]
libpthread/linuxthreads/events.c [new file with mode: 0644]
libpthread/linuxthreads/internals.h [new file with mode: 0644]
libpthread/linuxthreads/join.c [new file with mode: 0644]
libpthread/linuxthreads/linuxthreads.texi [new file with mode: 0644]
libpthread/linuxthreads/lockfile.c [new file with mode: 0644]
libpthread/linuxthreads/manager.c [new file with mode: 0644]
libpthread/linuxthreads/mutex.c [new file with mode: 0644]
libpthread/linuxthreads/no-tsd.c [new file with mode: 0644]
libpthread/linuxthreads/oldsemaphore.c [new file with mode: 0644]
libpthread/linuxthreads/pt-machine.c [new file with mode: 0644]
libpthread/linuxthreads/ptfork.c [new file with mode: 0644]
libpthread/linuxthreads/pthread.c [new file with mode: 0644]
libpthread/linuxthreads/ptlongjmp.c [new file with mode: 0644]
libpthread/linuxthreads/queue.h [new file with mode: 0644]
libpthread/linuxthreads/restart.h [new file with mode: 0644]
libpthread/linuxthreads/rwlock.c [new file with mode: 0644]
libpthread/linuxthreads/semaphore.c [new file with mode: 0644]
libpthread/linuxthreads/semaphore.h [new file with mode: 0644]
libpthread/linuxthreads/signals.c [new file with mode: 0644]
libpthread/linuxthreads/specific.c [new file with mode: 0644]
libpthread/linuxthreads/spinlock.c [new file with mode: 0644]
libpthread/linuxthreads/spinlock.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/alpha/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/alpha/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/arm/bits/armsigctx.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/arm/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/i386/i686/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/i386/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/i386/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/i386/useldt.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/m68k/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/m68k/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/mips/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/mips/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/powerpc/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/powerpc/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/pthread/bits/libc-tsd.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/pthread/bits/pthreadtypes.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/pthread/bits/stdio-lock.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/sparc/sparc32/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/sparc/sparc64/sigcontextinfo.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h [new file with mode: 0644]
libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h [new file with mode: 0644]
libpthread/linuxthreads/testrtsig.h [new file with mode: 0644]
libpthread/linuxthreads/weaks.c [new file with mode: 0644]
libpthread/linuxthreads/wrapsyscall.c [new file with mode: 0644]
libpthread/linuxthreads_db/ChangeLog [new file with mode: 0644]
libpthread/linuxthreads_db/Makefile [new file with mode: 0644]
libpthread/linuxthreads_db/Versions [new file with mode: 0644]
libpthread/linuxthreads_db/proc_service.h [new file with mode: 0644]
libpthread/linuxthreads_db/td_init.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_log.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_clear_event.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_delete.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_enable_stats.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_event_addr.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_event_getmsg.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_get_nthreads.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_get_ph.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_get_stats.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_map_id2thr.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_map_lwp2thr.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_new.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_reset_stats.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_set_event.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_setconcurrency.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_thr_iter.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_ta_tsd_iter.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_clear_event.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_dbresume.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_dbsuspend.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_event_enable.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_event_getmsg.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_get_info.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_getfpregs.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_getgregs.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_getxregs.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_getxregsize.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_set_event.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_setfpregs.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_setgregs.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_setprio.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_setsigpending.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_setxregs.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_sigsetmask.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_tsd.c [new file with mode: 0644]
libpthread/linuxthreads_db/td_thr_validate.c [new file with mode: 0644]
libpthread/linuxthreads_db/thread_db.h [new file with mode: 0644]
libpthread/linuxthreads_db/thread_dbP.h [new file with mode: 0644]
libpthread/pthread.c

index 0c6dd7a..877f47e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@
 TOPDIR=./
 include Rules.mak
 
-DIRS = extra ldso libc libcrypt libresolv libutil libm #libpthread
+DIRS = extra ldso libc libcrypt libresolv libutil libm libpthread
 
 all: headers uClibc_config.h subdirs shared finished
 
@@ -51,7 +51,7 @@ ifeq ($(strip $(HAVE_SHARED)),true)
        @$(MAKE) -C libresolv shared
        @$(MAKE) -C libutil shared
        @$(MAKE) -C libm shared
-       #@$(MAKE) -C libpthread shared
+       @$(MAKE) -C libpthread shared
 else
        @echo
        @echo Not building shared libraries...
@@ -165,6 +165,11 @@ uClibc_config.h: Makefile Config
        else \
            echo "#undef __UCLIBC_HAVE_LFS__" >> uClibc_config.h ; \
        fi
+       @if [ "$(INCLUDE_THREADS)" = "true" ] ; then \
+           echo "#define __UCLIBC_HAS_THREADS__ 1" >> uClibc_config.h ; \
+       else \
+           echo "#undef __UCLIBC_HAS_THREADS__" >> uClibc_config.h ; \
+       fi
        @if [ "$(UNIX98PTY_ONLY)" = "true" ] ; then \
            echo "#define UNIX98PTY_ONLY 1" >> uClibc_config.h ; \
        else \
index 92e0ef6..18d13b3 100644 (file)
--- a/Rules.mak
+++ b/Rules.mak
@@ -105,7 +105,7 @@ ifeq ($(strip $(HAVE_SHARED)),true)
     ifeq ($(strip $(BUILD_UCLIBC_LDSO)),true)
     LDSO=$(TOPDIR)lib/$(UCLIBC_LDSO)
     DYNAMIC_LINKER=$(SHARED_LIB_LOADER_PATH)/$(UCLIBC_LDSO)
-BUILD_DYNAMIC_LINKER=${shell cd $(TOPDIR)lib && pwd}/$(UCLIBC_LDSO)
+    BUILD_DYNAMIC_LINKER=${shell cd $(TOPDIR)lib && pwd}/$(UCLIBC_LDSO)
     else
     LDSO=$(SYSTEM_LDSO)
     BUILD_UCLIBC_LDSO=false
@@ -116,6 +116,9 @@ endif
 ifeq ($(strip $(DOPIC)),true)
     CFLAGS += -fPIC
 endif
+ifeq ($(strip $(INCLUDE_THREADS)),true)
+    CFLAGS  += -D_LIBC_REENTRANT
+endif
 
 # TARGET_PREFIX is the directory under which which the uClibc runtime
 # environment will be installed and used on the target system.   The 
index 033cf98..580ed3e 100644 (file)
 /* This comes between the return type and function name in
  *    a function definition to make that definition weak.  */
 # define weak_function __attribute__ ((weak))
+# define weak_const_function __attribute__ ((weak, __const__))
 /* Tacking on "\n\t#" to the section name makes gcc put it's bogus
  * section attributes on what looks like a comment to the assembler. */
 #  define link_warning(symbol, msg)                                          \
diff --git a/include/semaphore.h b/include/semaphore.h
new file mode 100644 (file)
index 0000000..8474223
--- /dev/null
@@ -0,0 +1,80 @@
+/* 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 <sys/types.h>
+
+#ifndef _PTHREAD_DESCR_DEFINED
+/* Thread descriptors.  Needed for `sem_t' definition.  */
+typedef struct _pthread_descr_struct *_pthread_descr;
+# define _PTHREAD_DESCR_DEFINED
+#endif
+
+/* System specific semaphore definition.  */
+typedef struct
+{
+  struct
+  {
+    long int status;
+    int spinlock;
+  } __sem_lock;
+  int __sem_value;
+  _pthread_descr __sem_waiting;
+} sem_t;
+
+
+
+/* Value returned if `sem_open' failed.  */
+#define SEM_FAILED     ((sem_t *) 0)
+
+/* Maximum value the semaphore can have.  */
+#define SEM_VALUE_MAX  ((int) ((~0u) >> 1))
+
+
+__BEGIN_DECLS
+
+/* Initialize semaphore object SEM to VALUE.  If PSHARED then share it
+   with other processes.  */
+extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
+
+/* Free resources associated with semaphore object SEM.  */
+extern int sem_destroy __P ((sem_t *__sem));
+
+/* Open a named semaphore NAME with open flaot OFLAG.  */
+extern sem_t *sem_open __P ((__const char *__name, int __oflag, ...));
+
+/* Close descriptor for named semaphore SEM.  */
+extern int sem_close __P ((sem_t *__sem));
+
+/* Remove named semaphore NAME.  */
+extern int sem_unlink __P ((__const char *__name));
+
+/* Wait for SEM being posted.  */
+extern int sem_wait __P ((sem_t *__sem));
+
+/* Test whether SEM is posted.  */
+extern int sem_trywait __P ((sem_t *__sem));
+
+/* Post SEM.  */
+extern int sem_post __P ((sem_t *__sem));
+
+/* Get current value of SEM and store it in *SVAL.  */
+extern int sem_getvalue __P ((sem_t *__sem, int *__sval));
+
+__END_DECLS
+
+#endif  /* semaphore.h */
index cd562d6..6dc65df 100644 (file)
@@ -30,6 +30,9 @@ DIRS = assert ctype dirent file fnmatch glob internals lsearch \
 ifeq ($(strip $(INCLUDE_REGEX)),true)
 DIRS += regex
 endif
+ifeq ($(strip $(INCLUDE_THREADS)),true)
+DIRS += pthread
+endif
 
 all: libc.a
 
diff --git a/libc/misc/internals/.cvsignore b/libc/misc/internals/.cvsignore
new file mode 100644 (file)
index 0000000..10e4867
--- /dev/null
@@ -0,0 +1 @@
+interp.c
index 29ffdec..c81e020 100644 (file)
@@ -24,7 +24,7 @@
 TOPDIR=../../../
 include $(TOPDIR)Rules.mak
 
-CSRC=ultostr.c ltostr.c __uClibc_main.c tempname.c
+CSRC=ultostr.c ltostr.c __uClibc_main.c tempname.c errno.c __errno_location.c
 ifeq ($(HAS_FLOATING_POINT),true)
        CSRC += dtostr.c zoicheck.c
 endif
diff --git a/libc/misc/internals/__errno_location.c b/libc/misc/internals/__errno_location.c
new file mode 100644 (file)
index 0000000..10b9775
--- /dev/null
@@ -0,0 +1,8 @@
+#include <errno.h>
+#undef errno
+
+int * weak_const_function __errno_location (void)
+{
+    return &errno;
+}
+
index 299e1ab..fd4e955 100644 (file)
  * avoided in the static library case.
  */
 
+#define        _ERRNO_H
 #include <stdlib.h>
 #include <unistd.h>
-#include <errno.h>
+//#include <errno.h>
+#undef errno
+
+#define __set_errno(val) (*__errno_location ()) = (val)
 
 /*
  * Prototypes.
@@ -29,13 +33,21 @@ void __uClibc_main(int argc, char **argv, char **envp)
 weak_alias(__environ, environ);
 extern void weak_function __init_stdio(void);
 extern void weak_function __stdio_flush_buffers(void);
-extern void weak_function __pthread_initialize_minimal (void);
+extern int *weak_function __errno_location (void);
 #else
 extern void __init_stdio(void);
 extern void __stdio_flush_buffers(void);
-extern void __pthread_initialize_minimal (void);
+extern int *__errno_location (void);
 #endif 
 
+/*
+ * Declare the __environ global variable and create a weak alias environ.
+ * Note: Apparently we must initialize __environ for the weak environ
+ * symbol to be included.
+ */
+
+char **__environ = 0;
+
 
 /*
  * Now for our main routine.
@@ -48,11 +60,6 @@ void __uClibc_main(int argc, char **argv, char **envp)
         */
        __environ = envp;
 
-       /* Initialize the thread library at least a bit so at least
-        * errno will be properly setup */
-       if (__pthread_initialize_minimal)
-           __pthread_initialize_minimal ();
-
 #if 0
        /* Some security at this point.  Prevent starting a SUID binary
         * where the standard file descriptors are not opened.  We have
@@ -81,14 +88,6 @@ void __uClibc_main(int argc, char **argv, char **envp)
        exit(main(argc, argv, envp));
 }
 
-/*
- * Declare the __environ global variable and create a weak alias environ.
- * Note: Apparently we must initialize __environ for the weak environ
- * symbol to be included.
- */
-
-char **__environ = 0;
-
 #ifndef HAVE_ELF
 /*
  * Define an empty function and use it as a weak alias for the stdio
diff --git a/libc/misc/internals/errno.c b/libc/misc/internals/errno.c
new file mode 100644 (file)
index 0000000..06978c4
--- /dev/null
@@ -0,0 +1,29 @@
+#if 0
+#include <features.h>
+/* Unforunately, this produces noisy warnings... */
+int errno __attribute__ ((section  (".bss")));
+int h_errno __attribute__ ((section  (".bss")));
+weak_alias(errno, _errno);
+weak_alias(h_errno, _h_errno);
+#else
+__asm__("
+.weak _errno;
+_errno = errno
+.weak _h_errno;
+_h_errno = h_errno
+
+.bss
+.globl  errno
+.type errno,%object
+.size errno,4
+errno:
+    .space  4
+
+.bss
+.globl  h_errno
+.type h_errno,%object
+.size h_errno,4
+h_errno:
+    .space  4
+");
+#endif
diff --git a/libc/misc/pthread/Makefile b/libc/misc/pthread/Makefile
new file mode 100644 (file)
index 0000000..8409789
--- /dev/null
@@ -0,0 +1,49 @@
+# Makefile for uClibc
+#
+# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org>
+#
+# 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.
+#
+# You should have received a copy of the GNU Library 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
+
+TOPDIR=../../../
+include $(TOPDIR)Rules.mak
+
+PTDIR = $(TOPDIR)libpthread/linuxthreads/
+SYSDEPINC = -I$(PTDIR)sysdeps/unix/sysv/linux \
+            -I$(PTDIR)sysdeps/pthread \
+            -I$(PTDIR)sysdeps/unix/sysv \
+            -I$(PTDIR)sysdeps/unix/unix \
+            -I$(PTDIR)sysdeps/$(TARGET_ARCH) \
+            -I$(PTDIR)sysdeps \
+            -I$(TOPDIR)libc/sysdeps/linux/$(TARGET_ARCH)
+CFLAGS += $(SYSDEPINC) -D_GNU_SOURCE
+
+CSRC=no-tsd.c #weaks.c
+COBJS=$(patsubst %.c,%.o, $(CSRC))
+OBJS=$(COBJS)
+
+all: $(OBJS) $(LIBC)
+
+$(LIBC): ar-target
+
+ar-target: $(OBJS)
+       $(AR) $(ARFLAGS) $(LIBC) $(OBJS)
+
+$(COBJS): %.o : %.c
+       $(CC) $(CFLAGS) -c $< -o $@
+       $(STRIPTOOL) -x -R .note -R .comment $*.o
+
+clean:
+       rm -f *.[oa] *~ core
+
diff --git a/libc/misc/pthread/no-tsd.c b/libc/misc/pthread/no-tsd.c
new file mode 100644 (file)
index 0000000..ef79cb8
--- /dev/null
@@ -0,0 +1,33 @@
+/* libc-internal interface for thread-specific data.
+   Copyright (C) 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.  */
+
+#include <bits/libc-tsd.h>
+
+/* This file provides uinitialized (common) definitions for the
+   hooks used internally by libc to access thread-specific data.
+
+   When -lpthread is used, it provides initialized definitions for these
+   variables (in specific.c), which override these uninitialized definitions.
+
+   If -lpthread is not used, these uninitialized variables default to zero,
+   which the __libc_tsd_* macros check for.   */
+
+void *(*__libc_internal_tsd_get) __P ((enum __libc_tsd_key_t));
+int (*__libc_internal_tsd_set) __P ((enum __libc_tsd_key_t,
+                                    __const void *));
diff --git a/libc/misc/pthread/weaks.c b/libc/misc/pthread/weaks.c
new file mode 100644 (file)
index 0000000..e2019d0
--- /dev/null
@@ -0,0 +1,107 @@
+/* The weak pthread functions for Linux.
+   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.  */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+extern int __pthread_return_0 __P ((void));
+extern int __pthread_return_1 __P ((void));
+extern void __pthread_return_void __P ((void));
+
+weak_alias (__pthread_return_0, pthread_attr_init)
+weak_alias (__pthread_return_0, pthread_attr_destroy)
+weak_alias (__pthread_return_0, pthread_attr_setdetachstate)
+weak_alias (__pthread_return_0, pthread_attr_getdetachstate)
+weak_alias (__pthread_return_0, pthread_attr_setschedparam)
+weak_alias (__pthread_return_0, pthread_attr_getschedparam)
+weak_alias (__pthread_return_0, pthread_attr_setschedpolicy)
+weak_alias (__pthread_return_0, pthread_attr_getschedpolicy)
+weak_alias (__pthread_return_0, pthread_attr_setinheritsched)
+weak_alias (__pthread_return_0, pthread_attr_getinheritsched)
+weak_alias (__pthread_return_0, pthread_attr_setscope)
+weak_alias (__pthread_return_0, pthread_attr_getscope)
+weak_alias (__pthread_return_0, pthread_attr_setstackaddr)
+weak_alias (__pthread_return_0, pthread_attr_getstackaddr)
+weak_alias (__pthread_return_0, pthread_attr_setstacksize)
+weak_alias (__pthread_return_0, pthread_attr_getstacksize)
+weak_alias (__pthread_return_0, pthread_mutex_init)
+weak_alias (__pthread_return_0, pthread_mutex_destroy)
+weak_alias (__pthread_return_0, pthread_mutex_lock)
+weak_alias (__pthread_return_0, pthread_mutex_trylock)
+weak_alias (__pthread_return_0, pthread_mutex_unlock)
+weak_alias (__pthread_return_0, pthread_mutexattr_init)
+weak_alias (__pthread_return_0, pthread_mutexattr_destroy)
+weak_alias (__pthread_return_0, pthread_mutexattr_settype)
+weak_alias (__pthread_return_0, pthread_mutexattr_gettype)
+weak_alias (__pthread_return_0, pthread_condattr_init)
+weak_alias (__pthread_return_0, pthread_condattr_destroy)
+weak_alias (__pthread_return_0, pthread_setschedparam)
+weak_alias (__pthread_return_0, pthread_getschedparam)
+weak_alias (__pthread_return_0, pthread_getcancelstate)
+weak_alias (__pthread_return_0, pthread_setcancelstate)
+weak_alias (__pthread_return_0, pthread_setcanceltype)
+weak_alias (__pthread_return_0, pthread_setconcurrency)
+weak_alias (__pthread_return_0, pthread_getconcurrency)
+weak_alias (__pthread_return_0, pthread_self)
+weak_alias (__pthread_return_0, pthread_cond_init)
+weak_alias (__pthread_return_0, pthread_cond_destroy)
+weak_alias (__pthread_return_0, pthread_cond_wait)
+weak_alias (__pthread_return_0, pthread_cond_timedwait)
+weak_alias (__pthread_return_0, pthread_cond_signal)
+weak_alias (__pthread_return_0, pthread_cond_broadcast)
+weak_alias (__pthread_return_0, pthread_rwlock_init)
+weak_alias (__pthread_return_0, pthread_rwlock_destroy)
+weak_alias (__pthread_return_0, pthread_rwlock_rdlock)
+weak_alias (__pthread_return_0, pthread_rwlock_wrlock)
+weak_alias (__pthread_return_0, pthread_rwlock_tryrdlock)
+weak_alias (__pthread_return_0, pthread_rwlock_trywrlock)
+weak_alias (__pthread_return_0, pthread_rwlock_unlock)
+weak_alias (__pthread_return_0, pthread_rwlockattr_init)
+weak_alias (__pthread_return_0, pthread_rwlockattr_destroy)
+weak_alias (__pthread_return_0, pthread_rwlockattr_setpshared)
+weak_alias (__pthread_return_0, pthread_rwlockattr_getpshared)
+
+
+/* Those are pthread functions which return 1 if successful. */
+weak_alias (__pthread_return_1, pthread_equal)
+
+/* pthread_exit () is a special case. */
+void weak_function 
+pthread_exit (void *retval)
+{
+  exit (EXIT_SUCCESS);
+}
+
+int
+__pthread_return_0 (void)
+{
+  return 0;
+}
+
+int
+__pthread_return_1 (void)
+{
+  return 1;
+}
+
+void
+__pthread_return_void (void)
+{
+}
index e4820e3..8b23a5f 100644 (file)
@@ -25,7 +25,7 @@ TOPDIR=../../../../
 include $(TOPDIR)Rules.mak
 
 CSRC=  waitpid.c kernel_version.c statfix.c getdnnm.c gethstnm.c \
-       mkfifo.c setegid.c wait.c errno.c getpagesize.c seteuid.c \
+       mkfifo.c setegid.c wait.c getpagesize.c seteuid.c \
        wait3.c setpgrp.c getdtablesize.c create_module.c ptrace.c \
        cmsg_nxthdr.c open64.c statfix64.c statfs64.c longjmp.c
 ifneq ($(strip $(EXCLUDE_BRK)),true)
diff --git a/libc/sysdeps/linux/common/errno.c b/libc/sysdeps/linux/common/errno.c
deleted file mode 100644 (file)
index 0d500dc..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-//#include <errno.h>
-
-int errno = 0;
-
-int * __attribute__ ((__weak__)) __errno_location ( void )
-{
-  return &errno;
-}
-
index 66414ec..4dad839 100644 (file)
@@ -1,7 +1,6 @@
-# Makefile for uClibc
+# Makefile for uClibc's pthread library
 #
-# Copyright (C) 2000 by by Lineo, inc. and Erik Andersen
-# Copyright (C) 2000, 2001 by Erik Andersen <andersee@debian.org>
+# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org>
 #
 # 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
 # You should have received a copy of the GNU Library 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
-#
-# Derived in part from the Linux-8086 C library, the GNU C Library, and several
-# other sundry sources.  Files within this library are copyright by their
-# respective copyright holders.
 
 TOPDIR=../
 include $(TOPDIR)Rules.mak
 
+#Adjust the soname version to avoid namespace collisions with glibc's libpthread
+PT_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
+
 LIBPTHREAD=libpthread.a
 LIBPTHREAD_SHARED=libpthread.so
-LIBPTHREAD_SHARED_FULLNAME=libpthread-$(MAJOR_VERSION).$(MINOR_VERSION).so
+LIBPTHREAD_SHARED_FULLNAME=libpthread-$(PT_VERSION).so
+
+LIBTHREAD_DB=libthread_db.a
+LIBTHREAD_DB_SHARED=libthread_db.so
+LIBTHREAD_DB_SHARED_FULLNAME=libthread_db-$(PT_VERSION).so
 
-CSRC = pthread.c
-OBJS=$(patsubst %.c,%.o, $(CSRC))
+DIRS=
+ifeq ($(strip $(INCLUDE_THREADS)),true)
+       DIRS+=linuxthreads
+ifeq ($(strip $(DODEBUG)),true)
+       DIRS+=linuxthreads_db
+endif
+endif
 
-all: $(OBJS) $(LIBPTHREAD)
+ALL_SUBDIRS = linuxthreads linuxthreads_db
 
-$(LIBPTHREAD): ar-target
+all: $(LIBPTHREAD) $(LIBTHREAD_DB)
+
+$(LIBPTHREAD): subdirs
+       @if [ -f $(LIBPTHREAD) ] ; then \
+               set -e; \
+               install -d $(TOPDIR)lib; \
+               rm -f $(TOPDIR)lib/$(LIBPTHREAD); \
+               install -m 644 $(LIBPTHREAD) $(TOPDIR)lib; \
+       fi;
+
+$(LIBTHREAD_DB): subdirs
+       @if [ -f $(LIBTHREAD_DB) ] ; then \
+               set -e; \
+               install -d $(TOPDIR)lib; \
+               rm -f $(TOPDIR)lib/$(LIBTHREAD_DB); \
+               install -m 644 $(LIBTHREAD_DB) $(TOPDIR)lib; \
+       fi;
 
-ar-target: $(OBJS)
-       $(AR) $(ARFLAGS) $(LIBPTHREAD) $(OBJS)
-       install -d $(TOPDIR)lib
-       rm -f $(TOPDIR)lib/$(LIBPTHREAD)
-       install -m 644 $(LIBPTHREAD) $(TOPDIR)lib
 
 $(OBJS): %.o : %.c
        $(CC) $(CFLAGS) -c $< -o $@
@@ -48,15 +66,53 @@ $(OBJS): %.o : %.c
 $(OBJ): Makefile
 
 shared: all
-       $(LD) $(LDFLAGS) -soname=$(LIBPTHREAD_SHARED).$(MAJOR_VERSION) \
-               -o $(LIBPTHREAD_SHARED_FULLNAME) --whole-archive $(LIBPTHREAD) \
-               --no-whole-archive -L$(TOPDIR)/lib -lc;
-       install -d $(TOPDIR)lib
-       rm -f $(TOPDIR)lib/$(LIBPTHREAD_SHARED_FULLNAME) $(TOPDIR)lib/$(LIBPTHREAD_SHARED).$(MAJOR_VERSION)
-       install -m 644 $(LIBPTHREAD_SHARED_FULLNAME) $(TOPDIR)lib;
-       (cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) $(LIBPTHREAD_SHARED)); 
-       (cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) $(LIBPTHREAD_SHARED).$(MAJOR_VERSION)); 
-
-clean: 
-       rm -f *.[oa] *~ core $(LIBPTHREAD_SHARED)* $(LIBPTHREAD_SHARED_FULLNAME)*
+       if [ -f $(LIBPTHREAD) ] ; then \
+               set -e; \
+               $(LD) $(LDFLAGS) -soname=$(LIBPTHREAD_SHARED).$(PT_VERSION) \
+                       -o $(LIBPTHREAD_SHARED_FULLNAME) --whole-archive $(LIBPTHREAD) \
+                       --no-whole-archive $(TOPDIR)/libc/misc/internals/interp.o \
+                       -L$(TOPDIR)/lib -lc; \
+               install -d $(TOPDIR)lib; \
+               rm -f $(TOPDIR)lib/$(LIBPTHREAD_SHARED_FULLNAME) \
+                       $(TOPDIR)lib/$(LIBPTHREAD_SHARED).$(PT_VERSION); \
+               install -m 644 $(LIBPTHREAD_SHARED_FULLNAME) $(TOPDIR)lib; \
+               (cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) \
+                       $(LIBPTHREAD_SHARED)); \
+               (cd $(TOPDIR)lib && ln -sf $(LIBPTHREAD_SHARED_FULLNAME) \
+                       $(LIBPTHREAD_SHARED).$(PT_VERSION)); \
+       fi;
+       if [ -f $(LIBTHREAD_DB) ] ; then \
+               set -e; \
+               $(LD) $(LDFLAGS) -soname=$(LIBTHREAD_DB_SHARED).$(PT_VERSION) \
+                       -o $(LIBTHREAD_DB_SHARED_FULLNAME) --whole-archive $(LIBTHREAD_DB) \
+                       --no-whole-archive $(TOPDIR)/libc/misc/internals/interp.o \
+                       -L$(TOPDIR)/lib -lc; \
+               install -d $(TOPDIR)lib; \
+               rm -f $(TOPDIR)lib/$(LIBTHREAD_DB_SHARED_FULLNAME) \
+                       $(TOPDIR)lib/$(LIBTHREAD_DB_SHARED).$(PT_VERSION); \
+               install -m 644 $(LIBTHREAD_DB_SHARED_FULLNAME) $(TOPDIR)lib; \
+               (cd $(TOPDIR)lib && ln -sf $(LIBTHREAD_DB_SHARED_FULLNAME) \
+                       $(LIBTHREAD_DB_SHARED)); \
+               (cd $(TOPDIR)lib && ln -sf $(LIBTHREAD_DB_SHARED_FULLNAME) \
+                       $(LIBTHREAD_DB_SHARED).$(PT_VERSION)); \
+       fi;
+
+tags:
+       ctags -R
+       
+subdirs: $(patsubst %, _dir_%, $(DIRS))
+subdirs_clean: $(patsubst %, _dirclean_%, $(ALL_SUBDIRS))
+
+$(patsubst %, _dir_%, $(DIRS)) : dummy
+       $(MAKE) -C $(patsubst _dir_%, %, $@)
+
+$(patsubst %, _dirclean_%, $(ALL_SUBDIRS)) : dummy
+       $(MAKE) -C $(patsubst _dirclean_%, %, $@) clean
+
+clean: subdirs_clean
+       rm -f *.[oa] *~ core $(LIBPTHREAD) $(LIBPTHREAD_SHARED_FULLNAME) \
+               $(LIBTHREAD_DB) $(LIBTHREAD_DB_SHARED_FULLNAME)
+
+.PHONY: dummy
+
 
diff --git a/libpthread/linuxthreads/ChangeLog b/libpthread/linuxthreads/ChangeLog
new file mode 100644 (file)
index 0000000..0025b87
--- /dev/null
@@ -0,0 +1,1257 @@
+2000-02-22  Ulrich Drepper  <drepper@redhat.com>
+
+       * semaphore.h (SEM_FAILED): Use 0 not NULL.
+
+2000-02-14  Ulrich Drepper  <drepper@redhat.com>
+
+       * condvar.c (pthread_cond_timedwait_relative_old): Tight loop with
+       nanosleep does not work either.  Get absolute time inside the
+       loop.
+       (pthread_cond_timedwait_relative_new): Likewise.
+       Patch by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+2000-02-13  Ulrich Drepper  <drepper@redhat.com>
+
+       * condvar.c (pthread_cond_timedwait_relative_old): Undo last patch
+       but keep the code around.  A bug in the kernel prevent us from
+       using the code.
+       (pthread_cond_timedwait_relative_new): Likewise.
+       (PR libc/1597 and libc/1598).
+
+2000-02-01  Kaz Kylheku  <kaz@ashi.footprints.net>
+
+       * condvar.c (pthread_cond_timedwait_relative_old): Do tight
+       loop around nanosleep calls instead of around most of the function
+       (pthread_cond_timedwait_relative_new): Likewise.
+       body.  Got rid of backwards goto and one local.
+
+2000-01-31  Ulrich Drepper  <drepper@redhat.com>
+
+       * condvar.c (pthread_cond_timedwait_relative_old): Recompute time
+       before every nanosleep call to account for time spent in the rest
+       of the function.
+       (pthread_cond_timedwait_relative_new): Likewise.
+       Patch by khendricks@ivey.uwo.ca (PR libc/1564).
+
+2000-01-29  Ulrich Drepper  <drepper@redhat.com>
+
+       * condvar.c (pthread_cond_timedwait_relative_old): Get remaining time
+       from nanosleep call so that in case we restart we only wait for the
+       remaining time.
+       (pthread_cond_timedwait_relative_new): Likewise.
+       Patch by khendricks@ivey.uwo.ca (PR libc/1561).
+
+2000-01-18  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_allocate_stack): Compute guard page address
+       correctly.  Patch by HJ Lu.
+
+2000-01-12  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h (pthread_readlock_info): New structure.
+       (_pthread_descr_struct): Add p_readlock_list, p_readlock_free, and
+       p_untracked_readlock_count.
+       * pthread.c (__pthread_initial_thread, pthread_manager_thread):
+       Add initializers for new fields.
+       * manager.c (pthread_free): Free read/write lock lists.
+       * queue.h (queue_is_empty): New function.
+       * rwlock.c: Implement requirements about when readers should get
+       locks assigned.
+       * sysdeps/pthread/pthread.h
+       (PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP): New definition.
+       * sysdeps/pthread/bits/pthreadtypes.h (struct _pthread_rwlock_t):
+       Define this name as well.
+       Patches by Kaz Kylheku <kaz@ashi.footprints.net>.
+
+2000-01-06  Andreas Jaeger  <aj@suse.de>
+
+       * pthread.c: Remove extra initializer.
+
+2000-01-05  Ulrich Drepper  <drepper@cygnus.com>
+
+       * pthread.c (__pthread_initial_thread, pthread_manager_thread):
+       Adjust initializers for struct _pthread_descr_struct change.
+       * internals.h (struct _pthread_descr_struct): Move new elements to
+       the end.
+
+2000-01-03  Kaz Kylheku  <kaz@ashi.footprints.net>
+
+       Redesigned how cancellation unblocks a thread from internal
+       cancellation points (sem_wait, pthread_join,
+       pthread_cond_{wait,timedwait}).
+       Cancellation won't eat a signal in any of these functions
+       (*required* by POSIX and Single Unix Spec!).
+       * condvar.c: Spontaneous wakeup on pthread_cond_timedwait won't eat a
+       simultaneous condition variable signal (not required by POSIX
+       or Single Unix Spec, but nice).
+       * spinlock.c: __pthread_lock queues back any received restarts
+       that don't belong to it instead of assuming ownership of lock
+       upon any restart; fastlock can no longer be acquired by two threads
+       simultaneously.
+       * restart.h: Restarts queue even on kernels that don't have
+       queued real time signals (2.0, early 2.1), thanks to atomic counter,
+       avoiding a rare race condition in pthread_cond_timedwait.
+
+1999-12-28  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/alpha/pt-machine.h:  Move stack_pointer definition to the
+       beginning.
+
+       * manager.c (__pthread_start): Add one more cast to assignment of
+       arg to prevent warning on 64bit machines.
+
+1999-12-21  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_handle_create): Set p_pid of new thread
+       before calling the callback function to report a new thread.
+
+1999-12-20  Andreas Jaeger  <aj@suse.de>
+
+       * pthread.c (pthread_initialize): Move getrlimit call after
+       setting of errno.
+
+1999-12-01  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/i386/pt-machine.h: Move stack_pointer definition to the
+       beginning.
+       * sysdeps/i386/i686/pt-machine.h: Likewise.
+       Patches by Alan Modra <alan@SPRI.Levels.UniSA.Edu.Au>.
+
+1999-11-23  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_start_thread_event): Initialize p_pid already
+       here.
+
+1999-11-22  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h: Add prototype for __pthread_manager_event.
+       * manager.c (__pthread_manager_event): New function.
+       (pthread_start_thread_event): Correct computation of self.
+       Use INIT_THREAD_SELF.
+       * pthread.c (__pthread_manager_thread): Initialize p_lock.
+       (__pthread_initialize_manager): Respect event flags also for creation
+       of the manager thread.
+
+1999-11-08  Ulrich Drepper  <drepper@cygnus.com>
+
+       * pthread.c (__pthread_initialize_manager): Initialize
+       __pthread_manager_thread.p_tid.
+
+1999-11-02  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h: Declare __pthread_last_event.
+       * manager.c: Define __pthread_last_event.
+       (pthread_handle_create): Set __pthread_last_event.
+       (pthread_exited): Likewise.
+       * join.c (pthread_exit): Likewise.
+
+       * Makefile (libpthread-routines): Add events.
+       * events.c: New file.
+       * internals.h: Protect against multiple inclusion.
+       Include thread_dbP.h header.
+       (struct _pthread_descr_struct): Add new fields p_report_events and
+       p_eventbuf.
+       Declare event reporting functions.
+       * join.c (pthread_exit): Signal event if this is wanted.
+       * manager.c (__pthread_threads_events): New variable.
+       (pthread_handle_create): Take new parameters with event information.
+       Signal TD_CREATE event if wanted.
+       (__pthread_manager): Adjust pthread_handle_create call.
+       (pthread_start_thread_event): New function.  Block until manager is
+       finished and then call pthread_start_thread.
+       (pthread_exited): Signal TD_REAP event if wanted.
+
+1999-10-26  Ulrich Drepper  <drepper@cygnus.com>
+
+       * restart.h (suspend_with_cancellation): Rewrite as a macro.
+
+       * condvar.c (pthread_cond_timedwait_relative): Don't mark as inline.
+
+1999-10-21  Xavier Leroy  <Xavier.Leroy@inria.fr>
+
+       * linuxthreads/pthread.c: For i386, wrap pthread_handle_sigrestart
+       and pthread_handle_sigcancel with functions that restore
+       %gs from the signal context.  For each signal handling function,
+       two wrappers are required, one for a non-RT signal and one for
+       a RT signal.
+       * linuxthreads/signal.c: For i386, add code to restore %gs
+       from the signal context in pthread_sighandler and
+       pthread_sighandler_rt.
+
+1999-10-09  Andreas Jaeger  <aj@suse.de>
+
+       * internals.h: Add __new_sem_post to get prototype in
+       manager.c; include semaphore.h for needed types.
+
+1999-10-08  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (__pthread_manager) [REQ_POST]: Use __new_sem_post
+       directly instead of calling sem_post which should not be necessary
+       but is faster and might help in some case to work around problems.
+
+1999-09-25  Ulrich Drepper  <drepper@cygnus.com>
+
+       * condvar.c (pthread_cond_timedwait_relative): Never return with
+       EINTR.  Patch by Andreas Schwab.
+
+1999-09-19  Ulrich Drepper  <drepper@cygnus.com>
+
+       * signals.c (sigaction): Correct last patch.  Don't select
+       pthread_sighandler_rt based on the signal number but instead of
+       the SA_SIGINFO flag.
+
+1999-09-23  Ulrich Drepper  <drepper@cygnus.com>
+
+       * specific.c: Move definitions of struct pthread_key_struct and
+       destr_function to ...
+       * internals.h: ...here.
+
+1999-09-03  Andreas Schwab  <schwab@suse.de>
+
+       * ptfork.c (__fork): Renamed from fork and use __libc_fork.  Add
+       fork as weak alias.
+       (__vfork): New function, alias vfork.
+       * Versions: Export __fork, vfork, and __vfork in libpthread.
+
+1999-08-23  Andreas Schwab  <schwab@suse.de>
+
+       * signals.c (pthread_sighandler): Add SIGCONTEXT_EXTRA_ARGS to
+       call to signal handler.
+
+1999-08-20  Ulrich Drepper  <drepper@cygnus.com>
+
+       * pthread.c (__pthread_reset_main_thread): Undo last change.
+       (__pthread_kill_other_threads_np): Reset signal handlers for the
+       signals we used in the thread implementation here.
+
+1999-08-19  Ulrich Drepper  <drepper@cygnus.com>
+
+       * pthread.c (__pthread_reset_main_thread): Reset signal handlers
+       for the signals we used in the thread implementation [PR libc/1234].
+
+       * Versions: Export __pthread_kill_other_threads_np from libpthread
+       for GLIBC_2.1.2.
+
+       * signals.c: Pass sigcontext through wrapper to the user function.
+
+1999-08-01  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Versions [ld.so] (GLIBC_2.0): Export __libc_internal_tsd_get and
+       __libc_internal_tsd_set.
+
+1999-07-29  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * manager.c: Remove inclusion of <linux/tasks.h> since it's not
+       needed anymore.
+
+1999-07-16  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * internals.h: Align _pthread_descr_struct to 32 bytes.
+       Reported by Tim Hockin <thockin@cobaltnet.com>, close PR libc/1206.
+
+1999-07-09  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_handle_create): Free mmap region after stack
+       if clone failed.  Patch by Kaz Kylheku <kaz@ashi.FootPrints.net>.
+
+1999-07-09  Cristian Gafton  <gafton@redhat.com>
+
+       * Makefile (libpthread-routines): Add oldsemaphore routine.
+       * Versions: Add sem_destroy, sem_getvalue, sem_init, sem_post,
+       sem_trywait, and sem_wait to GLIBC_2.1.
+       * oldsemaphore.c: New file.
+       * semaphore.c: Add default_symbol_versions for the changed functions.
+       (__new_sem_init): Rename from sem_init.
+       (__new_sem_post): Rename from sem_post.
+       (__new_sem_wait): Rename from sem_wait.
+       (__new_sem_trywait): Rename from sem_trywait.
+       (__new_sem_getvalue): Rename from sem_getvalue.
+       (__new_sem_destroy): Rename from sem_destroy.
+
+1999-06-23  Robey Pointer  <robey@netscape.com>
+
+       * internals.h: Added p_nextlock entry to separate queueing for a
+       lock from queueing for a CV (sometimes a thread queues on a lock
+       to serialize removing itself from a CV queue).
+       * pthread.c: Added p_nextlock to initializers.
+       * spinlock.c: Changed to use p_nextlock instead of p_nextwaiting.
+
+1999-05-23  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * man/pthread_cond_init.man: Correct example.
+       Reported by Tomas Berndtsson <tomas@nocrew.org>.
+
+       * linuxthreads.texi (Condition Variables): Likewise.
+
+1999-05-18  Jakub Jelinek  <jj@ultra.linux.cz>
+
+       * sysdeps/sparc/sparc64/pt-machine.h (__compare_and_swap): Use
+       casx not cas, also successful casx returns the old value in rd
+       and not the new value.
+
+1999-05-16  Xavier Leroy  <Xavier.Leroy@inria.fr>
+
+       * manager.c: If pthread_create() is given a NULL attribute
+       and the thread manager runs with a realtime policy, set the
+       scheduling policy of the newly created thread back to SCHED_OTHER.
+       * manager.c: If the PTHREAD_INHERIT_SCHED attribute is given,
+       initialize the schedpolicy field of new_thread->p_start_args
+       to that of the calling thread.
+
+1999-04-29  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/sparc/sparc64/pt-machine.h (__compare_and_swap): cas
+       instruction does not allow memory element to use offset.
+
+1999-04-28  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_allocate_stack): Optimize initialization of new
+       thread descriptor.
+
+       * sysdeps/pthread/bits/libc-lock.h (__libc_lock_define_initialized):
+       Don't use initializer since it is all zeroes.
+       (__libc_once_define): Likewise.
+
+1999-04-16  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * sysdeps/arm/Implies: Removed since cmpxchg/no-cmpxchg
+       doesn't exist anymore.
+       * sysdeps/i386/Implies: Likewise.
+       * sysdeps/m68k/Implies: Likewise.
+       * sysdeps/mips/Implies: Likewise.
+       * sysdeps/powerpc/Implies: Likewise.
+       * sysdeps/sparc/sparc32/Implies: Likewise.
+       * sysdeps/sparc/sparc64/Implies: Likewise.
+
+1999-04-15  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/alpha/bits/semaphore.h: Removed.
+       * sysdeps/powerpc/bits/semaphore.h: Removed.
+       * sysdeps/pthread/cmpxchg/bits/semaphore.h: Removed.
+       * sysdeps/pthread/no-cmpxchg/bits/semaphore.h: Removed.
+       * Makefile (headers): Remove bits/semaphore.h.
+
+       * semaphore.h: Define _pthread_descr if necessary.
+       Don't include limits.h.  Define SEM_VALUE_MAX directly.
+       Define SEM_FAILED.
+       (sem_t): Protect element names with leading __.
+       Add declarations for sem_close, sem_open, and sem_unlink.
+       * semaphore.c: Adjust all functions for new element names.
+       Define sem_close, sem_open, and sem_unlink.
+       * Versions (libthread): Add sem_close, sem_open, and sem_unlink for
+       GLIBC_2.1.1.
+       * sysdeps/pthread/bits/pthreadtypes.h: Define _pthread_descr only if
+       necessary.
+
+1999-03-16  H.J. Lu  <hjl@gnu.org>
+
+       * specific.c (pthread_key_delete): Check th->p_terminated to see
+       if the thread is running.
+
+       * Versions (__libc_internal_tsd_get, __libc_internal_tsd_set):
+       Added to GLIBC_2.0 for libc.so.
+
+1999-02-12  H.J. Lu  <hjl@gnu.org>
+
+       * Versions (__libc_current_sigrtmin, __libc_current_sigrtmax,
+       __libc_allocate_rtsig): Added to GLIBC_2.1.
+
+       * internals.h (DEFAULT_SIG_RESTART): Removed.
+       (DEFAULT_SIG_CANCEL): Removed.
+
+       * pthread.c (init_rtsigs, __libc_current_sigrtmin,
+       __libc_current_sigrtmax, __libc_allocate_rtsig): New functions.
+       (__pthread_sig_restart, __pthread_sig_cancel,
+       __pthread_sig_debug): Initialized.
+       (pthread_initialize): Call init_rtsigs () to initialize
+       real-time signals.
+
+1999-02-03  H.J. Lu  <hjl@gnu.org>
+
+       * manager.c (__pthread_manager): Do block __pthread_sig_debug.
+       Don't restart the thread which sent REQ_DEBUG.
+       (pthread_start_thread): Check if __pthread_sig_debug > 0
+       before debugging.
+
+       * pthread.c (__pthread_initialize_manager): Suspend ourself
+       after sending __pthread_sig_debug to gdb instead of
+       __pthread_sig_cancel.
+
+1999-01-24  H.J. Lu  <hjl@gnu.org>
+
+       * manager.c (__pthread_manager): Delete __pthread_sig_debug
+       from mask if __pthread_sig_debug > 0.
+       (pthread_handle_create): Increment __pthread_handles_num.
+
+       * manager.c (pthread_handle_create): Don't pass CLONE_PTRACE to clone.
+       * pthread.c (__pthread_initialize_manager): Likewise.
+
+       * pthread.c (pthread_initialize): Use __libc_allocate_rtsig (1)
+       instead of __libc_allocate_rtsig (2).
+       (__pthread_initialize_manager): Send __pthread_sig_debug to gdb
+       instead of __pthread_sig_cancel.
+       (pthread_handle_sigdebug): Fix comments.
+
+1999-01-21  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_allocate_stack): Set
+       __pthread_nonstandard_stacks if user-specified stack is used.
+
+1999-01-16  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/unix/sysv/linux/bits/posix_opt.h: Add _LFS_ASYNCHRONOUS_IO,
+       _LFS_LARGEFILE, _LFS64_LARGEFILE, and _LFS64_STDIO from Unix98.
+
+1999-01-07  Xavier Leroy  <Xavier.Leroy@inria.fr>
+
+       * pthread.c: Use a third signal __pthread_sig_debug distinct
+       from __pthread_sig_cancel to notify gdb when a thread is
+       created
+       * manager.c: Likewise.
+       * internals.h: Likewise.
+       * signals.c: The implementation of sigwait(s) assumed that
+       all signals in s have signal handlers already attached.
+       This is not required by the standard, so make it work
+       also if some of the signals have no handlers.
+
+1999-01-05  Andreas Schwab  <schwab@issan.cs.uni-dortmund.de>
+
+       * linuxthreads.texi: Remove pointers from first @node.  Move old
+       @node spec inside comment.
+
+1998-12-31  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/bits/stdio-lock.h: Define _IO_lock_lock and
+       _IO_lock_unlock.
+
+1998-12-29  Ulrich Drepper  <drepper@cygnus.com>
+
+       * semaphore.c (sem_trywait): Don't forget to unlock the semaphore
+       lock.  Patch by Bernd Schmidt <crux@pool.informatik.rwth-aachen.de>.
+
+1998-12-21  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c: Threads now send __pthread_sig_cancel on termination.
+       Change clone call and signal masks.
+       * thread.c (pthread_handle_sigrestart): Remove special code for
+       manager.
+       (pthread_handle_sigcancel): In manager thread call
+       __pthread_manager_sighandler.
+       * sysdeps/i386/pt-machine.h (__compare_and_swap): Add memory clobber.
+       * sysdeps/i386/i686/pt-machine.h: Likewise.
+       Patches by Xavier Leroy.
+
+1998-12-14  Ulrich Drepper  <drepper@cygnus.com>
+
+       * spinlock.c (__pthread_unlock): Don't crash if called for an
+       untaken mutex.  Reported by Ruslan V. Brushkoff <rus@Snif.Te.Net.UA>.
+
+       * Examples/ex6.c: Unbuffer stdout and reduce sleep time to reduce
+       overall runtime.
+
+1998-12-13  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Examples/ex3.c: Wait until all threads are started before
+       searching for the number to avoid race condition on very fast
+       systems.
+
+1998-12-08  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * sysdeps/pthread/pthread.h: Remove __pthread_setcanceltype
+       declaration since it's not needed.
+
+       * sysdeps/pthread/pthread.h: Move internal functions to ...
+       * internals.h: ...here.
+
+1998-12-02  H.J. Lu  <hjl@gnu.org>
+
+       * pthread.c (__pthread_sig_restart): Initiliaze to 0 if
+       SIGRTMIN is defined.
+       (__pthread_sig_cancel): Likewise.
+
+1998-12-01  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * wrapsyscall.c: Include <sys/mman.h> for msync,
+       <stdlib.h> for system and <termios.h> for tcdrain prototype.
+       Correct msync declaration.
+
+1998-11-29  Roland McGrath  <roland@baalperazim.frob.com>
+
+       * sysdeps/pthread/bits/libc-tsd.h (__libc_tsd_define, __libc_tsd_get,
+       __libc_tsd_set): New macros for new interface.
+       * no-tsd.c: New file, provide uninitialized defns of
+       __libc_internal_tsd_get and __libc_internal_tsd_set.
+       * Makefile (routines): Add no-tsd.
+
+1998-10-12  Roland McGrath  <roland@baalperazim.frob.com>
+
+       * internals.h: Include <bits/libc-tsd.h>, not <bits/libc-lock.h>.
+       * sysdeps/pthread/bits/libc-lock.h (__libc_internal_tsd_get,
+       __libc_internal_tsd_set): Move decls to ...
+       * sysdeps/pthread/bits/libc-tsd.h: New file for __libc_internal_tsd_*
+       declarations.
+
+       * sysdeps/pthread/bits/libc-lock.h (__libc_internal_tsd_get,
+       __libc_internal_tsd_set): Make these pointers to functions, not
+       functions; remove #pragma weak decls for them.
+       * specific.c (__libc_internal_tsd_get, __libc_internal_tsd_set):
+       Define static functions and initialized pointers to them.
+
+1998-11-18  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Makefile (CFLAGS-mutex.c): Define as -D__NO_WEAK_PTHREAD_ALIASES.
+       (CFLAGS-specific.c): Likewise.
+       (CFLAGS-pthread.c): Likewise.
+       (CFLAGS-ptfork.c): Likewise.
+       (CFLAGS-cancel.c): Likewise.
+       * sysdeps/pthread/bits/libc-lock.h: Don't mark __pthread_* functions
+       as weak references if __NO_WEAK_PTHREAD_ALIASES is defined.
+
+       * mutex.c (pthread_mutex_init): Define as strong symbol.
+       (pthread_mutex_destroy): Likewise.
+       (pthread_mutex_trylock): Likewise.
+       (pthread_mutex_lock): Likewise.
+       (pthread_mutex_unlock): Likewise.
+       (pthread_mutexattr_init): Likewise.
+       (pthread_mutexattr_destroy): Likewise.
+       (pthread_once): Likewise.
+       * ptfork.c (pthread_atfork): Likewise.
+       * specific.c (pthread_key_create): Likewise.
+       (pthread_setspecific): Likewise.
+       (pthread_getspecific): Likewise.
+
+1998-11-15  Andreas Schwab  <schwab@issan.cs.uni-dortmund.de>
+
+       * linuxthreads.texi: Fix punctuation after xref.
+
+1998-11-10  H.J. Lu  <hjl@gnu.org>
+
+       * sysdeps/unix/sysv/linux/bits/local_lim.h: Undefine NR_OPEN
+       if it is defined in <linux/limits.h>.
+
+1998-10-29 14:28  Ulrich Drepper  <drepper@cygnus.com>
+
+       * spinlock.h (__pthread_trylock): Define inline.
+       (__pthread_lock): Add extra parameter to declaration.  Declare
+       using internal_function.
+       (__pthread_unlock): Declare using internal_function.
+       * spinlock.c (__pthread_lock): Add new parameter.  Use it instead
+       of local variable self.  Avoid recomputing self.  Define using
+       internal_function.
+       (__pthread_trylock): Remove.
+       (__pthread_unlock): Define using internal_function.
+       * cancel.c: Adjust for __pthread_lock interface change.  Use already
+       computed self value is possible.
+       * condvar.c: Likewise.
+       * join.c: Likewise.
+       * manager.c: Likewise.
+       * mutex.c: Likewise.
+       * pthread.c: Likewise.
+       * rwlock.c: Likewise.
+       * semaphore.c: Likewise.
+       * signals.c: Likewise.
+
+1998-10-27 13:46  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/pthread.h (struct _pthread_cleanup_buffer): Prepend
+       __ to field names of the struct.
+       * sysdeps/pthread/bits/pthreadtypes.h (struct _pthread_fastlock):
+       Likewise.
+       (pthread_attr_t): Likewise.
+       (pthread_cond_t): Likewise.
+       (pthread_condattr_t): Likewise.
+       (pthread_mutex_t): Likewise.
+       (pthread_mutexattr_t): Likewise.
+       (pthread_rwlock_t): Likewise.
+       (pthread_rwlockattr_t): Likewise.
+       * attr.c: Adjust for pthread.h and pthreadtypes.h change.
+       * cancel.c: Likewise.
+       * condvar.c: Likewise.
+       * manager.c: Likewise.
+       * mutex.c: Likewise.
+       * pthread.c: Likewise.
+       * ptlongjmp.c: Likewise.
+       * rwlock.c: Likewise.
+       * spinlock.c: Likewise.
+
+1998-10-09  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/i386/pt-machine.h (get_eflags, set_eflags): Mark these
+       also with PT_EI.
+
+       * sysdeps/i386/i686/pt-machine.h: Remove unused inline
+       definitions.
+
+       * Makefile (libpthread-routines): Add pt-machine.
+       * pt-machine.c: New file.
+       * sysdeps/alpha/pt-machine.h: Define PT_EI as extern inline is not
+       yet defined.  Use PT_EI in extern inline definitions.
+       * sysdeps/arm/pt-machine.h: Likewise.
+       * sysdeps/i386/pt-machine.h: Likewise.
+       * sysdeps/i386/i686/pt-machine.h: Likewise.
+       * sysdeps/m68k/pt-machine.h: Likewise.
+       * sysdeps/mips/pt-machine.h: Likewise.
+       * sysdeps/powerpc/pt-machine.h: Likewise.
+       * sysdeps/sparc/sparc32/pt-machine.h: Likewise.
+       * sysdeps/sparc/sparc64/pt-machine.h: Likewise.
+
+1998-10-02  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * semaphore.h: Include <sys/types.h> so that _pthread_descr
+       is declared.
+
+1998-09-15  David S. Miller  <davem@pierdol.cobaltmicro.com>
+
+       * sysdeps/sparc/sparc32/pt-machine.h (INIT_THREAD_SELF): Add nr
+       argument.
+       * sysdeps/sparc/sparc64/pt-machine.h (INIT_THREAD_SELF): Likewise.
+
+1998-09-12 14:24 -0400  Zack Weinberg  <zack@rabi.phys.columbia.edu>
+
+       * linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h: Add
+       multiple inclusion guard.
+
+1998-09-02 11:08  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * signals.c (sigaction): Check that sig is less than NSIG to avoid
+       array index overflow.
+
+1998-09-06 10:56  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/semaphore.h: New file.
+
+1998-09-06 09:08  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/bits/libc-lock.h (enum __libc_tsd_key_t): Add
+       _LIBC_TSD_KEY_DL_ERROR.
+
+1998-08-31  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/i386/i686/pt-machine.h (testandset): Add memory clobber.
+       * sysdeps/i386/pt-machine.h: Likewise.
+       Suggested by Roland McGrath.
+
+1998-08-28 13:58  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h: Also define THREAD_GETMEM_NC and THREAD_SETMEM_NC to
+       access thread data with non-constant offsets.
+       * specific.c: Use THREAD_GETMEM_NC and THREAD_SETMEM_NC where
+       necessary.
+
+       * sysdeps/i386/useldt.h: Fix typo.  Add THREAD_GETMEM_NC and
+       THREAD_SETMEM_NC definitions.
+
+       * sysdeps/sparc/sparc32/pt-machine.h: Define THREAD_GETMEM_NC and
+       THREAD_SETMEM_NC.
+       * sysdeps/sparc/sparc64/pt-machine.h: Likewise.
+
+1998-08-26 15:46  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h: Define THREAD_GETMEM and THREAD_SETMEM to default if
+       not already defined.
+       (struct _pthread_descr_struct): Add p_self and p_nr field.
+       * manager.c (__pthread_handles): Define second element to point
+       to manager thread.
+       (__pthread_handles_num): Initialize to 2.
+       (__pthread_manager): Use INIT_THREAD_SELF with two arguments.
+       (pthread_start_thread): Likewise.
+       (pthread_handle_create): Start search for free slot at entry 2.
+       Initialize new fields p_self and p_nr.
+       Call __clone with CLONE_PTRACE if available.
+       (pthread_free): Call FREE_THREAD_SELF if available.
+       * pthread.c (__pthread_initial_thread): Initialize new fields.
+       (__pthread_manager_thread): Likewise.
+       (__pthread_initialize_manager): Call __clone with CLONE_PTRACE.
+
+       * cancel.c: Use THREAD_GETMEM and THREAD_SETMEM to access the
+       elements of the thread descriptor.
+       * condvar.c: Likewise.
+       * errno.c: Likewise.
+       * join.c: Likewise.
+       * manager.c: Likewise.
+       * pthread.c: Likewise.
+       * ptlongjmp.c: Likewise.
+       * semaphore.c: Likewise.
+       * signals.c: Likewise.
+       * specific.c: Likewise.
+       * spinlock.c: Likewise.
+
+       * sysdeps/alpha/pt-machine.h (INIT_THREAD_SELF): Add extra parameter.
+
+       * sysdeps/i386/useldt.h: New file.
+       * sysdeps/i386/i686/pt-machine.h: Show how to use this file.
+
+       * sysdeps/sparc/sparc32/pt-machine.h: Define THREAD_GETMEM and
+       THREAD_SETMEM using __thread_self.
+       * sysdeps/sparc/sparc64/pt-machine.h: Likewise.
+
+1998-08-24  Geoff Keating  <geoffk@ozemail.com.au>
+
+       * spinlock.c (__pthread_lock): Reset p_nextwaiting to NULL if it
+       turned out that we didn't need to queue after all.
+
+1998-08-22  Geoff Keating  <geoffk@ozemail.com.au>
+
+       * sysdeps/powerpc/pt-machine.h: Remove testandset, it's not used
+       and wastes space; correct types.
+
+1998-08-08 11:18  H.J. Lu  <hjl@gnu.org>
+
+       * signals.c (sigaction): Handle NULL argument.
+
+1998-08-04  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/unix/sysv/linux/bits/sigthread.h: Use __sigset_t instead
+       of sigset_t.
+
+1998-08-02  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * Makefile (linuxthreads-version): Extract correct number from
+       Banner.
+
+1998-07-29  Xavier Leroy  <Xavier.Leroy@inria.fr>
+
+       * Banner: Bump version number to 0.8
+       * FAQ.html: Many updates, in particular w.r.t. debugging.
+       * manager.c: Support for non-default stacksize for
+       LinuxThreads-allocated stacks;
+       don't use guard pages for stacks with default size, rely on
+       rlimit(RLIMIT_STACK) instead (it's cheaper).
+       * attr.c: Likewise.
+       * cancel.c: Use __pthread_sig_cancel and __pthread_sig_restart
+       everywhere instead of PTHREAD_SIG_CANCEL and PTHREAD_SIG_RESTART.
+       * condvar.c: Likewise.
+       * internals.h: Likewise.
+       * restart.h: Likewise.
+       * signals.c: Likewise.
+       * pthread.c: Likewise; set rlimit(RLIMIT_STACK) as we need it.
+
+1998-07-23  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * weaks.c: Define pthread_mutexattr_[sg]ettype instead of
+       __pthread_mutexattr_[sg]ettype.  Add more weak aliases.
+       * Versions: Put __pthread_mutexattr_settype under version
+       GLIBC_2.0.  Don't export __pthread_mutexattr_setkind_np and
+       __pthread_mutexattr_gettype.
+
+1998-07-23  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * sysdeps/pthread/bits/libc-lock.h: Make
+       __pthread_mutexattr_settype weak.  Don't make
+       __pthread_mutexattr_setkind_np weak.
+
+1998-07-16 10:52  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_handle_create): Check whether sched_setscheduler
+       call can succeed here.
+
+       * mutex.c: Define __pthread_mutexattr_settype and make
+       __pthread_mutexattr_setkind_np an alias.
+       Likewise for __pthread_mutexattr_gettype.
+
+1998-07-15 11:00 -0400  Zack Weinberg  <zack@rabi.phys.columbia.edu>
+
+       * attr.c (pthread_attr_setschedpolicy): Don't check whether caller
+       is root.
+
+1998-07-14 19:38  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/bits/libc-lock.h: Define __libc_cleanup_end.
+
+1998-07-11  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * Examples/ex6.c: Include <unistd.h> for usleep.
+
+1998-06-13 11:04  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * Examples/ex4.c (main): Use exit, not pthread_exit.
+
+1998-07-09 13:39  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Versions: Add __pthread_mutexattr_gettype and
+       __pthread_mutexattr_settype.
+       * lockfile.c: Use __pthread_mutexattr_settype instead of
+       __pthread_mutexattr_setkind_np.
+       * mutex.c: Define __pthread_mutexattr_gettype and
+       __pthread_mutexattr_settype.
+       * weak.c: Likewise.
+       * sysdeps/pthread/pthread.h: Declare __pthread_mutexattr_gettype and
+       __pthread_mutexattr_settype.
+       * sysdeps/pthread/bits/libc-lock.h (__libc_lock_init_recursive):
+       Use __pthread_mutexattr_settype.
+
+1998-07-08 22:26  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Versions: Add pthread_mutexattr_gettype, pthread_mutexattr_settype.
+       * mutex.c: Define weak alias pthread_mutexattr_gettype and
+       pthread_mutexattr_settype.
+       * sysdeps/pthread/pthread.h: Declare these functions.
+       Move pthread_sigmask and pthread_kill declaration in separate header.
+       * sysdeps/unix/sysv/linux/bits/sigthread.h: New file.
+
+1998-07-07 15:20  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Makefile: Add rules to compile and run tests.
+       * Examples/ex1.c: Little changes to fix warnings.
+       * Examples/ex2.c: Likewise.
+       * Examples/ex3.c: Likewise.
+       * Examples/ex4.c: Likewise.
+       * Examples/ex5.c: Likewise.
+       * Examples/ex6.c: New file.
+
+1998-07-05 11:54  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Versions: Add pthread_attr_init to GLIBC_2.1 version in libc.
+
+1998-07-01  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * attr.c: Include <string.h>.
+
+1998-06-30 11:47  Ulrich Drepper  <drepper@cygnus.com>
+
+       * attr.c: Include errno.h.  Use memcpy to copy sched_param.
+       * internals.h: Include limits.h.
+       * manager.c: Use memcpy to copy sched_param.
+       * ptfork.c: Include errno.h.
+       * pthread.c: Likewise.
+       * semaphore.c: Likewise.
+       * specific.c: Likewise.
+       * spinlock.h: Likewise.
+       * sysdeps/pthread/pthread.h: Include only allowed headers.  Move
+       type definition to ...
+       * sysdeps/pthread/bits/pthreadtypes.h: ...here.  New file.
+
+1998-06-29 12:34  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/pthread.h: Use __PMT not __P for function pointers.
+
+       * sysdeps/pthread/pthread.h: Define various PTHREAD_* symbols also
+       as macros as demanded in POSIX.1, Annex C.
+
+1998-06-29 12:29  Ulrich Drepper  <drepper@cygnus.com>
+
+       * internals.h (struct pthread_request): For free use pthread_t
+       instead of pthread_descr.
+       * join.c (pthread_join): Pass thread_id, not th to manager.
+       (pthread_detach): Likewise.
+       * manager.c (__pthread_manager): Except thread ID in FREE_REQ case.
+       (pthread_exited): Remove detached queue code.
+       (pthread_handle_free): Expect thread ID parameter and use it to
+       validate the thread decsriptor.  Don't use detached queue.
+       Patches by Xavier Leroy.
+
+1998-06-27  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * libpthread.map: Export accept, longjmp, sigaction, siglongjmp,
+       _IO_flockfile, _IO_ftrylockfile, _IO_funlockfile,
+       __pthread_atfork, __pthread_key_create, __pthread_once.
+       * internals.h: Doc fix.
+       * pthread.c (__pthread_initialize): Define again.
+
+1998-06-26  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_exited): If thread is not detached put it on
+       special list.
+       (pthread_handle_free): If thread is not on list with living threads
+       search on list with detached threads.
+
+       * sysdeps/pthread/pthread.h (PTHREAD_RWLOCK_INITIALIZER): Correct
+       for new definition of pthread_rwlock_t.
+
+       * spinlock.c: Correct test whether to compile
+       __pthread_compare_and_swap or not.
+
+1998-06-25 19:27  Ulrich Drepper  <drepper@cygnus.com>
+
+       * attr.c: Finish user stack support.  Change locking code to be safe
+       in situations with different priorities.
+       * cancel.c: Likewise.
+       * condvar.c: Likewise.
+       * internals.h: Likewise.
+       * join.c: Likewise.
+       * manager.c: Likewise.
+       * mutex.c: Likewise.
+       * pthread.c: Likewise.
+       * ptlongjmp.c: Likewise.
+       * queue.h: Likewise.
+       * rwlock.c: Likewise.
+       * semaphore.c: Likewise.
+       * semaphore.h: Likewise.
+       * signals.c: Likewise.
+       * spinlock.c: Likewise.
+       * spinlock.h: Likewise.
+       * sysdeps/pthread/pthread.h: Likewise.
+       Patches by Xavier Leroy.
+
+       * sysdeps/i386/i686/pt-machine.h: New file.
+
+1998-06-25  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/pthread.h: Make [sg]et_stacksize and
+       [sg]et_stackaddr prototypes always available.
+
+       * sysdeps/unix/sysv/linux/bits/posix_opt.h: Define
+       _POSIX_THREAD_ATTR_STACKSIZE and _POSIX_THREAD_ATTR_STACKADDR.
+
+1998-06-24  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_free): Undo patch from 980430.
+       Reported by David Wragg <dpw@doc.ic.ac.uk>.
+
+1998-06-09 15:07  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c: Define __pthread_manager_adjust_prio and use it to
+       increase priority when needed.
+       * internals.h: Add prototype for __pthread_manager_adjust_prio.
+       * mutex.c: Optimize mutexes to wake up only one thread.
+       * pthread.c: Move PID of manager for global variable in structure
+       element.
+       Patches by Xavier Leroy.
+
+1998-06-07 13:47  Ulrich Drepper  <drepper@cygnus.com>
+
+       * sysdeps/pthread/bits/libc-lock.h: Optimize cleanup handlers a bit.
+
+1998-06-03  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
+
+       * attr.c: Correct typo.
+
+1998-05-01  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_free): Unmap guard before the stack.
+       Patch by Matthias Urlichs.
+
+1998-04-30  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c (pthread_free): Detect already free child.
+       Patch by Xavier Leroy, reported by Matthias Urlichs.
+
+1998-04-23  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * Makefile (linuxthreads-version): Renamed back from
+       libpthread-version.
+
+1998-04-21  Ulrich Drepper  <drepper@cygnus.com>
+
+       * ptlongjmp.c: Add prototypes for __libc_siglongjmp and
+       __libc_longjmp.
+
+1998-04-20 14:55  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Makefile (libpthread-routines): Add ptlongjmp and spinlock.
+       * internals.h: Add definitions for new spinlock implementation.
+       * ptlongjmp.c: New file.
+       * spinlock.c: New file.
+       * spinlock.h (acquire): Don't reschedule using __sched_yield, use
+       new function __pthread_acquire to prevent deadlocks with thread
+       with different priorities.
+       Patches by Xavier Leroy <Xavier.Leroy@inria.fr>.
+
+1998-03-16  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * manager.c (__pthread_manager): Reduce first argument to select
+       to include just the needed file descriptor.
+
+1998-03-17 00:06  Ulrich Drepper  <drepper@cygnus.com>
+
+       * manager.c: Fix last patch which caused core dumps.
+
+       * pthread.c: Correctly handle missing SIGRTMIN.
+
+1998-03-15  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>
+
+       * libpthread.map: Add __libc_internal_tsd_get and
+       __libc_internal_tsd_set.  Add missing cancelable functions. Export
+       libc internal versions of the cancelable functions.
+
+1998-03-13 16:51  Ulrich Drepper  <drepper@cygnus.com>
+
+       * weaks.c: Define pthread_attr_init as GLIBC_2.0 and GLIBC_2.1.
+
+1998-03-13 00:46  Ulrich Drepper  <drepper@cygnus.com>
+
+       * attr.c: Implement pthread_attr_[gs]etguardsize,
+       pthread_attr_[gs]setstackaddr, pthread_attr_[gs]etstacksize.
+       Change pthread_attr_init to have two interfaces.
+       * internals.h (struct _pthread_descr_struct): Add new fields for
+       above functions.
+       * libpthread.map: Add names in GLIBC_2.1 section.
+       * manager.c (pthread_handle_create): Implement guardsize and
+       user stack.
+       (pthread_free): Likewise.
+       * pthread.c (pthread_create): Add new interface for changed
+       pthread_attr_t.
+       * sysdeps/pthread/pthread.h: Add prototypes for new functions.
+       * sysdeps/unix/sysv/linux/bits/local_lim.h: Add definition of
+       PTHREAD_STACK_MIN.
+
+1998-03-11 00:42  Wolfram Gloger  <wmglo@dent.med.uni-muenchen.de>
+
+       * 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/libpthread/linuxthreads/Changes b/libpthread/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/libpthread/linuxthreads/FAQ.html b/libpthread/linuxthreads/FAQ.html
new file mode 100644 (file)
index 0000000..21be33e
--- /dev/null
@@ -0,0 +1,1039 @@
+<HTML>
+<HEAD>
+<TITLE>LinuxThreads Frequently Asked Questions</TITLE>
+</HEAD>
+<BODY>
+<H1 ALIGN=center>LinuxThreads Frequently Asked Questions <BR>
+                 (with answers)</H1>
+<H2 ALIGN=center>[For LinuxThreads version 0.8]</H2>
+
+<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="C">C</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>
+
+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,
+as well as a number of optional extensions, are provided and conform
+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 used to be some rough edges in
+the LinuxThreads / C library interface with libc 5, but glibc 2
+fixes all of those problems and is now the standard C library on major
+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>
+
+<H4><A NAME="B.4">B.4: How should I report a possible bug in
+LinuxThreads?</A></H4>
+
+If you're using glibc 2, the best way by far is to use the
+<code>glibcbug</code> script to mail a bug report to the glibc
+maintainers. <P>
+
+If you're using an older libc, or don't have the <code>glibcbug</code>
+script on your machine, then e-mail me directly
+(<code>Xavier.Leroy@inria.fr</code>).  <P>
+
+In both cases, before sending the bug report, make sure that it is not 
+addressed already in this FAQ.  Also, try to send a short program that
+reproduces the weird behavior you observed. <P>
+
+<H4><A NAME="B.5">B.5: 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>
+
+The best choice by far is glibc 2, a.k.a. libc 6.  It offers very good
+support for multi-threading, and LinuxThreads has been closely
+integrated with glibc 2.  The glibc 2 distribution contains the
+sources of a specially adapted version of LinuxThreads.<P>
+
+glibc 2 comes preinstalled as the default C library on several Linux
+distributions, such as RedHat 5 and up, and Debian 2.
+Those distributions include the version of LinuxThreads matching
+glibc 2.<P>
+
+<H4><A NAME="C.2">C.2: My system has libc 5 preinstalled, not glibc
+2.  Can I still use LinuxThreads?</H4>
+
+Yes, but you're likely to run into some problems, as libc 5 only
+offers minimal support for threads and contains some bugs that affect
+multithreaded programs. <P>
+
+The versions of libc 5 that work best with LinuxThreads are
+libc 5.2.18 on the one hand, and libc 5.4.12 or later on the other hand.
+Avoid 5.3.12 and 5.4.7: these have problems with the per-thread errno
+variable. <P>
+
+<H4><A NAME="C.3">C.3: So, should I switch to glibc 2, or stay with a
+recent libc 5?</A></H4>
+
+I'd recommend you switch to glibc 2.  Even for single-threaded
+programs, glibc 2 is more solid and more standard-conformant than libc
+5.  And the shortcomings of libc 5 almost preclude any serious
+multi-threaded programming.<P>
+
+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.  Much easier is (re-)installing a
+Linux distribution based on glibc 2, such as RedHat 6.<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>
+
+<H4><A NAME="C.5">C.5: Where can I find libc 5 and the version of
+LinuxThreads that goes with it?</A></H4>
+
+For libc 5, see <A HREF="ftp://sunsite.unc.edu/pub/Linux/devel/GCC/"><code>ftp://sunsite.unc.edu/pub/Linux/devel/GCC/</code></A>.<P>
+
+For the libc 5 version of LinuxThreads, see
+<A HREF="ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads/">ftp://ftp.inria.fr/INRIA/Projects/cristal/Xavier.Leroy/linuxthreads/</A>.<P>
+
+<H4><A NAME="C.6">C.6: How can I recompile the glibc 2 version of the
+LinuxThreads sources?</A></H4>
+
+You must transfer the whole glibc sources, then drop the LinuxThreads
+sources in the <code>linuxthreads/</code> subdirectory, then recompile
+glibc as a whole.  There are now too many inter-dependencies between
+LinuxThreads and glibc 2 to allow separate re-compilation of LinuxThreads.
+<P>
+
+<H4><A NAME="C.7">C.7: What is the correspondence between LinuxThreads 
+version numbers, libc version numbers, and RedHat version
+numbers?</A></H4>
+
+Here is a summary. (Information on Linux distributions other than
+RedHat are welcome.)<P>
+
+<TABLE>
+<TR><TD>LinuxThreads </TD> <TD>C library</TD> <TD>RedHat</TD></TR>
+<TR><TD>0.7, 0.71 (for libc 5)</TD> <TD>libc 5.x</TD> <TD>RH 4.2</TD></TR>
+<TR><TD>0.7, 0.71 (for glibc 2)</TD> <TD>glibc 2.0.x</TD> <TD>RH 5.x</TD></TR>
+<TR><TD>0.8</TD> <TD>glibc 2.1.1</TD> <TD>RH 6.0</TD></TR>
+<TR><TD>0.8</TD> <TD>glibc 2.1.2</TD> <TD>not yet released</TD></TR>
+</TABLE>
+<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 libc 5 library. (See question <A
+HREF="#C.2">C.2</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.2">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 creates a lot of threads, and after
+a while <CODE>pthread_create()</CODE> no longer returns!</A></H4>
+
+This is known bug in the version of LinuxThreads that comes with glibc
+2.1.1.  An upgrade to 2.1.2 is recommended. <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>
+
+That behavior has mostly disappeared in recent releases of
+LinuxThreads (version 0.8 and up).  It was fairly common in older
+releases, though.
+
+What happens in LinuxThreads 0.7 and before 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>
+
+In LinuxThreads 0.8 and up, <code>pthread_unlock</code> restarts only
+one waiting thread, and pre-assign the mutex to that thread.  Hence,
+if the thread that unlocked the mutex tries to lock it again
+immediately, it will block until other waiting threads have had a
+chance to lock and unlock the mutex.  This results in much fairer
+scheduling.<P>
+
+Notice however that even the old "unfair" behavior is perfectly
+acceptable 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".  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>
+
+Again, this behavior is characteristic of old releases of LinuxThreads
+(0.7 and before); more recent versions (0.8 and up) should not exhibit
+this behavior.<P>
+
+The reason for this behavior 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>
+
+<H4><A NAME="D.9">D.9: I tried to use real-time threads and my program
+loops like crazy and freezes the whole machine!</A></H4>
+
+Versions of LinuxThreads prior to 0.8 are susceptible to ``livelocks''
+(one thread loops, consuming 100% of the CPU time) in conjunction with
+real-time scheduling.  Since real-time threads and processes have
+higher priority than normal Linux processes, all other processes on
+the machine, including the shell, the X server, etc, cannot run and
+the machine appears frozen.<P>
+
+The problem is fixed in LinuxThreads 0.8.<P>
+
+<H4><A NAME="D.10">D.10: My application needs to create thousands of
+threads, or maybe even more.  Can I do this with
+LinuxThreads?</A></H4>
+
+No.  You're going to run into several hard limits:
+<UL>
+<LI>Each thread, from the kernel's standpoint, is one process.  Stock
+Linux kernels are limited to at most 512 processes for the super-user,
+and half this number for regular users.  This can be changed by
+changing <code>NR_TASKS</code> in <code>include/linux/tasks.h</code>
+and recompiling the kernel.  On the x86 processors at least,
+architectural constraints seem to limit <code>NR_TASKS</code> to 4090
+at most.
+<LI>LinuxThreads contains a table of all active threads.  This table
+has room for 1024 threads at most.  To increase this limit, you must
+change <code>PTHREAD_THREADS_MAX</code> in the LinuxThreads sources
+and recompile.
+<LI>By default, each thread reserves 2M of virtual memory space for
+its stack.  This space is just reserved; actual memory is allocated
+for the stack on demand.  But still, on a 32-bit processor, the total
+virtual memory space available for the stacks is on the order of 1G,
+meaning that more than 500 threads will have a hard time fitting in.
+You can overcome this limitation by moving to a 64-bit platform, or by
+allocating smaller stacks yourself using the <code>setstackaddr</code>
+attribute.
+<LI>Finally, the Linux kernel contains many algorithms that run in
+time proportional to the number of process table entries.  Increasing
+this number drastically will slow down the kernel operations
+noticeably.
+</UL>
+(Other POSIX threads libraries have similar limitations, by the way.)
+For all those reasons, you'd better restructure your application so
+that it doesn't need more than, say, 100 threads.  For instance,
+in the case of a multithreaded server, instead of creating a new
+thread for each connection, maintain a fixed-size pool of worker
+threads that pick incoming connection requests from a queue.<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: Does LinuxThreads implement
+<CODE>pthread_attr_setstacksize()</CODE> and
+<CODE>pthread_attr_setstackaddr()</CODE>?</A></H4>
+
+These optional functions are provided in recent versions of
+LinuxThreads (0.8 and up).  Earlier releases did not provide these
+optional components of the POSIX standard.<P>
+
+Even if <CODE>pthread_attr_setstacksize()</CODE> and
+<CODE>pthread_attr_setstackaddr()</CODE> are now provided, we still
+recommend that you do not use them unless you really have strong
+reasons for doing so.  The default stack allocation strategy for
+LinuxThreads is nearly optimal: stacks start small (4k) and
+automatically grow on demand to a fairly large limit (2M).
+Moreover, there is no portable way to estimate the stack requirements
+of a thread, so setting the stack size yourself makes your program
+less reliable and non-portable.<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>
+
+Yes, but not with the stock gdb 4.17.  You need a specially patched
+version of gdb 4.17 developed by Eric Paire and colleages at The Open
+Group, Grenoble.  The patches against gdb 4.17 are available at
+<A HREF="http://www.gr.opengroup.org/java/jdk/linux/debug.htm"><code>http://www.gr.opengroup.org/java/jdk/linux/debug.htm</code></A>.
+Precompiled binaries of the patched gdb are available in RedHat's RPM
+format at <A
+HREF="http://odin.appliedtheory.com/"><code>http://odin.appliedtheory.com/</code></A>.<P>
+
+Some Linux distributions provide an already-patched version of gdb;
+others don't.  For instance, the gdb in RedHat 5.2 is thread-aware,
+but apparently not the one in RedHat 6.0.  Just ask (politely) the
+makers of your Linux distributions to please make sure that they apply
+the correct patches to gdb.<P>
+
+<H4><A NAME="G.2">G.2: Does it work with post-mortem debugging?</A></H4>
+
+Not very well.  Generally, 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.3">G.3: Any other ways to 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 subtle 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>
+
+The short answer is: because the Linux kernel you're using does not
+support realtime signals.  <P>
+
+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.<P>
+
+On ``old'' kernels (2.0 and early 2.1 kernels), there are only 32
+signals available and the kernel reserves all of them but two:
+<code>SIGUSR1</code> and <code>SIGUSR2</code>.  So, LinuxThreads has
+no choice but use those two signals.<P>
+
+On recent kernels (2.2 and up), more than 32 signals are provided in
+the form of realtime signals. When run on one of those kernels,
+LinuxThreads uses two reserved realtime signals for its internal
+operation, thus leaving <code>SIGUSR1</code> and <code>SIGUSR2</code>
+free for user code.  (This works only with glibc, not with libc 5.) <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 use X libraries that have been compiled with
+multithreading options set.  Linux distributions that come with glibc
+2 as the main C library generally provide thread-safe X libraries.
+At least, that seems to be the case for RedHat 5 and later.<P>
+
+You can try to recompile yourself the X libraries with multithreading
+options set.  They contain optional support for multithreading; it's
+just that the binaries provided by your Linux distribution 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 consider installing a Linux distribution
+that comes with thread-safe X libraries, such as RedHat 6.<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>.  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>
+
+<HR>
+<ADDRESS>Xavier.Leroy@inria.fr</ADDRESS>
+</BODY>
+</HTML>
diff --git a/libpthread/linuxthreads/LICENSE b/libpthread/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/libpthread/linuxthreads/Makefile b/libpthread/linuxthreads/Makefile
new file mode 100644 (file)
index 0000000..eba08a2
--- /dev/null
@@ -0,0 +1,59 @@
+# Makefile for uClibc's pthread library
+#
+# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org>
+#
+# 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.
+#
+# You should have received a copy of the GNU Library 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
+# Makefile for uClibc
+
+TOPDIR=../../
+include $(TOPDIR)Rules.mak
+
+#Adjust the soname version to avoid namespace collisions with glibc's libpthread
+PT_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
+LIBPTHREAD=../libpthread.a
+
+# set up system dependencies include dirs (NOTE: order matters!)
+PTDIR = $(TOPDIR)libpthread/linuxthreads/
+SYSDEPINC = -I$(PTDIR)sysdeps/unix/sysv/linux \
+            -I$(PTDIR)sysdeps/pthread \
+            -I$(PTDIR)sysdeps/unix/sysv \
+            -I$(PTDIR)sysdeps/unix/unix \
+            -I$(PTDIR)sysdeps/$(TARGET_ARCH) \
+            -I$(PTDIR)sysdeps \
+            -I$(TOPDIR)libc/sysdeps/linux/$(TARGET_ARCH)
+CFLAGS += $(SYSDEPINC) -DLIBPTHREAD_SO="\"libpthread.so.$(PT_VERSION)\"" -D_GNU_SOURCE
+
+CSRC=attr.c cancel.c condvar.c errno.c events.c join.c lockfile.c manager.c \
+       mutex.c oldsemaphore.c pt-machine.c ptfork.c pthread.c \
+       ptlongjmp.c rwlock.c semaphore.c signals.c specific.c spinlock.c \
+       wrapsyscall.c #weaks.c no-tsd.c
+COBJS=$(patsubst %.c,%.o, $(CSRC))
+OBJS=$(COBJS)
+
+all: $(OBJS) $(LIBPTHREAD)
+
+$(LIBPTHREAD): ar-target
+
+ar-target: $(OBJS)
+       $(AR) $(ARFLAGS) $(LIBPTHREAD) $(OBJS)
+
+$(COBJS): %.o : %.c
+       $(CC) $(CFLAGS) -c $< -o $@
+       $(STRIPTOOL) -x -R .note -R .comment $*.o
+
+clean:
+       rm -f *.[oa] *~ core
+
+
diff --git a/libpthread/linuxthreads/README b/libpthread/linuxthreads/README
new file mode 100644 (file)
index 0000000..955bd59
--- /dev/null
@@ -0,0 +1,166 @@
+        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 ftp.gnu.org:/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.
+
+  *** This is not anymore true when the application runs on a kernel
+      newer than approximately 2.1.60.
+
+- 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/libpthread/linuxthreads/README.Xfree3.2 b/libpthread/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/libpthread/linuxthreads/Versions b/libpthread/linuxthreads/Versions
new file mode 100644 (file)
index 0000000..c0ec792
--- /dev/null
@@ -0,0 +1,121 @@
+libc {
+  GLIBC_2.0 {
+    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_cond_broadcast; pthread_cond_destroy;
+    pthread_cond_init; pthread_cond_signal; pthread_cond_wait;
+    pthread_condattr_destroy; pthread_condattr_init; pthread_equal;
+    pthread_exit; pthread_getschedparam; pthread_mutex_destroy;
+    pthread_mutex_init; pthread_mutex_lock; pthread_mutex_unlock;
+    pthread_mutexattr_getkind_np; pthread_mutexattr_setkind_np;
+    pthread_self; pthread_setcancelstate; pthread_setcanceltype;
+    pthread_setschedparam;
+
+    # Internal libc interface to libpthread
+    __libc_internal_tsd_get; __libc_internal_tsd_set;
+  }
+  GLIBC_2.1 {
+    pthread_attr_init;
+  }
+}
+
+ld.so {
+  GLIBC_2.0 {
+    # Internal libc interface to libpthread
+    __libc_internal_tsd_get; __libc_internal_tsd_set;
+  }
+}
+
+libpthread {
+  GLIBC_2.0 {
+    # Hidden entry point (through macros).
+    _pthread_cleanup_pop; _pthread_cleanup_pop_restore; _pthread_cleanup_push;
+    _pthread_cleanup_push_defer;
+
+    # Internal libc interface to libpthread
+    __libc_internal_tsd_get; __libc_internal_tsd_set;
+
+    # Overwritten libc functions.
+    accept; close; connect; fcntl; fork; fsync; longjmp; lseek; msync;
+    nanosleep; open; pause; raise; read; recv; recvfrom; recvmsg; send;
+    sendmsg; sendto; sigaction; siglongjmp; system; tcdrain; wait;
+    waitpid; write;
+    __close; __connect; __fcntl; __lseek; __open; __read; __send; __wait;
+    __write;
+    _IO_flockfile; _IO_ftrylockfile; _IO_funlockfile;
+    vfork; __fork;
+
+    # 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_atfork; __pthread_initialize; __pthread_getspecific;
+    __pthread_key_create; __pthread_mutex_destroy; __pthread_mutex_init;
+    __pthread_mutex_lock; __pthread_mutex_trylock; __pthread_mutex_unlock;
+    __pthread_mutexattr_destroy; __pthread_mutexattr_init;
+    __pthread_mutexattr_settype; __pthread_once; __pthread_setspecific;
+
+    # The error functions.
+    __errno_location; __h_errno_location;
+  }
+  GLIBC_2.1 {
+    # Functions with changed interface.
+    pthread_attr_init; pthread_create;
+
+    # 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;
+
+    pthread_attr_getguardsize; pthread_attr_setguardsize;
+    pthread_attr_getstackaddr; pthread_attr_setstackaddr;
+    pthread_attr_getstacksize; pthread_attr_setstacksize;
+
+    pthread_getconcurrency; pthread_setconcurrency;
+
+    pthread_mutexattr_gettype; pthread_mutexattr_settype;
+
+    sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait;
+
+    # helper functions
+    __libc_current_sigrtmin; __libc_current_sigrtmax;
+    __libc_allocate_rtsig;
+  }
+  GLIBC_2.1.1 {
+    sem_close; sem_open; sem_unlink;
+  }
+  GLIBC_2.1.2 {
+    __pthread_kill_other_threads_np;
+    __vfork;
+  }
+}
diff --git a/libpthread/linuxthreads/attr.c b/libpthread/linuxthreads/attr.c
new file mode 100644 (file)
index 0000000..2907a56
--- /dev/null
@@ -0,0 +1,214 @@
+/* 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.                 */
+
+/* changed for uClibc */
+#define __sched_get_priority_min sched_get_priority_min
+#define __sched_get_priority_max sched_get_priority_max
+
+/* Handling of thread attributes */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include "pthread.h"
+#include "internals.h"
+
+extern int __getpagesize(void);
+
+/* NOTE: With uClibc I don't think we need this versioning stuff.
+ * Therefore, define the function pthread_attr_init() here using
+ * a strong symbol. */
+
+//int __pthread_attr_init_2_1(pthread_attr_t *attr)
+int pthread_attr_init(pthread_attr_t *attr)
+{
+  size_t ps = __getpagesize ();
+
+  attr->__detachstate = PTHREAD_CREATE_JOINABLE;
+  attr->__schedpolicy = SCHED_OTHER;
+  attr->__schedparam.sched_priority = 0;
+  attr->__inheritsched = PTHREAD_EXPLICIT_SCHED;
+  attr->__scope = PTHREAD_SCOPE_SYSTEM;
+  attr->__guardsize = ps;
+  attr->__stackaddr = NULL;
+  attr->__stackaddr_set = 0;
+  attr->__stacksize = STACK_SIZE - ps;
+  return 0;
+}
+
+/* uClibc: leave out this for now. */
+#if DO_PTHREAD_VERSIONING_WITH_UCLIBC
+#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING
+default_symbol_version (__pthread_attr_init_2_1, pthread_attr_init, GLIBC_2.1);
+
+int __pthread_attr_init_2_0(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;
+}
+symbol_version (__pthread_attr_init_2_0, pthread_attr_init, GLIBC_2.0);
+#else
+strong_alias (__pthread_attr_init_2_1, pthread_attr_init)
+#endif
+#endif /* DO_PTHREAD_VERSIONING_WITH_UCLIBC */
+
+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;
+  memcpy (&attr->__schedparam, param, sizeof (struct sched_param));
+  return 0;
+}
+
+int pthread_attr_getschedparam(const pthread_attr_t *attr,
+                               struct sched_param *param)
+{
+  memcpy (param, &attr->__schedparam, sizeof (struct sched_param));
+  return 0;
+}
+
+int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
+{
+  if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR)
+    return EINVAL;
+  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;
+}
+
+int __pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
+{
+  size_t ps = __getpagesize ();
+
+  /* First round up the guard size.  */
+  guardsize = roundup (guardsize, ps);
+
+  /* The guard size must not be larger than the stack itself */
+  if (guardsize >= attr->__stacksize) return EINVAL;
+
+  attr->__guardsize = guardsize;
+
+  return 0;
+}
+weak_alias (__pthread_attr_setguardsize, pthread_attr_setguardsize)
+
+int __pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
+{
+  *guardsize = attr->__guardsize;
+  return 0;
+}
+weak_alias (__pthread_attr_getguardsize, pthread_attr_getguardsize)
+
+int __pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr)
+{
+  attr->__stackaddr = stackaddr;
+  attr->__stackaddr_set = 1;
+  return 0;
+}
+weak_alias (__pthread_attr_setstackaddr, pthread_attr_setstackaddr)
+
+int __pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr)
+{
+  /* XXX This function has a stupid definition.  The standard specifies
+     no error value but what is if no stack address was set?  We simply
+     return the value we have in the member.  */
+  *stackaddr = attr->__stackaddr;
+  return 0;
+}
+weak_alias (__pthread_attr_getstackaddr, pthread_attr_getstackaddr)
+
+int __pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
+{
+  /* We don't accept value smaller than PTHREAD_STACK_MIN.  */
+  if (stacksize < PTHREAD_STACK_MIN)
+    return EINVAL;
+
+  attr->__stacksize = stacksize;
+  return 0;
+}
+weak_alias (__pthread_attr_setstacksize, pthread_attr_setstacksize)
+
+int __pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
+{
+  *stacksize = attr->__stacksize;
+  return 0;
+}
+weak_alias (__pthread_attr_getstacksize, pthread_attr_getstacksize)
diff --git a/libpthread/linuxthreads/cancel.c b/libpthread/linuxthreads/cancel.c
new file mode 100644 (file)
index 0000000..8fd8c1e
--- /dev/null
@@ -0,0 +1,171 @@
+/* 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 = THREAD_GETMEM(self, p_cancelstate);
+  THREAD_SETMEM(self, p_cancelstate, state);
+  if (THREAD_GETMEM(self, p_canceled) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
+      THREAD_GETMEM(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 = THREAD_GETMEM(self, p_canceltype);
+  THREAD_SETMEM(self, p_canceltype, type);
+  if (THREAD_GETMEM(self, p_canceled) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
+      THREAD_GETMEM(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;
+  int dorestart = 0;
+  pthread_descr th;
+  pthread_extricate_if *pextricate;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, thread)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+
+  th = handle->h_descr;
+
+  if (th->p_canceled) {
+    __pthread_unlock(&handle->h_lock);
+    return 0;
+  }
+
+  pextricate = th->p_extricate;
+  th->p_canceled = 1;
+  pid = th->p_pid;
+
+  /* If the thread has registered an extrication interface, then
+     invoke the interface. If it returns 1, then we succeeded in
+     dequeuing the thread from whatever waiting object it was enqueued
+     with. In that case, it is our responsibility to wake it up. 
+     And also to set the p_woken_by_cancel flag so the woken thread
+     can tell that it was woken by cancellation. */
+
+  if (pextricate != NULL) {
+    dorestart = pextricate->pu_extricate_func(pextricate->pu_object, th);
+    th->p_woken_by_cancel = dorestart;
+  }
+
+  __pthread_unlock(&handle->h_lock);
+
+  /* If the thread has suspended or is about to, then we unblock it by
+     issuing a restart, instead of a cancel signal. Otherwise we send
+     the cancel signal to unblock the thread from a cancellation point,
+     or to initiate asynchronous cancellation. The restart is needed so
+     we have proper accounting of restarts; suspend decrements the thread's
+     resume count, and restart() increments it.  This also means that suspend's
+     handling of the cancel signal is obsolete. */
+
+  if (dorestart)
+    restart(th);
+  else 
+    kill(pid, __pthread_sig_cancel);
+
+  return 0;
+}
+
+void pthread_testcancel(void)
+{
+  pthread_descr self = thread_self();
+  if (THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(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 = THREAD_GETMEM(self, p_cleanup);
+  THREAD_SETMEM(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);
+  THREAD_SETMEM(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 = THREAD_GETMEM(self, p_canceltype);
+  buffer->__prev = THREAD_GETMEM(self, p_cleanup);
+  THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED);
+  THREAD_SETMEM(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);
+  THREAD_SETMEM(self, p_cleanup, buffer->__prev);
+  THREAD_SETMEM(self, p_canceltype, buffer->__canceltype);
+  if (THREAD_GETMEM(self, p_canceled) &&
+      THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE &&
+      THREAD_GETMEM(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 = THREAD_GETMEM(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/libpthread/linuxthreads/condvar.c b/libpthread/linuxthreads/condvar.c
new file mode 100644 (file)
index 0000000..85754a1
--- /dev/null
@@ -0,0 +1,417 @@
+/* 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 int pthread_cond_timedwait_relative_old(pthread_cond_t *,
+    pthread_mutex_t *, const struct timespec *);
+
+static int pthread_cond_timedwait_relative_new(pthread_cond_t *,
+    pthread_mutex_t *, const struct timespec *);
+
+static int (*pthread_cond_tw_rel)(pthread_cond_t *, pthread_mutex_t *,
+    const struct timespec *) = pthread_cond_timedwait_relative_old;
+
+/* initialize this module */
+void __pthread_init_condvar(int rt_sig_available)
+{
+  if (rt_sig_available)
+    pthread_cond_tw_rel = pthread_cond_timedwait_relative_new;
+}
+
+int pthread_cond_init(pthread_cond_t *cond,
+                      const pthread_condattr_t *cond_attr)
+{
+  __pthread_init_lock(&cond->__c_lock);
+  cond->__c_waiting = NULL;
+  return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+  if (cond->__c_waiting != NULL) return EBUSY;
+  return 0;
+}
+
+/* Function called by pthread_cancel to remove the thread from
+   waiting on a condition variable queue. */
+
+static int cond_extricate_func(void *obj, pthread_descr th)
+{
+  volatile pthread_descr self = thread_self();
+  pthread_cond_t *cond = obj;
+  int did_remove = 0;
+
+  __pthread_lock(&cond->__c_lock, self);
+  did_remove = remove_from_queue(&cond->__c_waiting, th);
+  __pthread_unlock(&cond->__c_lock);
+
+  return did_remove;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+  volatile pthread_descr self = thread_self();
+  pthread_extricate_if extr;
+  int already_canceled = 0;
+
+  /* Set up extrication interface */
+  extr.pu_object = cond;
+  extr.pu_extricate_func = cond_extricate_func;
+
+  /* Register extrication interface */
+  __pthread_set_own_extricate_if(self, &extr);
+
+  /* Atomically enqueue thread for waiting, but only if it is not
+     canceled. If the thread is canceled, then it will fall through the
+     suspend call below, and then call pthread_exit without
+     having to worry about whether it is still on the condition variable queue.
+     This depends on pthread_cancel setting p_canceled before calling the
+     extricate function. */
+
+  __pthread_lock(&cond->__c_lock, self);
+  if (!(THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
+    enqueue(&cond->__c_waiting, self);
+  else
+    already_canceled = 1;
+  __pthread_unlock(&cond->__c_lock);
+
+  if (already_canceled) {
+    __pthread_set_own_extricate_if(self, 0);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+
+  pthread_mutex_unlock(mutex);
+
+  suspend(self);
+  __pthread_set_own_extricate_if(self, 0);
+
+  /* Check for cancellation again, to provide correct cancellation
+     point behavior */
+
+  if (THREAD_GETMEM(self, p_woken_by_cancel)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
+    THREAD_SETMEM(self, p_woken_by_cancel, 0);
+    pthread_mutex_lock(mutex);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+
+  pthread_mutex_lock(mutex);
+  return 0;
+}
+
+/* The following function is used on kernels that don't have rt signals.
+   SIGUSR1 is used as the restart signal. The different code is needed
+   because that ordinary signal does not queue. */
+
+static int
+pthread_cond_timedwait_relative_old(pthread_cond_t *cond,
+                               pthread_mutex_t *mutex,
+                               const struct timespec * abstime)
+{
+  volatile pthread_descr self = thread_self();
+  sigset_t unblock, initial_mask;
+  int already_canceled = 0;
+  int was_signalled = 0;
+  sigjmp_buf jmpbuf;
+  pthread_extricate_if extr;
+
+  /* Set up extrication interface */
+  extr.pu_object = cond;
+  extr.pu_extricate_func = cond_extricate_func;
+
+  /* Register extrication interface */
+  __pthread_set_own_extricate_if(self, &extr);
+
+  /* Enqueue to wait on the condition and check for cancellation. */
+  __pthread_lock(&cond->__c_lock, self);
+  if (!(THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
+    enqueue(&cond->__c_waiting, self);
+  else
+    already_canceled = 1;
+  __pthread_unlock(&cond->__c_lock);
+
+  if (already_canceled) {
+    __pthread_set_own_extricate_if(self, 0);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+
+  pthread_mutex_unlock(mutex);
+
+  if (atomic_decrement(&self->p_resume_count) == 0) {
+    /* Set up a longjmp handler for the restart signal, unblock
+       the signal and sleep. */
+
+    if (sigsetjmp(jmpbuf, 1) == 0) {
+      THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
+      THREAD_SETMEM(self, p_signal, 0);
+      /* Unblock the restart signal */
+      sigemptyset(&unblock);
+      sigaddset(&unblock, __pthread_sig_restart);
+      sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
+
+      while (1) {
+       struct timeval now;
+       struct timespec reltime;
+
+       /* Compute a time offset relative to now.  */
+       gettimeofday (&now, NULL);
+       reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
+       reltime.tv_sec = abstime->tv_sec - now.tv_sec;
+       if (reltime.tv_nsec < 0) {
+         reltime.tv_nsec += 1000000000;
+         reltime.tv_sec -= 1;
+       }
+
+       /* Sleep for the required duration. If woken by a signal, resume waiting
+          as required by Single Unix Specification.  */
+       if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
+         break;
+      }
+
+      /* Block the restart signal again */
+      sigprocmask(SIG_SETMASK, &initial_mask, NULL);
+      was_signalled = 0;
+    } else {
+      was_signalled = 1;
+    }
+    THREAD_SETMEM(self, p_signal_jmp, NULL);
+  }
+
+  /* Now was_signalled is true if we exited the above code
+     due to the delivery of a restart signal.  In that case,
+     we know we have been dequeued and resumed and that the
+     resume count is balanced.  Otherwise, there are some
+     cases to consider. First, try to bump up the resume count
+     back to zero. If it goes to 1, it means restart() was
+     invoked on this thread. The signal must be consumed
+     and the count bumped down and everything is cool.
+     Otherwise, no restart was delivered yet, so we remove
+     the thread from the queue. If this succeeds, it's a clear
+     case of timeout. If we fail to remove from the queue, then we
+     must wait for a restart. */
+
+  if (!was_signalled) {
+    if (atomic_increment(&self->p_resume_count) != -1) {
+      __pthread_wait_for_restart_signal(self);
+      atomic_decrement(&self->p_resume_count); /* should be zero now! */
+    } else {
+      int was_on_queue;
+      __pthread_lock(&cond->__c_lock, self);
+      was_on_queue = remove_from_queue(&cond->__c_waiting, self);
+      __pthread_unlock(&cond->__c_lock);
+
+      if (was_on_queue) {
+       __pthread_set_own_extricate_if(self, 0);
+       pthread_mutex_lock(mutex);
+       return ETIMEDOUT;
+      }
+
+      suspend(self);
+    }
+  }
+
+  __pthread_set_own_extricate_if(self, 0);
+
+  /* The remaining logic is the same as in other cancellable waits,
+     such as pthread_join sem_wait or pthread_cond wait. */
+
+  if (THREAD_GETMEM(self, p_woken_by_cancel)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
+    THREAD_SETMEM(self, p_woken_by_cancel, 0);
+    pthread_mutex_lock(mutex);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+
+  pthread_mutex_lock(mutex);
+  return 0;
+}
+
+/* The following function is used on new (late 2.1 and 2.2 and higher) kernels
+   that have rt signals which queue. */
+
+static int
+pthread_cond_timedwait_relative_new(pthread_cond_t *cond,
+                               pthread_mutex_t *mutex,
+                               const struct timespec * abstime)
+{
+  volatile pthread_descr self = thread_self();
+  sigset_t unblock, initial_mask;
+  int already_canceled = 0;
+  int was_signalled = 0;
+  sigjmp_buf jmpbuf;
+  pthread_extricate_if extr;
+
+  /* Set up extrication interface */
+  extr.pu_object = cond;
+  extr.pu_extricate_func = cond_extricate_func;
+
+  /* Register extrication interface */
+  __pthread_set_own_extricate_if(self, &extr);
+
+  /* Enqueue to wait on the condition and check for cancellation. */
+  __pthread_lock(&cond->__c_lock, self);
+  if (!(THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
+    enqueue(&cond->__c_waiting, self);
+  else
+    already_canceled = 1;
+  __pthread_unlock(&cond->__c_lock);
+
+  if (already_canceled) {
+    __pthread_set_own_extricate_if(self, 0);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+
+  pthread_mutex_unlock(mutex);
+
+  /* Set up a longjmp handler for the restart signal, unblock
+     the signal and sleep. */
+
+  if (sigsetjmp(jmpbuf, 1) == 0) {
+    THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
+    THREAD_SETMEM(self, p_signal, 0);
+    /* Unblock the restart signal */
+    sigemptyset(&unblock);
+    sigaddset(&unblock, __pthread_sig_restart);
+    sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
+
+    while (1) {
+       struct timeval now;
+       struct timespec reltime;
+
+       /* Compute a time offset relative to now.  */
+       gettimeofday (&now, NULL);
+       reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
+       reltime.tv_sec = abstime->tv_sec - now.tv_sec;
+       if (reltime.tv_nsec < 0) {
+         reltime.tv_nsec += 1000000000;
+         reltime.tv_sec -= 1;
+       }
+
+       /* Sleep for the required duration. If woken by a signal,
+          resume waiting as required by Single Unix Specification.  */
+       if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0)
+         break;
+      }
+
+    /* Block the restart signal again */
+    sigprocmask(SIG_SETMASK, &initial_mask, NULL);
+    was_signalled = 0;
+  } else {
+    was_signalled = 1;
+  }
+  THREAD_SETMEM(self, p_signal_jmp, NULL);
+
+  /* Now was_signalled is true if we exited the above code
+     due to the delivery of a restart signal.  In that case,
+     everything is cool. We have been removed from the queue
+     by the other thread, and consumed its signal.
+
+     Otherwise we this thread woke up spontaneously, or due to a signal other
+     than restart. The next thing to do is to try to remove the thread
+     from the queue. This may fail due to a race against another thread
+     trying to do the same. In the failed case, we know we were signalled,
+     and we may also have to consume a restart signal. */
+
+  if (!was_signalled) {
+    int was_on_queue;
+
+    /* __pthread_lock will queue back any spurious restarts that
+       may happen to it. */
+
+    __pthread_lock(&cond->__c_lock, self);
+    was_on_queue = remove_from_queue(&cond->__c_waiting, self);
+    __pthread_unlock(&cond->__c_lock);
+
+    if (was_on_queue) {
+      __pthread_set_own_extricate_if(self, 0);
+      pthread_mutex_lock(mutex);
+      return ETIMEDOUT;
+    }
+
+    /* Eat the outstanding restart() from the signaller */
+    suspend(self);
+  }
+
+  __pthread_set_own_extricate_if(self, 0);
+
+  /* The remaining logic is the same as in other cancellable waits,
+     such as pthread_join sem_wait or pthread_cond wait. */
+
+  if (THREAD_GETMEM(self, p_woken_by_cancel)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
+    THREAD_SETMEM(self, p_woken_by_cancel, 0);
+    pthread_mutex_lock(mutex);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+
+  pthread_mutex_lock(mutex);
+  return 0;
+}
+
+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+                           const struct timespec * abstime)
+{
+  /* Indirect call through pointer! */
+  return pthread_cond_tw_rel(cond, mutex, abstime);
+}
+
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+  pthread_descr th;
+
+  __pthread_lock(&cond->__c_lock, NULL);
+  th = dequeue(&cond->__c_waiting);
+  __pthread_unlock(&cond->__c_lock);
+  if (th != NULL) restart(th);
+  return 0;
+}
+
+int pthread_cond_broadcast(pthread_cond_t *cond)
+{
+  pthread_descr tosignal, th;
+
+  __pthread_lock(&cond->__c_lock, NULL);
+  /* Copy the current state of the waiting queue and empty it */
+  tosignal = cond->__c_waiting;
+  cond->__c_waiting = NULL;
+  __pthread_unlock(&cond->__c_lock);
+  /* 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;
+}
diff --git a/libpthread/linuxthreads/configure b/libpthread/linuxthreads/configure
new file mode 100644 (file)
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/libpthread/linuxthreads/debug.h b/libpthread/linuxthreads/debug.h
new file mode 100644 (file)
index 0000000..79acb23
--- /dev/null
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** NAME:
+** debug.h
+**
+** DESCRIPTION:
+** This header file defines the debug macros used in pthreads. To turn
+** debugging on, add -DDEBUG_PT to CFLAGS. It was added to the original
+** distribution of linuxthreads.
+**
+** 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 _PT_DEBUG_H
+#define _PT_DEBUG_H
+
+/* include asserts for now */
+#define DO_ASSERT
+
+/* define the PDEBUG macro here */
+#undef PDEBUG
+#ifdef DEBUG_PT
+#  define PDEBUG(fmt, args...) __pthread_message(__FUNCTION__": " fmt, ## args)
+#else
+#  define PDEBUG(fmt, args...) /* debug switched off */
+#endif
+
+/* nothing; placeholder to disable a PDEBUG message but don't delete it */
+#undef PDEBUGG
+#define PDEBUGG(fmt, args...) 
+
+/* Define ASSERT to stop/warn. Should be void in production code */
+#undef ASSERT
+#ifdef DO_ASSERT
+#  define ASSERT(x) if (!(x)) fprintf(stderr, "pt: assertion failed in %s:%i.\n",\
+                    __FILE__, __LINE__)
+#else
+#  define ASSERT(x)
+#endif
+
+#endif /* _PT_DEBUG_H */
diff --git a/libpthread/linuxthreads/errno.c b/libpthread/linuxthreads/errno.c
new file mode 100644 (file)
index 0000000..ad43be4
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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 */
+
+#define __FORCE_GLIBC
+#include <features.h>
+#include <errno.h>
+#include <netdb.h>
+#include "pthread.h"
+#include "internals.h"
+
+int * __errno_location()
+{
+  pthread_descr self = thread_self();
+  return THREAD_GETMEM (self, p_errnop);
+}
+
+int * __h_errno_location()
+{
+  pthread_descr self = thread_self();
+  return THREAD_GETMEM (self, p_h_errnop);
+}
diff --git a/libpthread/linuxthreads/events.c b/libpthread/linuxthreads/events.c
new file mode 100644 (file)
index 0000000..e5be3d9
--- /dev/null
@@ -0,0 +1,35 @@
+/* Event functions used while debugging.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* The functions contained here do nothing, they just return.  */
+
+void
+__linuxthreads_create_event (void)
+{
+}
+
+void
+__linuxthreads_death_event (void)
+{
+}
+
+void
+__linuxthreads_reap_event (void)
+{
+}
diff --git a/libpthread/linuxthreads/internals.h b/libpthread/linuxthreads/internals.h
new file mode 100644 (file)
index 0000000..933ccb5
--- /dev/null
@@ -0,0 +1,480 @@
+/* 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 _INTERNALS_H
+#define _INTERNALS_H   1
+
+/* Internal data structures */
+
+/* Includes */
+
+#include <bits/libc-tsd.h> /* for _LIBC_TSD_KEY_N */
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "pt-machine.h"
+#include "semaphore.h"
+#include "../linuxthreads_db/thread_dbP.h"
+
+#ifndef THREAD_GETMEM
+# define THREAD_GETMEM(descr, member) descr->member
+#endif
+#ifndef THREAD_GETMEM_NC
+# define THREAD_GETMEM_NC(descr, member) descr->member
+#endif
+#ifndef THREAD_SETMEM
+# define THREAD_SETMEM(descr, member, value) descr->member = (value)
+#endif
+#ifndef THREAD_SETMEM_NC
+# define THREAD_SETMEM_NC(descr, member, value) descr->member = (value)
+#endif
+
+/* 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)
+
+typedef void (*destr_function)(void *);
+
+struct pthread_key_struct {
+  int in_use;                   /* already allocated? */
+  destr_function destr;         /* destruction routine */
+};
+
+
+#define PTHREAD_START_ARGS_INITIALIZER { NULL, NULL, {{0, }}, 0, { 0 } }
+
+/* The type of thread descriptors */
+
+typedef struct _pthread_descr_struct * pthread_descr;
+
+/* Callback interface for removing the thread from waiting on an
+   object if it is cancelled while waiting or about to wait.
+   This hold a pointer to the object, and a pointer to a function
+   which ``extricates'' the thread from its enqueued state.
+   The function takes two arguments: pointer to the wait object,
+   and a pointer to the thread. It returns 1 if an extrication
+   actually occured, and hence the thread must also be signalled.
+   It returns 0 if the thread had already been extricated. */
+
+typedef struct _pthread_extricate_struct {
+    void *pu_object;
+    int (*pu_extricate_func)(void *, pthread_descr);
+} pthread_extricate_if;
+
+/* Atomic counter made possible by compare_and_swap */
+
+struct pthread_atomic {
+  long p_count;
+  int p_spinlock;
+};
+
+/* Context info for read write locks. The pthread_rwlock_info structure
+   is information about a lock that has been read-locked by the thread
+   in whose list this structure appears. The pthread_rwlock_context
+   is embedded in the thread context and contains a pointer to the
+   head of the list of lock info structures, as well as a count of
+   read locks that are untracked, because no info structure could be
+   allocated for them. */
+
+struct _pthread_rwlock_t;
+
+typedef struct _pthread_rwlock_info {
+  struct _pthread_rwlock_info *pr_next;
+  struct _pthread_rwlock_t *pr_lock;
+  int pr_lock_count;
+} pthread_readlock_info;
+
+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_descr p_nextlock;    /* can be on a queue and waiting on a lock */
+  pthread_t p_tid;              /* Thread identifier */
+  int p_pid;                    /* PID of Unix process */
+  int p_priority;               /* Thread priority (== 0 if not realtime) */
+  struct _pthread_fastlock * p_lock; /* 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 */
+  char * p_in_sighandler;       /* stack address of sighandler, or NULL */
+  char p_sigwaiting;            /* true if a sigwait() is in progress */
+  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 */
+  int p_userstack;             /* nonzero if the user provided the stack */
+  void *p_guardaddr;           /* address of guard area or NULL */
+  size_t p_guardsize;          /* size of guard area */
+  pthread_descr p_self;                /* Pointer to this structure */
+  int p_nr;                     /* Index of descriptor in __pthread_handles */
+  int p_report_events;         /* Nonzero if events must be reported.  */
+  td_eventbuf_t p_eventbuf;     /* Data for event.  */
+  struct pthread_atomic p_resume_count; /* number of times restart() was
+                                          called on thread */
+  char p_woken_by_cancel;       /* cancellation performed wakeup */
+  pthread_extricate_if *p_extricate; /* See above */
+  pthread_readlock_info *p_readlock_list;  /* List of readlock info structs */
+  pthread_readlock_info *p_readlock_free;  /* Free list of structs */
+  int p_untracked_readlock_count;      /* Readlocks not tracked by list */
+  /* New elements must be added at the end.  */
+} __attribute__ ((aligned(32))); /* We need to align the structure so that
+                                   doubles are aligned properly.  This is 8
+                                   bytes on MIPS and 16 bytes on MIPS64.
+                                   32 bytes might give better cache
+                                   utilization.  */
+
+/* The type of thread handles. */
+
+typedef struct pthread_handle_struct * pthread_handle;
+
+struct pthread_handle_struct {
+  struct _pthread_fastlock h_lock; /* Fast lock for sychronized access */
+  pthread_descr h_descr;        /* Thread descriptor or NULL if invalid */
+  char * h_bottom;              /* Lowest address in the stack thread */
+};
+
+/* 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_POST, REQ_DEBUG
+  } 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_t thread_id;      /*   identifier of thread to free */
+    } free;
+    struct {                    /* For REQ_PROCESS_EXIT: */
+      int code;                 /*   exit status */
+    } exit;
+    void * post;                /* For REQ_POST: the semaphore */
+  } req_args;
+};
+
+
+/* Signals used for suspend/restart and for cancellation notification.  */
+
+extern int __pthread_sig_restart;
+extern int __pthread_sig_cancel;
+
+/* Signal used for interfacing with gdb */
+
+extern int __pthread_sig_debug;
+
+/* 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. */
+
+/* For non-MMU systems also remember to stack top of the initial thread.
+ * This is adapted when other stacks are malloc'ed since we don't know
+ * the bounds a-priori. -StS */
+
+extern char *__pthread_initial_thread_bos;
+#ifndef __UCLIBC_HAS_MMU__
+extern char *__pthread_initial_thread_tos;
+#define NOMMU_INITIAL_THREAD_BOUNDS(tos,bos) if ((tos)>=__pthread_initial_thread_bos && (bos)<=__pthread_initial_thread_tos) __pthread_initial_thread_bos = (tos)+1
+#else
+#define NOMMU_INITIAL_THREAD_BOUNDS(tos,bos) /* empty */
+#endif /* __UCLIBC_HAS_MMU__ */
+
+
+/* Indicate whether at least one thread has a user-defined stack (if 1),
+   or all threads have stacks supplied by LinuxThreads (if 0). */
+
+extern int __pthread_nonstandard_stacks;
+
+/* File descriptor for sending requests to the thread manager.
+   Initially -1, meaning that __pthread_initialize_manager 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;
+
+/* Set to 1 by gdb if we're debugging */
+
+extern volatile int __pthread_threads_debug;
+
+/* Globally enabled events.  */
+extern volatile td_thr_events_t __pthread_threads_events;
+
+/* Pointer to descriptor of thread with last event.  */
+extern volatile pthread_descr __pthread_last_event;
+
+/* 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 */
+
+extern pthread_descr __pthread_find_self (void) __attribute__ ((const));
+
+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;
+#ifdef __UCLIBC_HAS_MMU__
+  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 if (__pthread_nonstandard_stacks)
+    return __pthread_find_self();
+  else
+    return (pthread_descr)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1;
+#else
+  /* For non-MMU we need to be more careful about the initial thread stack.
+   * We refine the initial thread stack bounds dynamically as we allocate
+   * the other stack frame such that it doesn't overlap with them. Then
+   * we can be sure to pick the right thread according to the current SP */
+
+  /* Since we allow other stack frames to be above or below, we need to
+   * treat this case special. When pthread_initialize() wasn't called yet,
+   * only the initial thread is there. */
+  if (__pthread_initial_thread_bos == NULL) {
+      return &__pthread_initial_thread;
+  }
+  else if (sp >= __pthread_initial_thread_bos
+          && sp < __pthread_initial_thread_tos) {
+      return &__pthread_initial_thread;
+  }
+  else if (sp >= __pthread_manager_thread_bos
+          && sp < __pthread_manager_thread_tos) {
+      return &__pthread_manager_thread;
+  }
+  else {
+      return __pthread_find_self();
+  }
+#endif /* __UCLIBC_HAS_MMU__ */
+#endif
+}
+
+/* Max number of times we must spin on a spinlock calling sched_yield().
+   After MAX_SPIN_COUNT iterations, we put the calling thread to sleep. */
+
+#ifndef MAX_SPIN_COUNT
+#define MAX_SPIN_COUNT 50
+#endif
+
+/* Duration of sleep (in nanoseconds) when we can't acquire a spinlock
+   after MAX_SPIN_COUNT iterations of sched_yield().
+   With the 2.0 and 2.1 kernels, this MUST BE > 2ms.
+   (Otherwise the kernel does busy-waiting for realtime threads,
+    giving other threads no chance to run.) */
+
+#ifndef SPIN_SLEEP_DURATION
+#define SPIN_SLEEP_DURATION 2000001
+#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);
+int __pthread_initialize_manager(void);
+void __pthread_message(char * fmt, ...);
+int __pthread_manager(void *reqfd);
+int __pthread_manager_event(void *reqfd);
+void __pthread_manager_sighandler(int sig);
+void __pthread_reset_main_thread(void);
+void __fresetlockfiles(void);
+void __pthread_manager_adjust_prio(int thread_prio);
+void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif);
+
+extern int __pthread_attr_setguardsize __P ((pthread_attr_t *__attr,
+                                            size_t __guardsize));
+extern int __pthread_attr_getguardsize __P ((__const pthread_attr_t *__attr,
+                                            size_t *__guardsize));
+extern int __pthread_attr_setstackaddr __P ((pthread_attr_t *__attr,
+                                            void *__stackaddr));
+extern int __pthread_attr_getstackaddr __P ((__const pthread_attr_t *__attr,
+                                            void **__stackaddr));
+extern int __pthread_attr_setstacksize __P ((pthread_attr_t *__attr,
+                                            size_t __stacksize));
+extern int __pthread_attr_getstacksize __P ((__const pthread_attr_t *__attr,
+                                            size_t *__stacksize));
+extern int __pthread_getconcurrency __P ((void));
+extern int __pthread_setconcurrency __P ((int __level));
+extern int __pthread_mutexattr_gettype __P ((__const pthread_mutexattr_t *__attr,
+                                            int *__kind));
+extern void __pthread_kill_other_threads_np __P ((void));
+
+void __pthread_restart_old(pthread_descr th);
+void __pthread_suspend_old(pthread_descr self);
+
+void __pthread_restart_new(pthread_descr th);
+void __pthread_suspend_new(pthread_descr self);
+
+void __pthread_wait_for_restart_signal(pthread_descr self);
+
+void __pthread_init_condvar(int rt_sig_available);
+
+/* Global pointers to old or new suspend functions */
+
+extern void (*__pthread_restart)(pthread_descr);
+extern void (*__pthread_suspend)(pthread_descr);
+
+/* 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 ssize_t __libc_read (int fd, void *buf, size_t count);
+extern pid_t __libc_waitpid (pid_t pid, int *stat_loc, int options);
+extern ssize_t __libc_write (int fd, const void *buf, size_t count);
+
+/* Prototypes for some of the new semaphore functions.  */
+extern int __new_sem_post (sem_t * sem);
+
+/* The functions called the signal events.  */
+extern void __linuxthreads_create_event (void);
+extern void __linuxthreads_death_event (void);
+extern void __linuxthreads_reap_event (void);
+
+#endif /* internals.h */
diff --git a/libpthread/linuxthreads/join.c b/libpthread/linuxthreads/join.c
new file mode 100644 (file)
index 0000000..ccb11b1
--- /dev/null
@@ -0,0 +1,213 @@
+/* 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"
+#include "debug.h" /* PDEBUG, added by StS */
+
+void pthread_exit(void * retval)
+{
+  pthread_descr self = thread_self();
+  pthread_descr joining;
+  struct pthread_request request;
+PDEBUG("self=%p, pid=%d\n", self, self->p_pid);
+
+  /* Reset the cancellation flag to avoid looping if the cleanup handlers
+     contain cancellation points */
+  THREAD_SETMEM(self, p_canceled, 0);
+  /* Call cleanup functions and destroy the thread-specific data */
+  __pthread_perform_cleanup();
+  __pthread_destroy_specifics();
+  /* Store return value */
+  __pthread_lock(THREAD_GETMEM(self, p_lock), self);
+  THREAD_SETMEM(self, p_retval, retval);
+  /* Say that we've terminated */
+  THREAD_SETMEM(self, p_terminated, 1);
+  /* See whether we have to signal the death.  */
+  if (THREAD_GETMEM(self, p_report_events))
+    {
+      /* See whether TD_DEATH is in any of the mask.  */
+      int idx = __td_eventword (TD_DEATH);
+      uint32_t mask = __td_eventmask (TD_DEATH);
+
+      if ((mask & (__pthread_threads_events.event_bits[idx]
+                  | THREAD_GETMEM(self,
+                                  p_eventbuf.eventmask).event_bits[idx]))
+         != 0)
+       {
+         /* Yep, we have to signal the death.  */
+         THREAD_SETMEM(self, p_eventbuf.eventnum, TD_DEATH);
+         THREAD_SETMEM(self, p_eventbuf.eventdata, self);
+         __pthread_last_event = self;
+
+         /* Now call the function to signal the event.  */
+         __linuxthreads_death_event();
+       }
+    }
+  /* See if someone is joining on us */
+  joining = THREAD_GETMEM(self, p_joining);
+PDEBUG("joining = %p, pid=%d\n", joining, joining->p_pid);
+  __pthread_unlock(THREAD_GETMEM(self, p_lock));
+  /* 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);
+}
+
+/* Function called by pthread_cancel to remove the thread from
+   waiting on a condition variable queue. */
+
+static int join_extricate_func(void *obj, pthread_descr th)
+{
+  volatile pthread_descr self = thread_self();
+  pthread_handle handle = obj;
+  pthread_descr jo;
+  int did_remove = 0;
+
+  __pthread_lock(&handle->h_lock, self);
+  jo = handle->h_descr;
+  did_remove = jo->p_joining != NULL;
+  jo->p_joining = NULL;
+  __pthread_unlock(&handle->h_lock);
+
+  return did_remove;
+}
+
+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;
+  pthread_extricate_if extr;
+  int already_canceled = 0;
+PDEBUG("\n");
+
+  /* Set up extrication interface */
+  extr.pu_object = handle;
+  extr.pu_extricate_func = join_extricate_func;
+
+  __pthread_lock(&handle->h_lock, self);
+  if (invalid_handle(handle, thread_id)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+  th = handle->h_descr;
+  if (th == self) {
+    __pthread_unlock(&handle->h_lock);
+    return EDEADLK;
+  }
+  /* If detached or already joined, error */
+  if (th->p_detached || th->p_joining != NULL) {
+    __pthread_unlock(&handle->h_lock);
+    return EINVAL;
+  }
+  /* If not terminated yet, suspend ourselves. */
+  if (! th->p_terminated) {
+    /* Register extrication interface */
+    __pthread_set_own_extricate_if(self, &extr); 
+    if (!(THREAD_GETMEM(self, p_canceled)
+       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
+      th->p_joining = self;
+    else
+      already_canceled = 1;
+    __pthread_unlock(&handle->h_lock);
+
+    if (already_canceled) {
+      __pthread_set_own_extricate_if(self, 0); 
+      pthread_exit(PTHREAD_CANCELED);
+    }
+
+PDEBUG("before suspend\n");
+    suspend(self);
+PDEBUG("after suspend\n");
+    /* Deregister extrication interface */
+    __pthread_set_own_extricate_if(self, 0); 
+
+    /* This is a cancellation point */
+    if (THREAD_GETMEM(self, p_woken_by_cancel)
+       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
+      THREAD_SETMEM(self, p_woken_by_cancel, 0);
+      pthread_exit(PTHREAD_CANCELED);
+    }
+    __pthread_lock(&handle->h_lock, self);
+  }
+  /* Get return value */
+  if (thread_return != NULL) *thread_return = th->p_retval;
+  __pthread_unlock(&handle->h_lock);
+  /* Send notification to thread manager */
+  if (__pthread_manager_request >= 0) {
+    request.req_thread = self;
+    request.req_kind = REQ_FREE;
+    request.req_args.free.thread_id = thread_id;
+    __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;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, thread_id)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+  th = handle->h_descr;
+  /* If already detached, error */
+  if (th->p_detached) {
+    __pthread_unlock(&handle->h_lock);
+    return EINVAL;
+  }
+  /* If already joining, don't do anything. */
+  if (th->p_joining != NULL) {
+    __pthread_unlock(&handle->h_lock);
+    return 0;
+  }
+  /* Mark as detached */
+  th->p_detached = 1;
+  terminated = th->p_terminated;
+  __pthread_unlock(&handle->h_lock);
+  /* 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_id = thread_id;
+    __libc_write(__pthread_manager_request,
+                (char *) &request, sizeof(request));
+  }
+  return 0;
+}
diff --git a/libpthread/linuxthreads/linuxthreads.texi b/libpthread/linuxthreads/linuxthreads.texi
new file mode 100644 (file)
index 0000000..7a98103
--- /dev/null
@@ -0,0 +1,1428 @@
+@node POSIX Threads
+@c @node POSIX Threads, , Top, Top
+@chapter POSIX Threads
+@c %MENU% The standard threads library
+
+@c This chapter needs more work bigtime. -zw
+
+This chapter describes the pthreads (POSIX threads) library.  This
+library provides support functions for multithreaded programs: thread
+primitives, synchronization objects, and so forth.  It also implements
+POSIX 1003.1b semaphores (not to be confused with System V semaphores).
+
+The threads operations (@samp{pthread_*}) do not use @var{errno}.
+Instead they return an error code directly.  The semaphore operations do
+use @var{errno}.
+
+@menu
+* Basic Thread Operations::     Creating, terminating, and waiting for threads.
+* Thread Attributes::           Tuning thread scheduling.
+* Cancellation::                Stopping a thread before it's done.
+* Cleanup Handlers::            Deallocating resources when a thread is
+                                  cancelled.
+* Mutexes::                     One way to synchronize threads.
+* Condition Variables::         Another way.
+* POSIX Semaphores::            And a third way.
+* Thread-Specific Data::        Variables with different values in
+                                  different threads.
+* Threads and Signal Handling:: Why you should avoid mixing the two, and
+                                  how to do it if you must.
+* Miscellaneous Thread Functions:: A grab bag of utility routines.
+@end menu
+
+@node Basic Thread Operations
+@section Basic Thread Operations
+
+These functions are the thread equivalents of @code{fork}, @code{exit},
+and @code{wait}.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_create (pthread_t * @var{thread}, pthread_attr_t * @var{attr}, void * (*@var{start_routine})(void *), void * @var{arg})
+@code{pthread_create} creates a new thread of control that executes
+concurrently with the calling thread. The new thread calls the
+function @var{start_routine}, passing it @var{arg} as first argument. The
+new thread terminates either explicitly, by calling @code{pthread_exit},
+or implicitly, by returning from the @var{start_routine} function. The
+latter case is equivalent to calling @code{pthread_exit} with the result
+returned by @var{start_routine} as exit code.
+
+The @var{attr} argument specifies thread attributes to be applied to the
+new thread. @xref{Thread Attributes}, for details. The @var{attr}
+argument can also be @code{NULL}, in which case default attributes are
+used: the created thread is joinable (not detached) and has an ordinary
+(not realtime) scheduling policy.
+
+On success, the identifier of the newly created thread is stored in the
+location pointed by the @var{thread} argument, and a 0 is returned. On
+error, a non-zero error code is returned.
+
+This function may return the following errors:
+@table @code
+@item EAGAIN
+Not enough system resources to create a process for the new thread,
+or more than @code{PTHREAD_THREADS_MAX} threads are already active.
+@end table
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun void pthread_exit (void *@var{retval})
+@code{pthread_exit} terminates the execution of the calling thread.  All
+cleanup handlers (@pxref{Cleanup Handlers}) that have been set for the
+calling thread with @code{pthread_cleanup_push} 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-@code{NULL} values associated with them in the calling thread
+(@pxref{Thread-Specific Data}).  Finally, execution of the calling
+thread is stopped.
+
+The @var{retval} argument is the return value of the thread. It can be
+retrieved from another thread using @code{pthread_join}.
+
+The @code{pthread_exit} function never returns.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cancel (pthread_t @var{thread})
+
+@code{pthread_cancel} sends a cancellation request to the thread denoted
+by the @var{thread} argument.  If there is no such thread,
+@code{pthread_cancel} fails and returns @code{ESRCH}.  Otherwise it
+returns 0. @xref{Cancellation}, for details.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_join (pthread_t @var{th}, void **thread_@var{return})
+@code{pthread_join} suspends the execution of the calling thread until
+the thread identified by @var{th} terminates, either by calling
+@code{pthread_exit} or by being cancelled.
+
+If @var{thread_return} is not @code{NULL}, the return value of @var{th}
+is stored in the location pointed to by @var{thread_return}.  The return
+value of @var{th} is either the argument it gave to @code{pthread_exit},
+or @code{PTHREAD_CANCELED} if @var{th} was cancelled.
+
+The joined thread @code{th} must be in the joinable state: it must not
+have been detached using @code{pthread_detach} or the
+@code{PTHREAD_CREATE_DETACHED} attribute to @code{pthread_create}.
+
+When a joinable thread terminates, its memory resources (thread
+descriptor and stack) are not deallocated until another thread performs
+@code{pthread_join} on it. Therefore, @code{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 @code{pthread_join} on a thread @var{th} on which
+another thread is already waiting for termination returns an error.
+
+@code{pthread_join} is a cancellation point. If a thread is canceled
+while suspended in @code{pthread_join}, the thread execution resumes
+immediately and the cancellation is executed without waiting for the
+@var{th} thread to terminate. If cancellation occurs during
+@code{pthread_join}, the @var{th} thread remains not joined.
+
+On success, the return value of @var{th} is stored in the location
+pointed to by @var{thread_return}, and 0 is returned. On error, one of
+the following values is returned:
+@table @code
+@item ESRCH
+No thread could be found corresponding to that specified by @var{th}.
+@item EINVAL
+The @var{th} thread has been detached, or another thread is already
+waiting on termination of @var{th}.
+@item EDEADLK
+The @var{th} argument refers to the calling thread.
+@end table
+@end deftypefun
+
+@node Thread Attributes
+@section Thread Attributes
+
+@comment pthread.h
+@comment POSIX
+
+Threads have a number of attributes that may be set at creation time.
+This is done by filling a thread attribute object @var{attr} of type
+@code{pthread_attr_t}, then passing it as second argument to
+@code{pthread_create}. Passing @code{NULL} is equivalent to passing a
+thread attribute object with all attributes set to their default values.
+
+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 @code{pthread_create} does
+not change the attributes of the thread previously created.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_attr_init (pthread_attr_t *@var{attr})
+@code{pthread_attr_init} initializes the thread attribute object
+@var{attr} and fills it with default values for the attributes. (The
+default values are listed below for each attribute.)
+
+Each attribute @var{attrname} (see below for a list of all attributes)
+can be individually set using the function
+@code{pthread_attr_set@var{attrname}} and retrieved using the function
+@code{pthread_attr_get@var{attrname}}.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_attr_destroy (pthread_attr_t *@var{attr})
+@code{pthread_attr_destroy} destroys the attribute object pointed to by
+@var{attr} releasing any resources associated with it.  @var{attr} is
+left in an undefined state, and you must not use it again in a call to
+any pthreads function until it has been reinitialized.
+@end deftypefun
+
+@findex pthread_attr_setinheritsched
+@findex pthread_attr_setschedparam
+@findex pthread_attr_setschedpolicy
+@findex pthread_attr_setscope
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_attr_set@var{attr} (pthread_attr_t *@var{obj}, int @var{value})
+Set attribute @var{attr} to @var{value} in the attribute object pointed
+to by @var{obj}.  See below for a list of possible attributes and the
+values they can take.
+
+On success, these functions return 0.  If @var{value} is not meaningful
+for the @var{attr} being modified, they will return the error code
+@code{EINVAL}.  Some of the functions have other failure modes; see
+below.
+@end deftypefun
+
+@findex pthread_attr_getinheritsched
+@findex pthread_attr_getschedparam
+@findex pthread_attr_getschedpolicy
+@findex pthread_attr_getscope
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_attr_get@var{attr} (const pthread_attr_t *@var{obj}, int *@var{value})
+Store the current setting of @var{attr} in @var{obj} into the variable
+pointed to by @var{value}.
+
+These functions always return 0.
+@end deftypefun
+
+The following thread attributes are supported:
+@table @samp
+@item detachstate
+Choose whether the thread is created in the joinable state (value
+@code{PTHREAD_CREATE_JOINABLE}) or in the detached state
+(@code{PTHREAD_CREATE_DETACHED}).  The default is
+@code{PTHREAD_CREATE_JOINABLE}.
+
+In the joinable state, another thread can synchronize on the thread
+termination and recover its termination code using @code{pthread_join},
+but some of the thread resources are kept allocated after the thread
+terminates, and reclaimed only when another thread performs
+@code{pthread_join} on that thread.
+
+In the detached state, the thread resources are immediately freed when
+it terminates, but @code{pthread_join} 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 @code{pthread_detach}.
+
+@item schedpolicy
+Select the scheduling policy for the thread: one of @code{SCHED_OTHER}
+(regular, non-realtime scheduling), @code{SCHED_RR} (realtime,
+round-robin) or @code{SCHED_FIFO} (realtime, first-in first-out).
+The default is @code{SCHED_OTHER}.
+@c Not doc'd in our manual: FIXME.
+@c See @code{sched_setpolicy} for more information on scheduling policies.
+
+The realtime scheduling policies @code{SCHED_RR} and @code{SCHED_FIFO}
+are available only to processes with superuser privileges.
+@code{pthread_attr_setschedparam} will fail and return @code{ENOTSUP} if
+you try to set a realtime policy when you are unprivileged.
+
+The scheduling policy of a thread can be changed after creation with
+@code{pthread_setschedparam}.
+
+@item schedparam
+Change the scheduling parameter (the scheduling priority)
+for the thread.  The default is 0.
+
+This attribute is not significant if the scheduling policy is
+@code{SCHED_OTHER}; it only matters for the realtime policies
+@code{SCHED_RR} and @code{SCHED_FIFO}.
+
+The scheduling priority of a thread can be changed after creation with
+@code{pthread_setschedparam}.
+
+@item inheritsched
+Choose whether the scheduling policy and scheduling parameter for the
+newly created thread are determined by the values of the
+@var{schedpolicy} and @var{schedparam} attributes (value
+@code{PTHREAD_EXPLICIT_SCHED}) or are inherited from the parent thread
+(value @code{PTHREAD_INHERIT_SCHED}).  The default is
+@code{PTHREAD_EXPLICIT_SCHED}.
+
+@item scope
+Choose the scheduling contention scope for the created thread.  The
+default is @code{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 possibility,
+@code{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.
+
+@code{PTHREAD_SCOPE_PROCESS} is not supported in LinuxThreads.  If you
+try to set the scope to this value @code{pthread_attr_setscope} will
+fail and return @code{ENOTSUP}.
+@end table
+
+@node Cancellation
+@section Cancellation
+
+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 threads are
+first created by @code{pthread_create}, they always defer cancellation
+requests.
+
+When a thread eventually honors a cancellation request, it behaves as if
+@code{pthread_exit(PTHREAD_CANCELED)} was called.  All cleanup handlers
+are executed in reverse order, finalization functions for
+thread-specific data are called, and finally the thread stops executing.
+If the cancelled thread was joinable, the return value
+@code{PTHREAD_CANCELED} is provided to whichever thread calls
+@var{pthread_join} on it. See @code{pthread_exit} for more information.
+
+Cancellation points are the points where the thread checks for pending
+cancellation requests and performs them.  The POSIX threads functions
+@code{pthread_join}, @code{pthread_cond_wait},
+@code{pthread_cond_timedwait}, @code{pthread_testcancel},
+@code{sem_wait}, and @code{sigwait} are cancellation points.  In
+addition, these system calls are cancellation points:
+
+@multitable @columnfractions .33 .33 .33
+@item @t{accept}       @tab @t{open}           @tab @t{sendmsg}
+@item @t{close}                @tab @t{pause}          @tab @t{sendto}
+@item @t{connect}      @tab @t{read}           @tab @t{system}
+@item @t{fcntl}                @tab @t{recv}           @tab @t{tcdrain}
+@item @t{fsync}                @tab @t{recvfrom}       @tab @t{wait}
+@item @t{lseek}                @tab @t{recvmsg}        @tab @t{waitpid}
+@item @t{msync}                @tab @t{send}           @tab @t{write}
+@item @t{nanosleep}
+@end multitable
+
+@noindent
+All library functions that call these functions (such as
+@code{printf}) are also cancellation points.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_setcancelstate (int @var{state}, int *@var{oldstate})
+@code{pthread_setcancelstate} changes the cancellation state for the
+calling thread -- that is, whether cancellation requests are ignored or
+not. The @var{state} argument is the new cancellation state: either
+@code{PTHREAD_CANCEL_ENABLE} to enable cancellation, or
+@code{PTHREAD_CANCEL_DISABLE} to disable cancellation (cancellation
+requests are ignored).
+
+If @var{oldstate} is not @code{NULL}, the previous cancellation state is
+stored in the location pointed to by @var{oldstate}, and can thus be
+restored later by another call to @code{pthread_setcancelstate}.
+
+If the @var{state} argument is not @code{PTHREAD_CANCEL_ENABLE} or
+@code{PTHREAD_CANCEL_DISABLE}, @code{pthread_setcancelstate} fails and
+returns @code{EINVAL}.  Otherwise it returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_setcanceltype (int @var{type}, int *@var{oldtype})
+@code{pthread_setcanceltype} changes the type of responses to
+cancellation requests for the calling thread: asynchronous (immediate)
+or deferred.  The @var{type} argument is the new cancellation type:
+either @code{PTHREAD_CANCEL_ASYNCHRONOUS} to cancel the calling thread
+as soon as the cancellation request is received, or
+@code{PTHREAD_CANCEL_DEFERRED} to keep the cancellation request pending
+until the next cancellation point. If @var{oldtype} is not @code{NULL},
+the previous cancellation state is stored in the location pointed to by
+@var{oldtype}, and can thus be restored later by another call to
+@code{pthread_setcanceltype}.
+
+If the @var{type} argument is not @code{PTHREAD_CANCEL_DEFERRED} or
+@code{PTHREAD_CANCEL_ASYNCHRONOUS}, @code{pthread_setcanceltype} fails
+and returns @code{EINVAL}.  Otherwise it returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun void pthread_testcancel (@var{void})
+@code{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.
+@end deftypefun
+
+@node Cleanup Handlers
+@section Cleanup Handlers
+
+Cleanup handlers are functions that get called when a thread terminates,
+either by calling @code{pthread_exit} 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 @code{malloc} or close file
+descriptors on thread termination.
+
+Here is how to lock a mutex @var{mut} in such a way that it will be
+unlocked if the thread is canceled while @var{mut} is locked:
+
+@smallexample
+pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
+pthread_mutex_lock(&mut);
+/* do some work */
+pthread_mutex_unlock(&mut);
+pthread_cleanup_pop(0);
+@end smallexample
+
+Equivalently, the last two lines can be replaced by
+
+@smallexample
+pthread_cleanup_pop(1);
+@end smallexample
+
+Notice that the code above is safe only in deferred cancellation mode
+(see @code{pthread_setcanceltype}). In asynchronous cancellation mode, a
+cancellation can occur between @code{pthread_cleanup_push} and
+@code{pthread_mutex_lock}, or between @code{pthread_mutex_unlock} and
+@code{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:
+
+@smallexample
+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);
+@end smallexample
+
+The code above can be rewritten in a more compact and efficient way,
+using the non-portable functions @code{pthread_cleanup_push_defer_np}
+and @code{pthread_cleanup_pop_restore_np}:
+
+@smallexample
+pthread_cleanup_push_defer_np(pthread_mutex_unlock, (void *) &mut);
+pthread_mutex_lock(&mut);
+/* do some work */
+pthread_cleanup_pop_restore_np(1);
+@end smallexample
+
+@comment pthread.h
+@comment POSIX
+@deftypefun void pthread_cleanup_push (void (*@var{routine}) (void *), void *@var{arg})
+
+@code{pthread_cleanup_push} installs the @var{routine} function with
+argument @var{arg} as a cleanup handler. From this point on to the
+matching @code{pthread_cleanup_pop}, the function @var{routine} will be
+called with arguments @var{arg} when the thread terminates, either
+through @code{pthread_exit} 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.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun void pthread_cleanup_pop (int @var{execute})
+@code{pthread_cleanup_pop} removes the most recently installed cleanup
+handler. If the @var{execute} argument is not 0, it also executes the
+handler, by calling the @var{routine} function with arguments
+@var{arg}. If the @var{execute} argument is 0, the handler is only
+removed but not executed.
+@end deftypefun
+
+Matching pairs of @code{pthread_cleanup_push} and
+@code{pthread_cleanup_pop} must occur in the same function, at the same
+level of block nesting.  Actually, @code{pthread_cleanup_push} and
+@code{pthread_cleanup_pop} are macros, and the expansion of
+@code{pthread_cleanup_push} introduces an open brace @code{@{} with the
+matching closing brace @code{@}} being introduced by the expansion of the
+matching @code{pthread_cleanup_pop}.
+
+@comment pthread.h
+@comment GNU
+@deftypefun void pthread_cleanup_push_defer_np (void (*@var{routine}) (void *), void *@var{arg})
+@code{pthread_cleanup_push_defer_np} is a non-portable extension that
+combines @code{pthread_cleanup_push} and @code{pthread_setcanceltype}.
+It pushes a cleanup handler just as @code{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.
+@end deftypefun
+
+@comment pthread.h
+@comment GNU
+@deftypefun void pthread_cleanup_pop_restore_np (int @var{execute})
+@code{pthread_cleanup_pop_restore_np} pops a cleanup handler introduced
+by @code{pthread_cleanup_push_defer_np}, and restores the cancellation
+type to its value at the time @code{pthread_cleanup_push_defer_np} was
+called.
+@end deftypefun
+
+@code{pthread_cleanup_push_defer_np} and
+@code{pthread_cleanup_pop_restore_np} must occur in matching pairs, at
+the same level of block nesting.
+
+The sequence
+
+@smallexample
+pthread_cleanup_push_defer_np(routine, arg);
+...
+pthread_cleanup_pop_defer_np(execute);
+@end smallexample
+
+@noindent
+is functionally equivalent to (but more compact and efficient than)
+
+@smallexample
+@{
+  int oldtype;
+  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
+  pthread_cleanup_push(routine, arg);
+  ...
+  pthread_cleanup_pop(execute);
+  pthread_setcanceltype(oldtype, NULL);
+@}
+@end smallexample
+
+
+@node Mutexes
+@section Mutexes
+
+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.
+
+None of the mutex functions is a cancellation point, not even
+@code{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.
+
+It is not safe to call mutex functions from a signal handler.  In
+particular, calling @code{pthread_mutex_lock} or
+@code{pthread_mutex_unlock} from a signal handler may deadlock the
+calling thread.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutex_init (pthread_mutex_t *@var{mutex}, const pthread_mutexattr_t *@var{mutexattr})
+
+@code{pthread_mutex_init} initializes the mutex object pointed to by
+@var{mutex} according to the mutex attributes specified in @var{mutexattr}.
+If @var{mutexattr} is @code{NULL}, default attributes are used instead.
+
+The LinuxThreads implementation supports only one mutex attribute,
+the @var{mutex kind}, which is either ``fast'', ``recursive'', or
+``error checking''. The kind of a mutex determines whether
+it can be locked again by a thread that already owns it.
+The default kind is ``fast''.
+
+Variables of type @code{pthread_mutex_t} can also be initialized
+statically, using the constants @code{PTHREAD_MUTEX_INITIALIZER} (for
+fast mutexes), @code{PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP} (for
+recursive mutexes), and @code{PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP}
+(for error checking mutexes).
+
+@code{pthread_mutex_init} always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutex_lock (pthread_mutex_t *mutex))
+@code{pthread_mutex_lock} locks the given mutex. If the mutex is
+currently unlocked, it becomes locked and owned by the calling thread,
+and @code{pthread_mutex_lock} returns immediately. If the mutex is
+already locked by another thread, @code{pthread_mutex_lock} suspends the
+calling thread until the mutex is unlocked.
+
+If the mutex is already locked by the calling thread, the behavior of
+@code{pthread_mutex_lock} depends on the kind of the mutex. If the mutex
+is of the ``fast'' kind, the calling thread is suspended.  It will
+remain suspended forever, because no other thread can unlock the mutex.
+If  the mutex is of the ``error checking'' kind, @code{pthread_mutex_lock}
+returns immediately with the error code @code{EDEADLK}.  If the mutex is
+of the ``recursive'' kind, @code{pthread_mutex_lock} succeeds and
+returns immediately, recording the number of times the calling thread
+has locked the mutex. An equal number of @code{pthread_mutex_unlock}
+operations must be performed before the mutex returns to the unlocked
+state.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutex_trylock (pthread_mutex_t *@var{mutex})
+@code{pthread_mutex_trylock} behaves identically to
+@code{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,
+@code{pthread_mutex_trylock} returns immediately with the error code
+@code{EBUSY}.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutex_unlock (pthread_mutex_t *@var{mutex})
+@code{pthread_mutex_unlock} unlocks the given mutex. The mutex is
+assumed to be locked and owned by the calling thread on entrance to
+@code{pthread_mutex_unlock}. If the mutex is of the ``fast'' kind,
+@code{pthread_mutex_unlock} always returns it to the unlocked state. If
+it is of the ``recursive'' kind, it decrements the locking count of the
+mutex (number of @code{pthread_mutex_lock} operations performed on it by
+the calling thread), and only when this count reaches zero is the mutex
+actually unlocked.
+
+On ``error checking'' mutexes, @code{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
+@code{pthread_mutex_unlock}.  If these conditions are not met,
+@code{pthread_mutex_unlock} returns @code{EPERM}, 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.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutex_destroy (pthread_mutex_t *@var{mutex})
+@code{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 @code{pthread_mutex_destroy} actually does nothing except
+checking that the mutex is unlocked.
+
+If the mutex is locked by some thread, @code{pthread_mutex_destroy}
+returns @code{EBUSY}.  Otherwise it returns 0.
+@end deftypefun
+
+If any of the above functions (except @code{pthread_mutex_init})
+is applied to an uninitialized mutex, they will simply return
+@code{EINVAL} and do nothing.
+
+A shared global variable @var{x} can be protected by a mutex as follows:
+
+@smallexample
+int x;
+pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+@end smallexample
+
+All accesses and modifications to @var{x} should be bracketed by calls to
+@code{pthread_mutex_lock} and @code{pthread_mutex_unlock} as follows:
+
+@smallexample
+pthread_mutex_lock(&mut);
+/* operate on x */
+pthread_mutex_unlock(&mut);
+@end smallexample
+
+Mutex attributes can be specified at mutex creation time, by passing a
+mutex attribute object as second argument to @code{pthread_mutex_init}.
+Passing @code{NULL} is equivalent to passing a mutex attribute object
+with all attributes set to their default values.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutexattr_init (pthread_mutexattr_t *@var{attr})
+@code{pthread_mutexattr_init} initializes the mutex attribute object
+@var{attr} and fills it with default values for the attributes.
+
+This function always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_mutexattr_destroy (pthread_mutexattr_t *@var{attr})
+@code{pthread_mutexattr_destroy} destroys a mutex attribute object,
+which must not be reused until it is
+reinitialized. @code{pthread_mutexattr_destroy} does nothing in the
+LinuxThreads implementation.
+
+This function always returns 0.
+@end deftypefun
+
+LinuxThreads supports only one mutex attribute: the mutex kind, which is
+either @code{PTHREAD_MUTEX_FAST_NP} for ``fast'' mutexes,
+@code{PTHREAD_MUTEX_RECURSIVE_NP} for ``recursive'' mutexes, or
+@code{PTHREAD_MUTEX_ERRORCHECK_NP} for ``error checking'' mutexes.  As
+the @code{NP} suffix indicates, this is a non-portable extension to the
+POSIX standard and should not be employed in portable programs.
+
+The mutex kind determines what happens if a thread attempts to lock a
+mutex it already owns with @code{pthread_mutex_lock}. If the mutex is of
+the ``fast'' kind, @code{pthread_mutex_lock} simply suspends the calling
+thread forever.  If the mutex is of the ``error checking'' kind,
+@code{pthread_mutex_lock} returns immediately with the error code
+@code{EDEADLK}.  If the mutex is of the ``recursive'' kind, the call to
+@code{pthread_mutex_lock} returns immediately with a success return
+code. The number of times the thread owning the mutex has locked it is
+recorded in the mutex. The owning thread must call
+@code{pthread_mutex_unlock} the same number of times before the mutex
+returns to the unlocked state.
+
+The default mutex kind is ``fast'', that is, @code{PTHREAD_MUTEX_FAST_NP}.
+
+@comment pthread.h
+@comment GNU
+@deftypefun int pthread_mutexattr_setkind_np (pthread_mutexattr_t *@var{attr}, int @var{kind})
+@code{pthread_mutexattr_setkind_np} sets the mutex kind attribute in
+@var{attr} to the value specified by @var{kind}.
+
+If @var{kind} is not @code{PTHREAD_MUTEX_FAST_NP},
+@code{PTHREAD_MUTEX_RECURSIVE_NP}, or
+@code{PTHREAD_MUTEX_ERRORCHECK_NP}, this function will return
+@code{EINVAL} and leave @var{attr} unchanged.
+@end deftypefun
+
+@comment pthread.h
+@comment GNU
+@deftypefun int pthread_mutexattr_getkind_np (const pthread_mutexattr_t *@var{attr}, int *@var{kind})
+@code{pthread_mutexattr_getkind_np} retrieves the current value of the
+mutex kind attribute in @var{attr} and stores it in the location pointed
+to by @var{kind}.
+
+This function always returns 0.
+@end deftypefun
+
+@node Condition Variables
+@section Condition Variables
+
+A condition (short for ``condition variable'') is a synchronization
+device that allows threads to suspend execution 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.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cond_init (pthread_cond_t *@var{cond}, pthread_condattr_t *cond_@var{attr})
+
+@code{pthread_cond_init} initializes the condition variable @var{cond},
+using the condition attributes specified in @var{cond_attr}, or default
+attributes if @var{cond_attr} is @code{NULL}. The LinuxThreads
+implementation supports no attributes for conditions, hence the
+@var{cond_attr} parameter is actually ignored.
+
+Variables of type @code{pthread_cond_t} can also be initialized
+statically, using the constant @code{PTHREAD_COND_INITIALIZER}.
+
+This function always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cond_signal (pthread_cond_t *@var{cond})
+@code{pthread_cond_signal} restarts one of the threads that are waiting
+on the condition variable @var{cond}. If no threads are waiting on
+@var{cond}, nothing happens. If several threads are waiting on
+@var{cond}, exactly one is restarted, but it is not specified which.
+
+This function always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cond_broadcast (pthread_cond_t *@var{cond})
+@code{pthread_cond_broadcast} restarts all the threads that are waiting
+on the condition variable @var{cond}. Nothing happens if no threads are
+waiting on @var{cond}.
+
+This function always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cond_wait (pthread_cond_t *@var{cond}, pthread_mutex_t *@var{mutex})
+@code{pthread_cond_wait} atomically unlocks the @var{mutex} (as per
+@code{pthread_unlock_mutex}) and waits for the condition variable
+@var{cond} to be signaled. The thread execution is suspended and does
+not consume any CPU time until the condition variable is signaled. The
+@var{mutex} must be locked by the calling thread on entrance to
+@code{pthread_cond_wait}. Before returning to the calling thread,
+@code{pthread_cond_wait} re-acquires @var{mutex} (as per
+@code{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.
+
+This function always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cond_timedwait (pthread_cond_t *@var{cond}, pthread_mutex_t *@var{mutex}, const struct timespec *@var{abstime})
+@code{pthread_cond_timedwait} atomically unlocks @var{mutex} and waits
+on @var{cond}, as @code{pthread_cond_wait} does, but it also bounds the
+duration of the wait. If @var{cond} has not been signaled before time
+@var{abstime}, the mutex @var{mutex} is re-acquired and
+@code{pthread_cond_timedwait} returns the error code @code{ETIMEDOUT}.
+The wait can also be interrupted by a signal; in that case
+@code{pthread_cond_timedwait} returns @code{EINTR}.
+
+The @var{abstime} parameter specifies an absolute time, with the same
+origin as @code{time} and @code{gettimeofday}: an @var{abstime} of 0
+corresponds to 00:00:00 GMT, January 1, 1970.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_cond_destroy (pthread_cond_t *@var{cond})
+@code{pthread_cond_destroy} destroys the condition variable @var{cond},
+freeing the resources it might hold.  If any threads are waiting on the
+condition variable, @code{pthread_cond_destroy} leaves @var{cond}
+untouched and returns @code{EBUSY}.  Otherwise it returns 0, and
+@var{cond} must not be used again until it is reinitialized.
+
+In the LinuxThreads implementation, no resources are associated with
+condition variables, so @code{pthread_cond_destroy} actually does
+nothing.
+@end deftypefun
+
+@code{pthread_cond_wait} and @code{pthread_cond_timedwait} are
+cancellation points. If a thread is cancelled while suspended in one of
+these functions, the thread immediately resumes execution, relocks the
+mutex specified by  @var{mutex}, and finally executes the cancellation.
+Consequently, cleanup handlers are assured that @var{mutex} is locked
+when they are called.
+
+It is not safe to call the condition variable functions from a signal
+handler. In particular, calling @code{pthread_cond_signal} or
+@code{pthread_cond_broadcast} from a signal handler may deadlock the
+calling thread.
+
+Consider two shared variables @var{x} and @var{y}, protected by the
+mutex @var{mut}, and a condition variable @var{cond} that is to be
+signaled whenever @var{x} becomes greater than @var{y}.
+
+@smallexample
+int x,y;
+pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+@end smallexample
+
+Waiting until @var{x} is greater than @var{y} is performed as follows:
+
+@smallexample
+pthread_mutex_lock(&mut);
+while (x <= y) @{
+        pthread_cond_wait(&cond, &mut);
+@}
+/* operate on x and y */
+pthread_mutex_unlock(&mut);
+@end smallexample
+
+Modifications on @var{x} and @var{y} that may cause @var{x} to become greater than
+@var{y} should signal the condition if needed:
+
+@smallexample
+pthread_mutex_lock(&mut);
+/* modify x and y */
+if (x > y) pthread_cond_broadcast(&cond);
+pthread_mutex_unlock(&mut);
+@end smallexample
+
+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
+@var{x} and @var{y}), @code{pthread_cond_signal} can be used as a slightly more
+efficient alternative to @code{pthread_cond_broadcast}. In doubt, use
+@code{pthread_cond_broadcast}.
+
+To wait for @var{x} to becomes greater than @var{y} with a timeout of 5
+seconds, do:
+
+@smallexample
+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);
+@end smallexample
+
+Condition attributes can be specified at condition creation time, by
+passing a condition attribute object as second argument to
+@code{pthread_cond_init}.  Passing @code{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.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_condattr_init (pthread_condattr_t *@var{attr})
+@deftypefunx int pthread_condattr_destroy (pthread_condattr_t *@var{attr})
+@code{pthread_condattr_init} initializes the condition attribute object
+@var{attr} and fills it with default values for the attributes.
+@code{pthread_condattr_destroy} destroys the condition attribute object
+@var{attr}.
+
+Both functions do nothing in the LinuxThreads implementation.
+
+@code{pthread_condattr_init} and @code{pthread_condattr_destroy} always
+return 0.
+@end deftypefun
+
+@node POSIX Semaphores
+@section POSIX Semaphores
+
+@vindex SEM_VALUE_MAX
+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.
+
+Semaphores have a maximum value past which they cannot be incremented.
+The macro @code{SEM_VALUE_MAX} is defined to be this maximum value.  In
+the GNU C library, @code{SEM_VALUE_MAX} is equal to @code{INT_MAX}
+(@pxref{Range of Type}), but it may be much smaller on other systems.
+
+The pthreads library implements POSIX 1003.1b semaphores.  These should
+not be confused with System V semaphores (@code{ipc}, @code{semctl} and
+@code{semop}).
+@c !!! SysV IPC is not doc'd at all in our manual
+
+All the semaphore functions and macros are defined in @file{semaphore.h}.
+
+@comment semaphore.h
+@comment POSIX
+@deftypefun int sem_init (sem_t *@var{sem}, int @var{pshared}, unsigned int @var{value})
+@code{sem_init} initializes the semaphore object pointed to by
+@var{sem}. The count associated with the semaphore is set initially to
+@var{value}. The @var{pshared} argument indicates whether the semaphore
+is local to the current process (@var{pshared} is zero) or is to be
+shared between several processes (@var{pshared} is not zero).
+
+On success @code{sem_init} returns 0.  On failure it returns -1 and sets
+@var{errno} to one of the following values:
+
+@table @code
+@item EINVAL
+@var{value} exceeds the maximal counter value @code{SEM_VALUE_MAX}
+
+@item ENOSYS
+@var{pshared} is not zero.  LinuxThreads currently does not support
+process-shared semaphores.  (This will eventually change.)
+@end table
+@end deftypefun
+
+@comment semaphore.h
+@comment POSIX
+@deftypefun int sem_destroy (sem_t * @var{sem})
+@code{sem_destroy} destroys a semaphore object, freeing the resources it
+might hold.  If any threads are waiting on the semaphore when
+@code{sem_destroy} is called, it fails and sets @var{errno} to
+@code{EBUSY}.
+
+In the LinuxThreads implementation, no resources are associated with
+semaphore objects, thus @code{sem_destroy} actually does nothing except
+checking that no thread is waiting on the semaphore.  This will change
+when process-shared semaphores are implemented.
+@end deftypefun
+
+@comment semaphore.h
+@comment POSIX
+@deftypefun int sem_wait (sem_t * @var{sem})
+@code{sem_wait} suspends the calling thread until the semaphore pointed
+to by @var{sem} has non-zero count. It then atomically decreases the
+semaphore count.
+
+@code{sem_wait} is a cancellation point.  It always returns 0.
+@end deftypefun
+
+@comment semaphore.h
+@comment POSIX
+@deftypefun int sem_trywait (sem_t * @var{sem})
+@code{sem_trywait} is a non-blocking variant of @code{sem_wait}. If the
+semaphore pointed to by @var{sem} has non-zero count, the count is
+atomically decreased and @code{sem_trywait} immediately returns 0.  If
+the semaphore count is zero, @code{sem_trywait} immediately returns -1
+and sets errno to @code{EAGAIN}.
+@end deftypefun
+
+@comment semaphore.h
+@comment POSIX
+@deftypefun int sem_post (sem_t * @var{sem})
+@code{sem_post} atomically increases the count of the semaphore pointed to
+by @var{sem}. This function never blocks.
+
+@c !!! This para appears not to agree with the code.
+On processors supporting atomic compare-and-swap (Intel 486, Pentium and
+later, Alpha, PowerPC, MIPS II, Motorola 68k, Ultrasparc), the
+@code{sem_post} function is can safely 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 earlier Sparc
+chips, the current LinuxThreads implementation of @code{sem_post} is not
+async-signal safe, because the hardware does not support the required
+atomic operations.
+
+@code{sem_post} always succeeds and returns 0, unless the semaphore
+count would exceed @code{SEM_VALUE_MAX} after being incremented.  In
+that case @code{sem_post} returns -1 and sets @var{errno} to
+@code{EINVAL}.  The semaphore count is left unchanged.
+@end deftypefun
+
+@comment semaphore.h
+@comment POSIX
+@deftypefun int sem_getvalue (sem_t * @var{sem}, int * @var{sval})
+@code{sem_getvalue} stores in the location pointed to by @var{sval} the
+current count of the semaphore @var{sem}.  It always returns 0.
+@end deftypefun
+
+@node Thread-Specific Data
+@section Thread-Specific Data
+
+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 @code{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 @code{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 @code{NULL}
+with all keys.
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_key_create (pthread_key_t *@var{key}, void (*destr_function) (void *))
+@code{pthread_key_create} allocates a new TSD key. The key is stored in
+the location pointed to by @var{key}. There is a limit of
+@code{PTHREAD_KEYS_MAX} on the number of keys allocated at a given
+time. The value initially associated with the returned key is
+@code{NULL} in all currently executing threads.
+
+The @var{destr_function} argument, if not @code{NULL}, specifies a
+destructor function associated with the key. When a thread terminates
+via @code{pthread_exit} or by cancellation, @var{destr_function} is
+called on the value associated with the key in that thread. The
+@var{destr_function} is not called if a key is deleted with
+@code{pthread_key_delete} or a value is changed with
+@code{pthread_setspecific}.  The order in which destructor functions are
+called at thread termination time is unspecified.
+
+Before the destructor function is called, the @code{NULL} value is
+associated with the key in the current thread.  A destructor function
+might, however, re-associate non-@code{NULL} values to that key or some
+other key.  To deal with this, if after all the destructors have been
+called for all non-@code{NULL} values, there are still some
+non-@code{NULL} values with associated destructors, then the process is
+repeated.  The LinuxThreads implementation stops the process after
+@code{PTHREAD_DESTRUCTOR_ITERATIONS} iterations, even if some
+non-@code{NULL} values with associated descriptors remain.  Other
+implementations may loop indefinitely.
+
+@code{pthread_key_create} returns 0 unless @code{PTHREAD_KEYS_MAX} keys
+have already been allocated, in which case it fails and returns
+@code{EAGAIN}.
+@end deftypefun
+
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_key_delete (pthread_key_t @var{key})
+@code{pthread_key_delete} deallocates a TSD key. It does not check
+whether non-@code{NULL} values are associated with that key in the
+currently executing threads, nor call the destructor function associated
+with the key.
+
+If there is no such key @var{key}, it returns @code{EINVAL}.  Otherwise
+it returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_setspecific (pthread_key_t @var{key}, const void *@var{pointer})
+@code{pthread_setspecific} changes the value associated with @var{key}
+in the calling thread, storing the given @var{pointer} instead.
+
+If there is no such key @var{key}, it returns @code{EINVAL}.  Otherwise
+it returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun {void *} pthread_getspecific (pthread_key_t @var{key})
+@code{pthread_getspecific} returns the value currently associated with
+@var{key} in the calling thread.
+
+If there is no such key @var{key}, it returns @code{NULL}.
+@end deftypefun
+
+The following code fragment allocates a thread-specific array of 100
+characters, with automatic reclaimation at thread exit:
+
+@smallexample
+/* 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);
+@}
+@end smallexample
+
+@node Threads and Signal Handling
+@section Threads and Signal Handling
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_sigmask (int @var{how}, const sigset_t *@var{newmask}, sigset_t *@var{oldmask})
+@code{pthread_sigmask} changes the signal mask for the calling thread as
+described by the @var{how} and @var{newmask} arguments. If @var{oldmask}
+is not @code{NULL}, the previous signal mask is stored in the location
+pointed to by @var{oldmask}.
+
+The meaning of the @var{how} and @var{newmask} arguments is the same as
+for @code{sigprocmask}. If @var{how} is @code{SIG_SETMASK}, the signal
+mask is set to @var{newmask}. If @var{how} is @code{SIG_BLOCK}, the
+signals specified to @var{newmask} are added to the current signal mask.
+If @var{how} is @code{SIG_UNBLOCK}, the signals specified to
+@var{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 @code{sigaction}, are shared
+between all threads.
+
+The @code{pthread_sigmask} function returns 0 on success, and one of the
+following error codes on error:
+@table @code
+@item EINVAL
+@var{how} is not one of @code{SIG_SETMASK}, @code{SIG_BLOCK}, or @code{SIG_UNBLOCK}
+
+@item EFAULT
+@var{newmask} or @var{oldmask} point to invalid addresses
+@end table
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_kill (pthread_t @var{thread}, int @var{signo})
+@code{pthread_kill} sends signal number @var{signo} to the thread
+@var{thread}.  The signal is delivered and handled as described in
+@ref{Signal Handling}.
+
+@code{pthread_kill} returns 0 on success, one of the following error codes
+on error:
+@table @code
+@item EINVAL
+@var{signo} is not a valid signal number
+
+@item ESRCH
+The thread @var{thread} does not exist (e.g. it has already terminated)
+@end table
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int sigwait (const sigset_t *@var{set}, int *@var{sig})
+@code{sigwait} suspends the calling thread until one of the signals in
+@var{set} is delivered to the calling thread. It then stores the number
+of the signal received in the location pointed to by @var{sig} and
+returns. The signals in @var{set} must be blocked and not ignored on
+entrance to @code{sigwait}. If the delivered signal has a signal handler
+function attached, that function is @emph{not} called.
+
+@code{sigwait} is a cancellation point.  It always returns 0.
+@end deftypefun
+
+For @code{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 @code{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 @code{sigwait}.
+
+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 @code{sigwait} on that
+signal, it will not be restarted.
+
+The LinuxThreads implementation of @code{sigwait} installs dummy signal
+handlers for the signals in @var{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).
+
+@node Miscellaneous Thread Functions
+@section Miscellaneous Thread Functions
+
+@comment pthread.h
+@comment POSIX
+@deftypefun {pthread_t} pthread_self (@var{void})
+@code{pthread_self} returns the thread identifier for the calling thread.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_equal (pthread_t thread1, pthread_t thread2)
+@code{pthread_equal} determines if two thread identifiers refer to the same
+thread.
+
+A non-zero value is returned if @var{thread1} and @var{thread2} refer to
+the same thread. Otherwise, 0 is returned.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_detach (pthread_t @var{th})
+@code{pthread_detach} puts the thread @var{th} in the detached
+state. This guarantees that the memory resources consumed by @var{th}
+will be freed immediately when @var{th} terminates. However, this
+prevents other threads from synchronizing on the termination of @var{th}
+using @code{pthread_join}.
+
+A thread can be created initially in the detached state, using the
+@code{detachstate} attribute to @code{pthread_create}. In contrast,
+@code{pthread_detach} applies to threads created in the joinable state,
+and which need to be put in the detached state later.
+
+After @code{pthread_detach} completes, subsequent attempts to perform
+@code{pthread_join} on @var{th} will fail. If another thread is already
+joining the thread @var{th} at the time @code{pthread_detach} is called,
+@code{pthread_detach} does nothing and leaves @var{th} in the joinable
+state.
+
+On success, 0 is returned. On error, one of the following codes is
+returned:
+@table @code
+@item ESRCH
+No thread could be found corresponding to that specified by @var{th}
+@item EINVAL
+The thread @var{th} is already in the detached state
+@end table
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_atfork (void (*@var{prepare})(void), void (*@var{parent})(void), void (*@var{child})(void))
+
+@code{pthread_atfork} registers handler functions to be called just
+before and just after a new process is created with @code{fork}. The
+@var{prepare} handler will be called from the parent process, just
+before the new process is created. The @var{parent} handler will be
+called from the parent process, just before @code{fork} returns. The
+@var{child} handler will be called from the child process, just before
+@code{fork} returns.
+
+@code{pthread_atfork} returns 0 on success and a non-zero error code on
+error.
+
+One or more of the three handlers @var{prepare}, @var{parent} and
+@var{child} can be given as @code{NULL}, meaning that no handler needs
+to be called at the corresponding point.
+
+@code{pthread_atfork} can be called several times to install several
+sets of handlers. At @code{fork} time, the @var{prepare} handlers are
+called in LIFO order (last added with @code{pthread_atfork}, first
+called before @code{fork}), while the @var{parent} and @var{child}
+handlers are called in FIFO order (first added, first called).
+
+If there is insufficient memory available to register the handlers,
+@code{pthread_atfork} fails and returns @code{ENOMEM}.  Otherwise it
+returns 0.
+@end deftypefun
+
+To understand the purpose of @code{pthread_atfork}, recall that
+@code{fork} duplicates the whole memory space, including mutexes in
+their current locking state, but only the calling thread: other threads
+are not running in the child process. Thus, if a mutex is locked by a
+thread other than the thread calling @code{fork}, that mutex will remain
+locked forever in the child process, possibly blocking the execution of
+the child process. To avoid this, install handlers with
+@code{pthread_atfork} as follows: the @var{prepare} handler locks the
+global mutexes (in locking order), and the @var{parent} and @var{child}
+handlers unlock them (in reverse order). Alternatively, @var{prepare}
+and @var{parent} can be set to @code{NULL} and @var{child} to a function
+that calls @code{pthread_mutex_init} on the global mutexes.
+
+@comment pthread.h
+@comment GNU
+@deftypefun void pthread_kill_other_threads_np (@var{void})
+@code{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 @code{exec} functions,
+e.g. @code{execve}.
+
+Termination of the other threads is not performed through
+@code{pthread_cancel} 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.
+
+According to POSIX 1003.1c, a successful @code{exec*} in one of the
+threads should automatically terminate all other threads in the program.
+This behavior is not yet implemented in LinuxThreads.  Calling
+@code{pthread_kill_other_threads_np} before @code{exec*} achieves much
+of the same behavior, except that if @code{exec*} ultimately fails, then
+all other threads are already killed.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_once (pthread_once_t *once_@var{control}, void (*@var{init_routine}) (void))
+
+The purpose of @code{pthread_once} is to ensure that a piece of
+initialization code is executed at most once. The @var{once_control}
+argument points to a static or extern variable statically initialized
+to @code{PTHREAD_ONCE_INIT}.
+
+The first time @code{pthread_once} is called with a given
+@var{once_control} argument, it calls @var{init_routine} with no
+argument and changes the value of the @var{once_control} variable to
+record that initialization has been performed. Subsequent calls to
+@code{pthread_once} with the same @code{once_control} argument do
+nothing.
+
+@code{pthread_once} always returns 0.
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_setschedparam (pthread_t target_@var{thread}, int @var{policy}, const struct sched_param *@var{param})
+
+@code{pthread_setschedparam} sets the scheduling parameters for the
+thread @var{target_thread} as indicated by @var{policy} and
+@var{param}. @var{policy} can be either @code{SCHED_OTHER} (regular,
+non-realtime scheduling), @code{SCHED_RR} (realtime, round-robin) or
+@code{SCHED_FIFO} (realtime, first-in first-out). @var{param} specifies
+the scheduling priority for the two realtime policies.  See
+@code{sched_setpolicy} for more information on scheduling policies.
+
+The realtime scheduling policies @code{SCHED_RR} and @code{SCHED_FIFO}
+are available only to processes with superuser privileges.
+
+On success, @code{pthread_setschedparam} returns 0.  On error it returns
+one of the following codes:
+@table @code
+@item EINVAL
+@var{policy} is not one of @code{SCHED_OTHER}, @code{SCHED_RR},
+@code{SCHED_FIFO}, or the priority value specified by @var{param} is not
+valid for the specified policy
+
+@item EPERM
+Realtime scheduling was requested but the calling process does not have
+sufficient privileges.
+
+@item ESRCH
+The @var{target_thread} is invalid or has already terminated
+
+@item EFAULT
+@var{param} points outside the process memory space
+@end table
+@end deftypefun
+
+@comment pthread.h
+@comment POSIX
+@deftypefun int pthread_getschedparam (pthread_t target_@var{thread}, int *@var{policy}, struct sched_param *@var{param})
+
+@code{pthread_getschedparam} retrieves the scheduling policy and
+scheduling parameters for the thread @var{target_thread} and stores them
+in the locations pointed to by @var{policy} and @var{param},
+respectively.
+
+@code{pthread_getschedparam} returns 0 on success, or one of the
+following error codes on failure:
+@table @code
+@item ESRCH
+The @var{target_thread} is invalid or has already terminated.
+
+@item EFAULT
+@var{policy} or @var{param} point outside the process memory space.
+
+@end table
+@end deftypefun
diff --git a/libpthread/linuxthreads/lockfile.c b/libpthread/linuxthreads/lockfile.c
new file mode 100644 (file)
index 0000000..18c3fed
--- /dev/null
@@ -0,0 +1,88 @@
+/* lockfile - Handle locking and unlocking of stream.
+   Copyright (C) 1996, 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.  */
+
+#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
+  return 0;
+#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_settype (&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/libpthread/linuxthreads/manager.c b/libpthread/linuxthreads/manager.c
new file mode 100644 (file)
index 0000000..5d355e1
--- /dev/null
@@ -0,0 +1,786 @@
+/* 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 */
+
+/* mods for uClibc: getpwd and getpagesize are the syscalls */
+#define __getpid getpid
+#define __getpagesize getpagesize
+
+#include <errno.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/poll.h>          /* for poll */
+#include <sys/mman.h>           /* for mmap */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>           /* for waitpid macros */
+
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+#include "semaphore.h"
+#include "debug.h" /* PDEBUG, added by StS */
+
+/* Array of active threads. Entry 0 is reserved for the initial thread. */
+struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] =
+{ { LOCK_INITIALIZER, &__pthread_initial_thread, 0},
+  { LOCK_INITIALIZER, &__pthread_manager_thread, 0}, /* All NULLs */ };
+
+/* For debugging purposes put the maximum number of threads in a variable.  */
+const int __linuxthreads_pthread_threads_max = PTHREAD_THREADS_MAX;
+
+/* Indicate whether at least one thread has a user-defined stack (if 1),
+   or if all threads have stacks supplied by LinuxThreads (if 0). */
+int __pthread_nonstandard_stacks;
+
+/* Number of active entries in __pthread_handles (used by gdb) */
+volatile int __pthread_handles_num = 2;
+
+/* Whether to use debugger additional actions for thread creation
+   (set to 1 by gdb) */
+volatile int __pthread_threads_debug;
+
+/* Globally enabled events.  */
+volatile td_thr_events_t __pthread_threads_events;
+
+/* Pointer to thread descriptor with last event.  */
+volatile pthread_descr __pthread_last_event;
+
+/* 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,
+                                int report_events,
+                                td_thr_events_t *event_maskp);
+static void pthread_handle_free(pthread_t th_id);
+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) (long int) arg;
+  struct pollfd ufd;
+  sigset_t mask;
+  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, 1);
+#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_cancel and SIGTRAP */
+  sigfillset(&mask);
+  sigdelset(&mask, __pthread_sig_cancel); /* for thread termination */
+  sigdelset(&mask, SIGTRAP);            /* for debugging purposes */
+  sigprocmask(SIG_SETMASK, &mask, NULL);
+  /* Raise our priority to match that of main thread */
+  __pthread_manager_adjust_prio(__pthread_main_thread->p_priority);
+  /* Synchronize debugging of the thread manager */
+  n = __libc_read(reqfd, (char *)&request, sizeof(request));
+  ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG);
+  ufd.fd = reqfd;
+  ufd.events = POLLIN;
+  /* Enter server loop */
+  while(1) {
+PDEBUG("before poll\n");
+    n = poll(&ufd, 1, 2000);
+PDEBUG("after poll\n");
+
+    /* 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 && (ufd.revents & POLLIN)) {
+PDEBUG("before __libc_read\n");
+      n = __libc_read(reqfd, (char *)&request, sizeof(request));
+PDEBUG("after __libc_read, n=%d\n", n);
+      ASSERT(n == sizeof(request));
+      switch(request.req_kind) {
+      case REQ_CREATE:
+PDEBUG("got REQ_CREATE\n");
+        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,
+                               request.req_thread->p_report_events,
+                               &request.req_thread->p_eventbuf.eventmask);
+PDEBUG("restarting %d\n", request.req_thread);
+        restart(request.req_thread);
+        break;
+      case REQ_FREE:
+PDEBUG("got REQ_FREE\n");
+       pthread_handle_free(request.req_args.free.thread_id);
+        break;
+      case REQ_PROCESS_EXIT:
+PDEBUG("got REQ_PROCESS_EXIT from %d, exit code = %d\n", 
+       request.req_thread, request.req_args.exit.code);
+        pthread_handle_exit(request.req_thread,
+                            request.req_args.exit.code);
+        break;
+      case REQ_MAIN_THREAD_EXIT:
+PDEBUG("got REQ_MAIN_THREAD_EXIT\n");
+        main_thread_exiting = 1;
+        if (__pthread_main_thread->p_nextlive == __pthread_main_thread) {
+          restart(__pthread_main_thread);
+          return 0;
+        }
+        break;
+      case REQ_POST:
+PDEBUG("got REQ_POST\n");
+        __new_sem_post(request.req_args.post);
+        break;
+      case REQ_DEBUG:
+PDEBUG("got REQ_DEBUG\n");
+       /* Make gdb aware of new thread and gdb will restart the
+          new thread when it is ready to handle the new thread. */
+       if (__pthread_threads_debug && __pthread_sig_debug > 0)
+PDEBUG("about to call raise(__pthread_sig_debug)\n");
+         raise(__pthread_sig_debug);
+        break;
+      }
+    }
+  }
+}
+
+int __pthread_manager_event(void *arg)
+{
+  /* If we have special thread_self processing, initialize it.  */
+#ifdef INIT_THREAD_SELF
+  INIT_THREAD_SELF(&__pthread_manager_thread, 1);
+#endif
+
+  /* Get the lock the manager will free once all is correctly set up.  */
+  __pthread_lock (THREAD_GETMEM((&__pthread_manager_thread), p_lock), NULL);
+  /* Free it immediately.  */
+  __pthread_unlock (THREAD_GETMEM((&__pthread_manager_thread), p_lock));
+
+  return __pthread_manager(arg);
+}
+
+/* Process creation */
+
+static int pthread_start_thread(void *arg)
+{
+  pthread_descr self = (pthread_descr) arg;
+  struct pthread_request request;
+  void * outcome;
+  /* Initialize special thread_self processing, if any.  */
+#ifdef INIT_THREAD_SELF
+  INIT_THREAD_SELF(self, self->p_nr);
+#endif
+PDEBUG("\n");
+  /* Make sure our pid field is initialized, just in case we get there
+     before our father has initialized it. */
+  THREAD_SETMEM(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 (THREAD_GETMEM(self, p_start_args.schedpolicy) >= 0)
+    /* Explicit scheduling attributes were provided: apply them */
+    sched_setscheduler(THREAD_GETMEM(self, p_pid),
+                        THREAD_GETMEM(self, p_start_args.schedpolicy),
+                         &self->p_start_args.schedparam);
+  else if (__pthread_manager_thread.p_priority > 0)
+    /* Default scheduling required, but thread manager runs in realtime
+       scheduling: switch new thread to SCHED_OTHER policy */
+    {
+      struct sched_param default_params;
+      default_params.sched_priority = 0;
+      sched_setscheduler(THREAD_GETMEM(self, p_pid),
+                           SCHED_OTHER, &default_params);
+    }
+  /* Make gdb aware of new thread */
+  if (__pthread_threads_debug && __pthread_sig_debug > 0) {
+    request.req_thread = self;
+    request.req_kind = REQ_DEBUG;
+    __libc_write(__pthread_manager_request,
+                 (char *) &request, sizeof(request));
+    suspend(self);
+  }
+  /* Run the thread code */
+  outcome = self->p_start_args.start_routine(THREAD_GETMEM(self,
+                                                          p_start_args.arg));
+  /* Exit with the given return value */
+  pthread_exit(outcome);
+  return 0;
+}
+
+static int pthread_start_thread_event(void *arg)
+{
+  pthread_descr self = (pthread_descr) arg;
+
+#ifdef INIT_THREAD_SELF
+  INIT_THREAD_SELF(self, self->p_nr);
+#endif
+  /* Make sure our pid field is initialized, just in case we get there
+     before our father has initialized it. */
+  THREAD_SETMEM(self, p_pid, __getpid());
+  /* Get the lock the manager will free once all is correctly set up.  */
+  __pthread_lock (THREAD_GETMEM(self, p_lock), NULL);
+  /* Free it immediately.  */
+  __pthread_unlock (THREAD_GETMEM(self, p_lock));
+
+  /* Continue with the real function.  */
+  return pthread_start_thread (arg);
+}
+
+static int pthread_allocate_stack(const pthread_attr_t *attr,
+                                  pthread_descr default_new_thread,
+                                  int pagesize,
+                                  pthread_descr * out_new_thread,
+                                  char ** out_new_thread_bottom,
+                                  char ** out_guardaddr,
+                                  size_t * out_guardsize)
+{
+  pthread_descr new_thread;
+  char * new_thread_bottom;
+  char * guardaddr;
+  size_t stacksize, guardsize;
+
+  if (attr != NULL && attr->__stackaddr_set)
+    {
+      /* The user provided a stack. */
+      new_thread =
+        (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1;
+      new_thread_bottom = (char *) attr->__stackaddr - attr->__stacksize;
+      guardaddr = NULL;
+      guardsize = 0;
+      __pthread_nonstandard_stacks = 1;
+    }
+  else
+    {
+#ifdef __UCLIBC_HAS_MMU__
+      stacksize = STACK_SIZE - pagesize;
+      if (attr != NULL)
+        stacksize = MIN (stacksize, roundup(attr->__stacksize, pagesize));
+      /* Allocate space for stack and thread descriptor at default address */
+      new_thread = default_new_thread;
+      new_thread_bottom = (char *) (new_thread + 1) - stacksize;
+      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)
+        /* Bad luck, this segment is already mapped. */
+        return -1;
+      /* We manage to get a stack.  Now see whether we need a guard
+         and allocate it if necessary.  Notice that the default
+         attributes (stack_size = STACK_SIZE - pagesize) do not need
+        a guard page, since the RLIMIT_STACK soft limit prevents stacks
+        from running into one another. */
+      if (stacksize == STACK_SIZE - pagesize)
+        {
+          /* We don't need a guard page. */
+          guardaddr = NULL;
+          guardsize = 0;
+        }
+      else
+        {
+          /* Put a bad page at the bottom of the stack */
+          guardsize = attr->__guardsize;
+          guardaddr = (void *)new_thread_bottom - guardsize;
+          if (mmap ((caddr_t) guardaddr, guardsize, 0, MAP_FIXED, -1, 0)
+              == MAP_FAILED)
+            {
+              /* We don't make this an error.  */
+              guardaddr = NULL;
+              guardsize = 0;
+            }
+        }
+#else
+      /* We cannot mmap to this huge chunk of stack space when we don't have
+       * an MMU. Pretend we are using a user provided stack even if there was
+       * none provided by the user. Thus, we get around the mmap and reservation
+       * of a huge stack segment. -StS */
+
+      char *new_stack;
+
+      if ((new_stack = malloc(INITIAL_STACK_SIZE)) == NULL) {
+       /* bad luck, we cannot malloc any more */
+       return -1;
+      }
+
+      PDEBUG("malloced chunk: base=%p, size=0x%04x\n", new_stack, INITIAL_STACK_SIZE);
+
+      /* Set up the pointers. new_thread marks the TOP of the stack frame and
+       * the address of the pthread_descr struct at the same time. Therefore we
+       * must account for its size and fit it in the malloc()'ed block. The
+       * value of `new_thread' is then passed to clone() as the stack argument.
+       *
+       *               ^ +------------------------+
+       *               | |  pthread_descr struct  |
+       *               | +------------------------+  <- new_thread 
+       * malloc block  | |                        |
+       *               | |  thread stack          |
+       *               | |                        |
+       *               v +------------------------+  <- new_thread_bottom
+       *
+       * Note: The calculated value of new_thread must be word aligned otherwise
+       * the kernel chokes on a non-aligned stack frame. Choose the lower
+       * available word boundary.
+       */
+      new_thread_bottom = (pthread_descr) new_stack;
+      new_thread = (long)((char *) new_stack + INITIAL_STACK_SIZE - sizeof(*new_thread) - 1) 
+         & -sizeof(void*); /* align new_thread */
+      guardaddr = NULL;
+      guardsize = 0;
+
+      PDEBUG("thread stack: bos=%p, tos=%p\n", new_thread_bottom, new_thread);
+
+      /* check the initial thread stack boundaries so they don't overlap */
+      NOMMU_INITIAL_THREAD_BOUNDS(new_thread, new_thread_bottom);
+
+      PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos, 
+            __pthread_initial_thread_tos);
+
+      /* on non-MMU systems we always have non-standard stack frames */
+      __pthread_nonstandard_stacks = 1;
+      
+#endif /* __UCLIBC_HAS_MMU__ */
+    }
+
+  /* Clear the thread data structure.  */
+  memset (new_thread, '\0', sizeof (*new_thread));
+  *out_new_thread = new_thread;
+  *out_new_thread_bottom = new_thread_bottom;
+  *out_guardaddr = guardaddr;
+  *out_guardsize = guardsize;
+  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,
+                                int report_events,
+                                td_thr_events_t *event_maskp)
+{
+  size_t sseg;
+  int pid;
+  pthread_descr new_thread;
+  char * new_thread_bottom;
+  pthread_t new_thread_id;
+  char *guardaddr = NULL;
+  size_t guardsize = 0;
+  int pagesize = __getpagesize();
+
+  /* First check whether we have to change the policy and if yes, whether
+     we can  do this.  Normally this should be done by examining the
+     return value of the sched_setscheduler call in pthread_start_thread
+     but this is hard to implement.  FIXME  */
+  if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0)
+    return EPERM;
+  /* Find a free segment for the thread, and allocate a stack if needed */
+  for (sseg = 2; ; sseg++)
+    {
+      if (sseg >= PTHREAD_THREADS_MAX)
+       return EAGAIN;
+      if (__pthread_handles[sseg].h_descr != NULL)
+       continue;
+      if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize,
+                                 &new_thread, &new_thread_bottom,
+                                 &guardaddr, &guardsize) == 0)
+        break;
+    }
+  __pthread_handles_num++;
+  /* Allocate new thread identifier */
+  pthread_threads_counter += PTHREAD_THREADS_MAX;
+  new_thread_id = sseg + pthread_threads_counter;
+  /* Initialize the thread descriptor.  Elements which have to be
+     initialized to zero already have this value.  */
+  new_thread->p_tid = new_thread_id;
+  new_thread->p_lock = &(__pthread_handles[sseg].h_lock);
+  new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE;
+  new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED;
+  new_thread->p_errnop = &new_thread->p_errno;
+  new_thread->p_h_errnop = &new_thread->p_h_errno;
+  new_thread->p_guardaddr = guardaddr;
+  new_thread->p_guardsize = guardsize;
+  new_thread->p_self = new_thread;
+  new_thread->p_nr = sseg;
+  /* Initialize the thread handle */
+  __pthread_init_lock(&__pthread_handles[sseg].h_lock);
+  __pthread_handles[sseg].h_descr = new_thread;
+  __pthread_handles[sseg].h_bottom = new_thread_bottom;
+  /* Determine scheduling parameters for the thread */
+  new_thread->p_start_args.schedpolicy = -1;
+  if (attr != NULL) {
+    new_thread->p_detached = attr->__detachstate;
+    new_thread->p_userstack = attr->__stackaddr_set;
+
+    switch(attr->__inheritsched) {
+    case PTHREAD_EXPLICIT_SCHED:
+      new_thread->p_start_args.schedpolicy = attr->__schedpolicy;
+      memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam,
+             sizeof (struct sched_param));
+      break;
+    case PTHREAD_INHERIT_SCHED:
+      new_thread->p_start_args.schedpolicy = sched_getscheduler(father_pid);
+      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;
+  /* Raise priority of thread manager if needed */
+  __pthread_manager_adjust_prio(new_thread->p_priority);
+  /* Do the cloning.  We have to use two different functions depending
+     on whether we are debugging or not.  */
+  pid = 0;     /* Note that the thread never can have PID zero.  */
+  if (report_events)
+    {
+      /* See whether the TD_CREATE event bit is set in any of the
+         masks.  */
+      int idx = __td_eventword (TD_CREATE);
+      uint32_t mask = __td_eventmask (TD_CREATE);
+
+      if ((mask & (__pthread_threads_events.event_bits[idx]
+                  | event_maskp->event_bits[idx])) != 0)
+       {
+         /* Lock the mutex the child will use now so that it will stop.  */
+         __pthread_lock(new_thread->p_lock, NULL);
+
+         /* We have to report this event.  */
+         pid = clone(pthread_start_thread_event, (void **) new_thread,
+                       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
+                       __pthread_sig_cancel, new_thread);
+         if (pid != -1)
+           {
+             /* Now fill in the information about the new thread in
+                the newly created thread's data structure.  We cannot let
+                the new thread do this since we don't know whether it was
+                already scheduled when we send the event.  */
+             new_thread->p_eventbuf.eventdata = new_thread;
+             new_thread->p_eventbuf.eventnum = TD_CREATE;
+             __pthread_last_event = new_thread;
+
+             /* We have to set the PID here since the callback function
+                in the debug library will need it and we cannot guarantee
+                the child got scheduled before the debugger.  */
+             new_thread->p_pid = pid;
+
+             /* Now call the function which signals the event.  */
+             __linuxthreads_create_event ();
+
+             /* Now restart the thread.  */
+             __pthread_unlock(new_thread->p_lock);
+           }
+       }
+    }
+  if (pid == 0)
+PDEBUG("cloning new_thread = %p\n", new_thread);
+    pid = clone(pthread_start_thread, (void **) new_thread,
+                 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
+                 __pthread_sig_cancel, new_thread);
+  /* Check if cloning succeeded */
+  if (pid == -1) {
+    /* Free the stack if we allocated it */
+    if (attr == NULL || !attr->__stackaddr_set)
+      {
+#ifdef __UCLIBC_HAS_MMU__
+       if (new_thread->p_guardsize != 0)
+         munmap(new_thread->p_guardaddr, new_thread->p_guardsize);
+       munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
+              INITIAL_STACK_SIZE);
+#else
+       free(new_thread_bottom);
+#endif /* __UCLIBC_HAS_MMU__ */
+      }
+    __pthread_handles[sseg].h_descr = NULL;
+    __pthread_handles[sseg].h_bottom = NULL;
+    __pthread_handles_num--;
+    return errno;
+  }
+PDEBUG("new thread pid = %d\n", pid);
+  /* 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;
+}
+
+
+/* Try to free the resources of a thread when requested by pthread_join
+   or pthread_detach on a terminated thread. */
+
+static void pthread_free(pthread_descr th)
+{
+  pthread_handle handle;
+  pthread_readlock_info *iter, *next;
+  char *h_bottom_save;
+
+  ASSERT(th->p_exited);
+  /* Make the handle invalid */
+  handle =  thread_handle(th->p_tid);
+  __pthread_lock(&handle->h_lock, NULL);
+  h_bottom_save = handle->h_bottom;
+  handle->h_descr = NULL;
+  handle->h_bottom = (char *)(-1L);
+  __pthread_unlock(&handle->h_lock);
+#ifdef FREE_THREAD_SELF
+  FREE_THREAD_SELF(th, th->p_nr);
+#endif
+  /* One fewer threads in __pthread_handles */
+  __pthread_handles_num--;
+
+  /* Destroy read lock list, and list of free read lock structures.
+     If the former is not empty, it means the thread exited while
+     holding read locks! */
+
+  for (iter = th->p_readlock_list; iter != NULL; iter = next)
+    {
+      next = iter->pr_next;
+      free(iter);
+    }
+
+  for (iter = th->p_readlock_free; iter != NULL; iter = next)
+    {
+      next = iter->pr_next;
+      free(iter);
+    }
+
+  /* If initial thread, nothing to free */
+  if (th == &__pthread_initial_thread) return;
+#ifdef __UCLIBC_HAS_MMU__
+  if (!th->p_userstack)
+    {
+      /* Free the stack and thread descriptor area */
+      if (th->p_guardsize != 0)
+       munmap(th->p_guardaddr, th->p_guardsize);
+      munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE);
+    }
+#else
+  /* For non-MMU systems we always malloc the stack, so free it here. -StS */
+  if (!th->p_userstack) {
+      free(h_bottom_save);
+  }
+#endif /* __UCLIBC_HAS_MMU__ */
+}
+
+/* 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 */
+      __pthread_lock(th->p_lock, NULL);
+      th->p_exited = 1;
+      /* If we have to signal this event do it now.  */
+      if (th->p_report_events)
+       {
+         /* See whether TD_DEATH is in any of the mask.  */
+         int idx = __td_eventword (TD_REAP);
+         uint32_t mask = __td_eventmask (TD_REAP);
+
+         if ((mask & (__pthread_threads_events.event_bits[idx]
+                      | th->p_eventbuf.eventmask.event_bits[idx])) != 0)
+           {
+             /* Yep, we have to signal the death.  */
+             th->p_eventbuf.eventnum = TD_DEATH;
+             th->p_eventbuf.eventdata = th;
+             __pthread_last_event = th;
+
+             /* Now call the function to signal the event.  */
+             __linuxthreads_reap_event();
+           }
+       }
+      detached = th->p_detached;
+      __pthread_unlock(th->p_lock);
+      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;
+PDEBUG("\n");
+
+  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);
+    }
+  }
+}
+
+/* Try to free the resources of a thread when requested by pthread_join
+   or pthread_detach on a terminated thread. */
+
+static void pthread_handle_free(pthread_t th_id)
+{
+  pthread_handle handle = thread_handle(th_id);
+  pthread_descr th;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, th_id)) {
+    /* pthread_reap_children has deallocated the thread already,
+       nothing needs to be done */
+    __pthread_unlock(&handle->h_lock);
+    return;
+  }
+  th = handle->h_descr;
+  if (th->p_exited) {
+    __pthread_unlock(&handle->h_lock);
+    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;
+    __pthread_unlock(&handle->h_lock);
+  }
+}
+
+/* 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_cancel in thread manager thread */
+
+void __pthread_manager_sighandler(int sig)
+{
+  terminated_children = 1;
+}
+
+/* Adjust priority of thread manager so that it always run at a priority
+   higher than all threads */
+
+void __pthread_manager_adjust_prio(int thread_prio)
+{
+  struct sched_param param;
+
+  if (thread_prio <= __pthread_manager_thread.p_priority) return;
+  param.sched_priority =
+    thread_prio < sched_get_priority_max(SCHED_FIFO)
+    ? thread_prio + 1 : thread_prio;
+  sched_setscheduler(__pthread_manager_thread.p_pid, SCHED_FIFO, &param);
+  __pthread_manager_thread.p_priority = thread_prio;
+}
diff --git a/libpthread/linuxthreads/mutex.c b/libpthread/linuxthreads/mutex.c
new file mode 100644 (file)
index 0000000..2217a50
--- /dev/null
@@ -0,0 +1,201 @@
+/* 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.                 */
+
+/* changes for uClibc: remove strong_alias'es and define the real symbol */
+
+/* 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)
+{
+  __pthread_init_lock(&mutex->__m_lock);
+  mutex->__m_kind =
+    mutex_attr == NULL ? PTHREAD_MUTEX_FAST_NP : mutex_attr->__mutexkind;
+  mutex->__m_count = 0;
+  mutex->__m_owner = NULL;
+  return 0;
+}
+//strong_alias (__pthread_mutex_init, pthread_mutex_init)
+
+int pthread_mutex_destroy(pthread_mutex_t * mutex)
+{
+  if (mutex->__m_lock.__status != 0) return EBUSY;
+  return 0;
+}
+//strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy)
+
+int pthread_mutex_trylock(pthread_mutex_t * mutex)
+{
+  pthread_descr self;
+  int retcode;
+
+  switch(mutex->__m_kind) {
+  case PTHREAD_MUTEX_FAST_NP:
+    retcode = __pthread_trylock(&mutex->__m_lock);
+    return retcode;
+  case PTHREAD_MUTEX_RECURSIVE_NP:
+    self = thread_self();
+    if (mutex->__m_owner == self) {
+      mutex->__m_count++;
+      return 0;
+    }
+    retcode = __pthread_trylock(&mutex->__m_lock);
+    if (retcode == 0) {
+      mutex->__m_owner = self;
+      mutex->__m_count = 0;
+    }
+    return retcode;
+  case PTHREAD_MUTEX_ERRORCHECK_NP:
+    retcode = __pthread_trylock(&mutex->__m_lock);
+    if (retcode == 0) {
+      mutex->__m_owner = thread_self();
+    }
+    return retcode;
+  default:
+    return EINVAL;
+  }
+}
+//strong_alias (__pthread_mutex_trylock, pthread_mutex_trylock)
+
+int pthread_mutex_lock(pthread_mutex_t * mutex)
+{
+  pthread_descr self;
+
+  switch(mutex->__m_kind) {
+  case PTHREAD_MUTEX_FAST_NP:
+    __pthread_lock(&mutex->__m_lock, NULL);
+    return 0;
+  case PTHREAD_MUTEX_RECURSIVE_NP:
+    self = thread_self();
+    if (mutex->__m_owner == self) {
+      mutex->__m_count++;
+      return 0;
+    }
+    __pthread_lock(&mutex->__m_lock, self);
+    mutex->__m_owner = self;
+    mutex->__m_count = 0;
+    return 0;
+  case PTHREAD_MUTEX_ERRORCHECK_NP:
+    self = thread_self();
+    if (mutex->__m_owner == self) return EDEADLK;
+    __pthread_lock(&mutex->__m_lock, self);
+    mutex->__m_owner = self;
+    return 0;
+  default:
+    return EINVAL;
+  }
+}
+//strong_alias (__pthread_mutex_lock, pthread_mutex_lock)
+
+int pthread_mutex_unlock(pthread_mutex_t * mutex)
+{
+  switch (mutex->__m_kind) {
+  case PTHREAD_MUTEX_FAST_NP:
+    __pthread_unlock(&mutex->__m_lock);
+    return 0;
+  case PTHREAD_MUTEX_RECURSIVE_NP:
+    if (mutex->__m_count > 0) {
+      mutex->__m_count--;
+      return 0;
+    }
+    mutex->__m_owner = NULL;
+    __pthread_unlock(&mutex->__m_lock);
+    return 0;
+  case PTHREAD_MUTEX_ERRORCHECK_NP:
+    if (mutex->__m_owner != thread_self() || mutex->__m_lock.__status == 0)
+      return EPERM;
+    mutex->__m_owner = NULL;
+    __pthread_unlock(&mutex->__m_lock);
+    return 0;
+  default:
+    return EINVAL;
+  }
+}
+//strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
+
+int pthread_mutexattr_init(pthread_mutexattr_t *attr)
+{
+  attr->__mutexkind = PTHREAD_MUTEX_FAST_NP;
+  return 0;
+}
+//strong_alias (__pthread_mutexattr_init, pthread_mutexattr_init)
+
+int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
+{
+  return 0;
+}
+//strong_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy)
+
+int __pthread_mutexattr_settype(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_settype, pthread_mutexattr_settype)
+weak_alias ( __pthread_mutexattr_settype, __pthread_mutexattr_setkind_np)
+weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np)
+
+int __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind)
+{
+  *kind = attr->__mutexkind;
+  return 0;
+}
+weak_alias (__pthread_mutexattr_gettype, pthread_mutexattr_gettype)
+weak_alias (__pthread_mutexattr_gettype, __pthread_mutexattr_getkind_np)
+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;
+}
+//strong_alias (__pthread_once, pthread_once)
diff --git a/libpthread/linuxthreads/no-tsd.c b/libpthread/linuxthreads/no-tsd.c
new file mode 100644 (file)
index 0000000..ef79cb8
--- /dev/null
@@ -0,0 +1,33 @@
+/* libc-internal interface for thread-specific data.
+   Copyright (C) 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.  */
+
+#include <bits/libc-tsd.h>
+
+/* This file provides uinitialized (common) definitions for the
+   hooks used internally by libc to access thread-specific data.
+
+   When -lpthread is used, it provides initialized definitions for these
+   variables (in specific.c), which override these uninitialized definitions.
+
+   If -lpthread is not used, these uninitialized variables default to zero,
+   which the __libc_tsd_* macros check for.   */
+
+void *(*__libc_internal_tsd_get) __P ((enum __libc_tsd_key_t));
+int (*__libc_internal_tsd_set) __P ((enum __libc_tsd_key_t,
+                                    __const void *));
diff --git a/libpthread/linuxthreads/oldsemaphore.c b/libpthread/linuxthreads/oldsemaphore.c
new file mode 100644 (file)
index 0000000..a634bad
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * This file contains the old semaphore code that we need to
+ * preserve for glibc-2.0 backwards compatibility. Port to glibc 2.1
+ * done by Cristian Gafton.
+ */
+
+/* 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 <errno.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+#include "queue.h"
+
+typedef struct {
+    long int sem_status;
+    int sem_spinlock;
+} old_sem_t;
+
+/* Maximum value the semaphore can have.  */
+#define SEM_VALUE_MAX   ((int) ((~0u) >> 1))
+
+static inline int sem_compare_and_swap(old_sem_t *sem, long oldval, long newval)
+{
+    return compare_and_swap(&sem->sem_status, oldval, newval, &sem->sem_spinlock);
+}
+
+/* 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 __old_sem_init(old_sem_t *sem, int pshared, unsigned int value)
+{
+    if (value > SEM_VALUE_MAX) {
+       errno = EINVAL;
+       return -1;
+    }
+    if (pshared) {
+       errno = ENOSYS;
+       return -1;
+    }
+  sem->sem_spinlock = 0;
+  sem->sem_status = ((long)value << 1) + 1;
+  return 0;
+}
+
+/* Function called by pthread_cancel to remove the thread from
+   waiting inside __old_sem_wait. Here we simply unconditionally
+   indicate that the thread is to be woken, by returning 1. */
+
+static int old_sem_extricate_func(void *obj, pthread_descr th)
+{
+    return 1;
+}
+
+int __old_sem_wait(old_sem_t * sem)
+{
+    long oldstatus, newstatus;
+    volatile pthread_descr self = thread_self();
+    pthread_descr * th;
+    pthread_extricate_if extr;
+
+    /* Set up extrication interface */
+    extr.pu_object = 0;
+    extr.pu_extricate_func = old_sem_extricate_func;
+
+    while (1) {
+       /* Register extrication interface */
+       __pthread_set_own_extricate_if(self, &extr); 
+       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. */
+         __pthread_set_own_extricate_if(self, 0); 
+           return 0;
+       }
+       /* Wait for sem_post or cancellation */
+       suspend(self);
+       __pthread_set_own_extricate_if(self, 0); 
+
+       /* 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 __old_sem_trywait(old_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 __old_sem_post(old_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 __old_sem_getvalue(old_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 __old_sem_destroy(old_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);
+  }
+}
+
+#if defined PIC && DO_VERSIONING
+symbol_version (__old_sem_init, sem_init, GLIBC_2.0);
+symbol_version (__old_sem_wait, sem_wait, GLIBC_2.0);
+symbol_version (__old_sem_trywait, sem_trywait, GLIBC_2.0);
+symbol_version (__old_sem_post, sem_post, GLIBC_2.0);
+symbol_version (__old_sem_getvalue, sem_getvalue, GLIBC_2.0);
+symbol_version (__old_sem_destroy, sem_destroy, GLIBC_2.0);
+#endif
+
diff --git a/libpthread/linuxthreads/pt-machine.c b/libpthread/linuxthreads/pt-machine.c
new file mode 100644 (file)
index 0000000..438008d
--- /dev/null
@@ -0,0 +1,22 @@
+/* "Instantiation of machine-dependent pthreads inline functions.
+   Copyright (C) 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.  */
+
+#define PT_EI
+
+#include <pt-machine.h>
diff --git a/libpthread/linuxthreads/ptfork.c b/libpthread/linuxthreads/ptfork.c
new file mode 100644 (file)
index 0000000..0c4e252
--- /dev/null
@@ -0,0 +1,107 @@
+/* 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.                 */
+
+/* mods for uClibc: removed strong alias and defined funcs properly */
+
+/* The "atfork" stuff */
+
+#include <errno.h>
+#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;
+}
+//strong_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 __libc_fork(void);
+
+pid_t __fork(void)
+{
+  pid_t 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 = __libc_fork();
+  if (pid == 0) {
+    __pthread_reset_main_thread();
+    __fresetlockfiles();
+    pthread_call_handlers(child);
+  } else {
+    pthread_call_handlers(parent);
+  }
+  return pid;
+}
+weak_alias (__fork, fork);
+
+pid_t __vfork(void)
+{
+  return __fork();
+}
+weak_alias (__vfork, vfork);
diff --git a/libpthread/linuxthreads/pthread.c b/libpthread/linuxthreads/pthread.c
new file mode 100644 (file)
index 0000000..51d600a
--- /dev/null
@@ -0,0 +1,876 @@
+/* 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 */
+
+#define __FORCE_GLIBC
+#include <features.h>
+#include <errno.h>
+#include <netdb.h>     /* for h_errno */
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+#include "debug.h"      /* added to linuxthreads -StS */
+
+/* mods for uClibc: getpwd and getpagesize are the syscalls */
+#define __getpid getpid
+#define __getpagesize getpagesize
+/* mods for uClibc: __libc_sigaction is not in any standard headers */
+#include <signal.h>
+extern int __libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact);
+
+
+/* These variables are used by the setup code.  */
+extern int _errno;
+extern int _h_errno;
+
+
+/* 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 */
+  NULL,                              /* pthread_descr p_nextlock */
+  PTHREAD_THREADS_MAX,        /* pthread_t p_tid */
+  0,                          /* int p_pid */
+  0,                          /* int p_priority */
+  &__pthread_handles[0].h_lock, /* struct _pthread_fastlock * p_lock */
+  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 */
+  &_errno,                       /* int *p_errnop */
+  0,                          /* int p_errno */
+  &_h_errno,                       /* int *p_h_errnop */
+  0,                          /* int p_h_errno */
+  NULL,                       /* char * p_in_sighandler */
+  0,                          /* char p_sigwaiting */
+  PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
+  {NULL},                     /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */
+  {NULL},                     /* void * p_libc_specific[_LIBC_TSD_KEY_N] */
+  0,                          /* int p_userstack */
+  NULL,                       /* void * p_guardaddr */
+  0,                          /* size_t p_guardsize */
+  &__pthread_initial_thread,  /* pthread_descr p_self */
+  0,                          /* Always index 0 */
+  0,                          /* int p_report_events */
+  {{{0, }}, 0, NULL},         /* td_eventbuf_t p_eventbuf */
+  ATOMIC_INITIALIZER,         /* struct pthread_atomic p_resume_count */
+  0,                          /* char p_woken_by_cancel */
+  NULL,                       /* struct pthread_extricate_if *p_extricate */
+  NULL,                              /* pthread_readlock_info *p_readlock_list; */
+  NULL,                       /* pthread_readlock_info *p_readlock_free; */
+  0                           /* int p_untracked_readlock_count; */
+};
+
+/* Descriptor of the manager thread; none of this is used but the error
+   variables, the p_pid and p_priority fields,
+   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 */
+  NULL,                              /* pthread_descr p_nextlock */
+  0,                          /* int p_tid */
+  0,                          /* int p_pid */
+  0,                          /* int p_priority */
+  &__pthread_handles[1].h_lock, /* struct _pthread_fastlock * p_lock */
+  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 */
+  &__pthread_manager_thread.p_errno, /* int *p_errnop */
+  0,                          /* int p_errno */
+  NULL,                       /* int *p_h_errnop */
+  0,                          /* int p_h_errno */
+  NULL,                       /* char * p_in_sighandler */
+  0,                          /* char p_sigwaiting */
+  PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
+  {NULL},                     /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */
+  {NULL},                     /* void * p_libc_specific[_LIBC_TSD_KEY_N] */
+  0,                          /* int p_userstack */
+  NULL,                       /* void * p_guardaddr */
+  0,                          /* size_t p_guardsize */
+  &__pthread_manager_thread,  /* pthread_descr p_self */
+  1,                          /* Always index 1 */
+  0,                          /* int p_report_events */
+  {{{0, }}, 0, NULL},         /* td_eventbuf_t p_eventbuf */
+  ATOMIC_INITIALIZER,         /* struct pthread_atomic p_resume_count */
+  0,                          /* char p_woken_by_cancel */
+  NULL,                       /* struct pthread_extricate_if *p_extricate */
+  NULL,                              /* pthread_readlock_info *p_readlock_list; */
+  NULL,                       /* pthread_readlock_info *p_readlock_free; */
+  0                           /* int p_untracked_readlock_count; */
+};
+
+/* 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;
+
+/* For non-MMU systems also remember to stack top of the initial thread.
+ * This is adapted when other stacks are malloc'ed since we don't know
+ * the bounds a-priori. -StS */
+
+#ifndef __UCLIBC_HAS_MMU__
+char *__pthread_initial_thread_tos = NULL;
+#endif /* __UCLIBC_HAS_MMU__ */
+
+/* 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;
+
+/* 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;
+
+/* Pointers that select new or old suspend/resume functions
+   based on availability of rt signals. */
+
+void (*__pthread_restart)(pthread_descr) = __pthread_restart_old;
+void (*__pthread_suspend)(pthread_descr) = __pthread_suspend_old;
+
+/* Communicate relevant LinuxThreads constants to gdb */
+
+const int __pthread_threads_max = PTHREAD_THREADS_MAX;
+const int __pthread_sizeof_handle = sizeof(struct pthread_handle_struct);
+const int __pthread_offsetof_descr = offsetof(struct pthread_handle_struct,
+                                              h_descr);
+const int __pthread_offsetof_pid = offsetof(struct _pthread_descr_struct,
+                                            p_pid);
+
+/* Forward declarations */
+
+static void pthread_exit_process(int retcode, void *arg);
+#ifndef __i386__
+static void pthread_handle_sigcancel(int sig);
+static void pthread_handle_sigrestart(int sig);
+#else
+static void pthread_handle_sigcancel(int sig, struct sigcontext ctx);
+static void pthread_handle_sigrestart(int sig, struct sigcontext ctx);
+#endif
+static void pthread_handle_sigdebug(int sig);
+
+/* Signal numbers used for the communication.
+   In these variables we keep track of the used variables.  If the
+   platform does not support any real-time signals we will define the
+   values to some unreasonable value which will signal failing of all
+   the functions below.  */
+#ifndef __SIGRTMIN
+static int current_rtmin = -1;
+static int current_rtmax = -1;
+int __pthread_sig_restart = SIGUSR1;
+int __pthread_sig_cancel = SIGUSR2;
+int __pthread_sig_debug = 0;
+#else
+static int current_rtmin;
+static int current_rtmax;
+
+#if __SIGRTMAX - __SIGRTMIN >= 3
+int __pthread_sig_restart = __SIGRTMIN;
+int __pthread_sig_cancel = __SIGRTMIN + 1;
+int __pthread_sig_debug = __SIGRTMIN + 2;
+#else
+int __pthread_sig_restart = SIGUSR1;
+int __pthread_sig_cancel = SIGUSR2;
+int __pthread_sig_debug = 0;
+#endif
+
+static int rtsigs_initialized;
+
+#include "testrtsig.h"
+
+static void
+init_rtsigs (void)
+{
+  if (!kernel_has_rtsig ())
+    {
+      current_rtmin = -1;
+      current_rtmax = -1;
+#if __SIGRTMAX - __SIGRTMIN >= 3
+      __pthread_sig_restart = SIGUSR1;
+      __pthread_sig_cancel = SIGUSR2;
+      __pthread_sig_debug = 0;
+#endif
+PDEBUG("no rt-sigs, sig_restart=%d, sig_cancel=%d.\n", __pthread_sig_restart, __pthread_sig_cancel  );
+      __pthread_init_condvar(0);
+    }
+  else
+    {
+#if __SIGRTMAX - __SIGRTMIN >= 3
+      current_rtmin = __SIGRTMIN + 3;
+      __pthread_restart = __pthread_restart_new;
+      __pthread_suspend = __pthread_wait_for_restart_signal;
+      __pthread_init_condvar(1);
+#else
+      current_rtmin = __SIGRTMIN;
+      __pthread_init_condvar(0);
+#endif
+
+      current_rtmax = __SIGRTMAX;
+PDEBUG("have rt-sigs, rtmin = %d, rtmax = %d.\n", current_rtmin, current_rtmax);
+    }
+
+  rtsigs_initialized = 1;
+}
+#endif
+
+/* Return number of available real-time signal with highest priority.  */
+int
+__libc_current_sigrtmin (void)
+{
+#ifdef __SIGRTMIN
+  if (!rtsigs_initialized)
+    init_rtsigs ();
+#endif
+  return current_rtmin;
+}
+
+/* Return number of available real-time signal with lowest priority.  */
+int
+__libc_current_sigrtmax (void)
+{
+#ifdef __SIGRTMIN
+  if (!rtsigs_initialized)
+    init_rtsigs ();
+#endif
+  return current_rtmax;
+}
+
+/* Allocate real-time signal with highest/lowest available
+   priority.  Please note that we don't use a lock since we assume
+   this function to be called at program start.  */
+int
+__libc_allocate_rtsig (int high)
+{
+#ifndef __SIGRTMIN
+  return -1;
+#else
+  if (!rtsigs_initialized)
+    init_rtsigs ();
+  if (current_rtmin == -1 || current_rtmin > current_rtmax)
+    /* We don't have anymore signal available.  */
+    return -1;
+
+  return high ? current_rtmin++ : current_rtmax--;
+#endif
+}
+
+/* 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;
+  struct rlimit limit;
+  int max_stack;
+
+  /* If already done (e.g. by a constructor called earlier!), bail out */
+  if (__pthread_initial_thread_bos != NULL) return;
+#ifdef TEST_FOR_COMPARE_AND_SWAP
+  /* Test if compare-and-swap is available */
+  __pthread_has_cas = compare_and_swap_is_available();
+#endif
+  /* 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, 0);
+#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;
+  /* Play with the stack size limit to make sure that no stack ever grows
+     beyond STACK_SIZE minus two pages (one page for the thread descriptor
+     immediately beyond, and one page to act as a guard page). */
+
+#ifdef __UCLIBC_HAS_MMU__
+  /* We cannot allocate a huge chunk of memory to mmap all thread stacks later
+   * on a non-MMU system. Thus, we don't need the rlimit either. -StS */
+  getrlimit(RLIMIT_STACK, &limit);
+  max_stack = STACK_SIZE - 2 * __getpagesize();
+  if (limit.rlim_cur > max_stack) {
+    limit.rlim_cur = max_stack;
+    setrlimit(RLIMIT_STACK, &limit);
+  }
+#else
+  /* For non-MMU assume __pthread_initial_thread_tos at upper page boundary, and
+   * __pthread_initial_thread_bos at address 0. These bounds are refined as we 
+   * malloc other stack frames such that they don't overlap. -StS
+   */
+  __pthread_initial_thread_tos =
+    (char *)(((long)CURRENT_STACK_FRAME + __getpagesize()) & ~(__getpagesize() - 1));
+  __pthread_initial_thread_bos = (char *) 1; /* set it non-zero so we know we have been here */
+  PDEBUG("initial thread stack bounds: bos=%p, tos=%p\n",
+        __pthread_initial_thread_bos, __pthread_initial_thread_tos);
+#endif /* __UCLIBC_HAS_MMU__ */
+
+#ifdef __SIGRTMIN
+  /* Initialize real-time signals. */
+  init_rtsigs ();
+#endif
+  /* Setup signal handlers for the initial thread.
+     Since signal handlers are shared between threads, these settings
+     will be inherited by all other threads. */
+#ifndef __i386__
+  sa.sa_handler = pthread_handle_sigrestart;
+#else
+  sa.sa_handler = (__sighandler_t) pthread_handle_sigrestart;
+#endif
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  __libc_sigaction(__pthread_sig_restart, &sa, NULL);
+#ifndef __i386__
+  sa.sa_handler = pthread_handle_sigcancel;
+#else
+  sa.sa_handler = (__sighandler_t) pthread_handle_sigcancel;
+#endif
+  sa.sa_flags = 0;
+  __libc_sigaction(__pthread_sig_cancel, &sa, NULL);
+  if (__pthread_sig_debug > 0) {
+    sa.sa_handler = pthread_handle_sigdebug;
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;
+    __libc_sigaction(__pthread_sig_debug, &sa, NULL);
+  }
+  /* Initially, block __pthread_sig_restart. Will be unblocked on demand. */
+  sigemptyset(&mask);
+  sigaddset(&mask, __pthread_sig_restart);
+PDEBUG("block mask = %x\n", mask);
+  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);
+}
+
+void __pthread_initialize(void)
+{
+  pthread_initialize();
+}
+
+int __pthread_initialize_manager(void)
+{
+  int manager_pipe[2];
+  int pid;
+  struct pthread_request request;
+
+  /* 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;
+
+  /* On non-MMU systems we make sure that the initial thread bounds don't overlap
+   * with the manager stack frame */
+  NOMMU_INITIAL_THREAD_BOUNDS(__pthread_manager_thread_tos,__pthread_manager_thread_bos);
+  PDEBUG("manager stack: size=%d, bos=%p, tos=%p\n", THREAD_MANAGER_STACK_SIZE,
+        __pthread_manager_thread_bos, __pthread_manager_thread_tos);
+  PDEBUG("initial stack: estimate bos=%p, tos=%p\n",
+        __pthread_initial_thread_bos, __pthread_initial_thread_tos);
+
+  /* Setup pipe to communicate with thread manager */
+  if (pipe(manager_pipe) == -1) {
+    free(__pthread_manager_thread_bos);
+    return -1;
+  }
+  /* Start the thread manager */
+  pid = 0;
+  if (__pthread_initial_thread.p_report_events)
+    {
+      /* It's a bit more complicated.  We have to report the creation of
+        the manager thread.  */
+      int idx = __td_eventword (TD_CREATE);
+      uint32_t mask = __td_eventmask (TD_CREATE);
+
+      if ((mask & (__pthread_threads_events.event_bits[idx]
+                  | __pthread_initial_thread.p_eventbuf.eventmask.event_bits[idx]))
+         != 0)
+       {
+         pid = clone(__pthread_manager_event,
+                       (void **) __pthread_manager_thread_tos,
+                       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
+                       (void *)(long)manager_pipe[0]);
+
+         if (pid != -1)
+           {
+             /* Now fill in the information about the new thread in
+                the newly created thread's data structure.  We cannot let
+                the new thread do this since we don't know whether it was
+                already scheduled when we send the event.  */
+             __pthread_manager_thread.p_eventbuf.eventdata =
+               &__pthread_manager_thread;
+             __pthread_manager_thread.p_eventbuf.eventnum = TD_CREATE;
+             __pthread_last_event = &__pthread_manager_thread;
+             __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1;
+             __pthread_manager_thread.p_pid = pid;
+
+             /* Now call the function which signals the event.  */
+             __linuxthreads_create_event ();
+
+             /* Now restart the thread.  */
+             __pthread_unlock(__pthread_manager_thread.p_lock);
+           }
+       }
+    }
+
+  if (pid == 0) {
+    pid = clone(__pthread_manager, (void **) __pthread_manager_thread_tos,
+                 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
+                 (void *)(long)manager_pipe[0]);
+  }
+  if (pid == -1) {
+    free(__pthread_manager_thread_bos);
+    __libc_close(manager_pipe[0]);
+    __libc_close(manager_pipe[1]);
+    return -1;
+  }
+  __pthread_manager_request = manager_pipe[1]; /* writing end */
+  __pthread_manager_reader = manager_pipe[0]; /* reading end */
+  __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1;
+  __pthread_manager_thread.p_pid = pid;
+  /* Make gdb aware of new thread manager */
+  if (__pthread_threads_debug && __pthread_sig_debug > 0)
+    {
+      raise(__pthread_sig_debug);
+      /* We suspend ourself and gdb will wake us up when it is
+        ready to handle us. */
+      __pthread_wait_for_restart_signal(thread_self());
+    }
+  /* Synchronize debugging of the thread manager */
+PDEBUG("send REQ_DEBUG to manager thread\n");
+  request.req_kind = REQ_DEBUG;
+  __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
+  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);
+  PDEBUG("write REQ_CREATE to manager thread\n");
+  __libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
+PDEBUG("before suspend(self)\n");
+  suspend(self);
+PDEBUG("after suspend(self)\n");
+  if (THREAD_GETMEM(self, p_retcode) == 0)
+    *thread = (pthread_t) THREAD_GETMEM(self, p_retval);
+  return THREAD_GETMEM(self, p_retcode);
+}
+
+/* Simple operations on thread identifiers */
+
+pthread_t pthread_self(void)
+{
+  pthread_descr self = thread_self();
+  return THREAD_GETMEM(self, p_tid);
+}
+
+int pthread_equal(pthread_t thread1, pthread_t thread2)
+{
+  return thread1 == thread2;
+}
+
+/* Helper function for thread_self in the case of user-provided stacks */
+
+#ifndef THREAD_SELF
+
+pthread_descr __pthread_find_self()
+{
+  char * sp = CURRENT_STACK_FRAME;
+  pthread_handle h;
+
+  /* __pthread_handles[0] is the initial thread, __pthread_handles[1] is
+     the manager threads handled specially in thread_self(), so start at 2 */
+  h = __pthread_handles + 2;
+  while (! (sp <= (char *) h->h_descr && sp >= h->h_bottom)) h++;
+
+#ifdef DEBUG_PT
+  if (h->h_descr == NULL) {
+      printf("*** "__FUNCTION__" ERROR descriptor is NULL!!!!! ***\n\n");
+      _exit(1);
+  }
+#endif
+
+  return h->h_descr;
+}
+
+#endif
+
+/* Thread scheduling */
+
+int pthread_setschedparam(pthread_t thread, int policy,
+                          const struct sched_param *param)
+{
+  pthread_handle handle = thread_handle(thread);
+  pthread_descr th;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, thread)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+  th = handle->h_descr;
+  if (sched_setscheduler(th->p_pid, policy, param) == -1) {
+    __pthread_unlock(&handle->h_lock);
+    return errno;
+  }
+  th->p_priority = policy == SCHED_OTHER ? 0 : param->sched_priority;
+  __pthread_unlock(&handle->h_lock);
+  if (__pthread_manager_request >= 0)
+    __pthread_manager_adjust_prio(th->p_priority);
+  return 0;
+}
+
+int pthread_getschedparam(pthread_t thread, int *policy,
+                          struct sched_param *param)
+{
+  pthread_handle handle = thread_handle(thread);
+  int pid, pol;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, thread)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+  pid = handle->h_descr->p_pid;
+  __pthread_unlock(&handle->h_lock);
+  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_thread.p_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). */
+
+#ifndef __i386__
+static void pthread_handle_sigrestart(int sig)
+{
+  pthread_descr self = thread_self();
+  PDEBUG("got called in non-i386 mode for %u\n", self);
+#else
+static void pthread_handle_sigrestart(int sig, struct sigcontext ctx)
+{
+  pthread_descr self;
+  asm volatile ("movw %w0,%%gs" : : "r" (ctx.gs));
+  self = thread_self();
+  PDEBUG("got called in i386-mode for %u\n", self);
+#endif
+  THREAD_SETMEM(self, p_signal, sig);
+  if (THREAD_GETMEM(self, p_signal_jmp) != NULL)
+    siglongjmp(*THREAD_GETMEM(self, p_signal_jmp), 1);
+}
+
+/* The handler for the CANCEL signal checks for cancellation
+   (in asynchronous mode), for process-wide exit and exec requests.
+   For the thread manager thread, redirect the signal to
+   __pthread_manager_sighandler. */
+
+#ifndef __i386__
+static void pthread_handle_sigcancel(int sig)
+{
+  pthread_descr self = thread_self();
+  sigjmp_buf * jmpbuf;
+#else
+static void pthread_handle_sigcancel(int sig, struct sigcontext ctx)
+{
+  pthread_descr self;
+  sigjmp_buf * jmpbuf;
+  asm volatile ("movw %w0,%%gs" : : "r" (ctx.gs));
+  self = thread_self();
+#endif
+
+  if (self == &__pthread_manager_thread)
+    {
+      __pthread_manager_sighandler(sig);
+      return;
+    }
+  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_thread.p_pid, NULL, __WCLONE);
+    _exit(__pthread_exit_code);
+  }
+  if (THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
+    if (THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS)
+      pthread_exit(PTHREAD_CANCELED);
+    jmpbuf = THREAD_GETMEM(self, p_cancel_jmp);
+    if (jmpbuf != NULL) {
+      THREAD_SETMEM(self, p_cancel_jmp, NULL);
+      siglongjmp(*jmpbuf, 1);
+    }
+  }
+}
+
+/* Handler for the DEBUG signal.
+   The debugging strategy is as follows:
+   On reception of a REQ_DEBUG request (sent by new threads created to
+   the thread manager under debugging mode), the thread manager throws
+   __pthread_sig_debug to itself. The debugger (if active) intercepts
+   this signal, takes into account new threads and continue execution
+   of the thread manager by propagating the signal because it doesn't
+   know what it is specifically done for. In the current implementation,
+   the thread manager simply discards it. */
+
+static void pthread_handle_sigdebug(int sig)
+{
+  /* Nothing */
+}
+
+/* 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 */
+  THREAD_SETMEM(self, p_pid, __getpid());
+  /* Make the forked thread the main thread */
+  __pthread_main_thread = self;
+  THREAD_SETMEM(self, p_nextlive, self);
+  THREAD_SETMEM(self, p_prevlive, self);
+  /* Now this thread modifies the global variables.  */
+  THREAD_SETMEM(self, p_errnop, &_errno);
+  THREAD_SETMEM(self, p_h_errnop, &_h_errno);
+}
+
+/* Process-wide exec() request */
+
+void __pthread_kill_other_threads_np(void)
+{
+  struct sigaction sa;
+  /* 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();
+  /* Reset the signal handlers behaviour for the signals the
+     implementation uses since this would be passed to the new
+     process.  */
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sa.sa_handler = SIG_DFL;
+  __libc_sigaction(__pthread_sig_restart, &sa, NULL);
+  __libc_sigaction(__pthread_sig_cancel, &sa, NULL);
+  if (__pthread_sig_debug > 0)
+    __libc_sigaction(__pthread_sig_debug, &sa, NULL);
+}
+weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np)
+
+/* Concurrency symbol level.  */
+static int current_level;
+
+int __pthread_setconcurrency(int level)
+{
+  /* We don't do anything unless we have found a useful interpretation.  */
+  current_level = level;
+  return 0;
+}
+weak_alias (__pthread_setconcurrency, pthread_setconcurrency)
+
+int __pthread_getconcurrency(void)
+{
+  return current_level;
+}
+weak_alias (__pthread_getconcurrency, pthread_getconcurrency)
+
+void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif)
+{
+  __pthread_lock(self->p_lock, self);
+  THREAD_SETMEM(self, p_extricate, peif);
+  __pthread_unlock(self->p_lock);
+}
+
+/* Primitives for controlling thread execution */
+
+void __pthread_wait_for_restart_signal(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;
+    PDEBUG("temporary block mask = %x\n", mask);
+    sigsuspend(&mask);                   /* Wait for signal */
+    PDEBUG(" *** after sigsuspend *** \n");
+  } while (self->p_signal !=__pthread_sig_restart );
+}
+
+/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT signals.
+   On these kernels, we use SIGUSR1 and SIGUSR2 for restart and cancellation.
+   Since the restart signal does not queue, we use an atomic counter to create
+   queuing semantics. This is needed to resolve a rare race condition in
+   pthread_cond_timedwait_relative. */
+
+void __pthread_restart_old(pthread_descr th)
+{
+  if (atomic_increment(&th->p_resume_count) == -1)
+    kill(th->p_pid, __pthread_sig_restart);
+}
+
+void __pthread_suspend_old(pthread_descr self)
+{
+  if (atomic_decrement(&self->p_resume_count) <= 0)
+    __pthread_wait_for_restart_signal(self);
+}
+
+void __pthread_restart_new(pthread_descr th)
+{
+    kill(th->p_pid, __pthread_sig_restart);
+}
+
+/* There is no __pthread_suspend_new because it would just
+   be a wasteful wrapper for __pthread_wait_for_restart_signal */
+
+/* Debugging aid */
+
+#ifdef DEBUG_PT
+#include <stdarg.h>
+
+void __pthread_message(char * fmt, ...)
+{
+  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/libpthread/linuxthreads/ptlongjmp.c b/libpthread/linuxthreads/ptlongjmp.c
new file mode 100644 (file)
index 0000000..c0ea822
--- /dev/null
@@ -0,0 +1,55 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1998 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.                 */
+
+/* Redefine siglongjmp and longjmp so that they interact correctly
+   with cleanup handlers */
+
+#include <setjmp.h>
+#include "pthread.h"
+#include "internals.h"
+
+/* These functions are not declared anywhere since they shouldn't be
+   used at another place but here.  */
+extern void __libc_siglongjmp (sigjmp_buf env, int val)
+     __attribute__ ((noreturn));
+extern void __libc_longjmp (sigjmp_buf env, int val)
+     __attribute__ ((noreturn));
+
+
+static void pthread_cleanup_upto(__jmp_buf target)
+{
+  pthread_descr self = thread_self();
+  struct _pthread_cleanup_buffer * c;
+
+  for (c = THREAD_GETMEM(self, p_cleanup);
+       c != NULL && _JMPBUF_UNWINDS(target, c);
+       c = c->__prev)
+    c->__routine(c->__arg);
+  THREAD_SETMEM(self, p_cleanup, c);
+  if (THREAD_GETMEM(self, p_in_sighandler)
+      && _JMPBUF_UNWINDS(target, THREAD_GETMEM(self, p_in_sighandler)))
+    THREAD_SETMEM(self, p_in_sighandler, NULL);
+}
+
+void siglongjmp(sigjmp_buf env, int val)
+{
+  pthread_cleanup_upto(env->__jmpbuf);
+  __libc_siglongjmp(env, val);
+}
+
+void longjmp(jmp_buf env, int val)
+{
+  pthread_cleanup_upto(env->__jmpbuf);
+  __libc_siglongjmp(env, val);
+}
diff --git a/libpthread/linuxthreads/queue.h b/libpthread/linuxthreads/queue.h
new file mode 100644 (file)
index 0000000..28bd755
--- /dev/null
@@ -0,0 +1,61 @@
+/* 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 */
+
+/* Waiting queues are represented by lists of thread descriptors
+   linked through their p_nextwaiting field.  The lists are kept
+   sorted by decreasing priority, and then decreasing waiting time. */
+
+static inline void enqueue(pthread_descr * q, pthread_descr th)
+{
+  int prio = th->p_priority;
+  ASSERT(th->p_nextwaiting == NULL);
+  for (; *q != NULL; q = &((*q)->p_nextwaiting)) {
+    if (prio > (*q)->p_priority) {
+      th->p_nextwaiting = *q;
+      *q = th;
+      return;
+    }
+  }
+  *q = th;
+}
+
+static inline pthread_descr dequeue(pthread_descr * q)
+{
+  pthread_descr th;
+  th = *q;
+  if (th != NULL) {
+    *q = th->p_nextwaiting;
+    th->p_nextwaiting = NULL;
+  }
+  return th;
+}
+
+static inline int remove_from_queue(pthread_descr * q, pthread_descr th)
+{
+  for (; *q != NULL; q = &((*q)->p_nextwaiting)) {
+    if (*q == th) {
+      *q = th->p_nextwaiting;
+      th->p_nextwaiting = NULL;
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static inline int queue_is_empty(pthread_descr * q)
+{
+    return *q == NULL;
+}
diff --git a/libpthread/linuxthreads/restart.h b/libpthread/linuxthreads/restart.h
new file mode 100644 (file)
index 0000000..702d7d1
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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)
+{
+  __pthread_restart(th); /* see pthread.c */ 
+}
+
+static inline void suspend(pthread_descr self)
+{
+  __pthread_suspend(self); /* see pthread.c */
+}
diff --git a/libpthread/linuxthreads/rwlock.c b/libpthread/linuxthreads/rwlock.c
new file mode 100644 (file)
index 0000000..977fd88
--- /dev/null
@@ -0,0 +1,486 @@
+/* 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 <stdlib.h>
+#include "internals.h"
+#include "queue.h"
+#include "spinlock.h"
+#include "restart.h"
+
+/*
+ * Check whether the calling thread already owns one or more read locks on the
+ * specified lock. If so, return a pointer to the read lock info structure
+ * corresponding to that lock.
+ */
+
+static pthread_readlock_info *
+rwlock_is_in_list(pthread_descr self, pthread_rwlock_t *rwlock)
+{
+  pthread_readlock_info *info;
+
+  for (info = self->p_readlock_list; info != NULL; info = info->pr_next)
+    {
+      if (info->pr_lock == rwlock)
+       return info;
+    }
+
+  return NULL;
+}
+
+/*
+ * Add a new lock to the thread's list of locks for which it has a read lock.
+ * A new info node must be allocated for this, which is taken from the thread's
+ * free list, or by calling malloc. If malloc fails, a null pointer is
+ * returned. Otherwise the lock info structure is initialized and pushed
+ * onto the thread's list.
+ */
+
+static pthread_readlock_info *
+rwlock_add_to_list(pthread_descr self, pthread_rwlock_t *rwlock)
+{
+  pthread_readlock_info *info = self->p_readlock_free;
+
+  if (info != NULL) 
+    self->p_readlock_free = info->pr_next;
+  else
+    info = malloc(sizeof *info);
+
+  if (info == NULL)
+    return NULL;
+
+  info->pr_lock_count = 1;
+  info->pr_lock = rwlock;
+  info->pr_next = self->p_readlock_list;
+  self->p_readlock_list = info;
+
+  return info;
+}
+
+/*
+ * If the thread owns a read lock over the given pthread_rwlock_t,
+ * and this read lock is tracked in the thread's lock list,
+ * this function returns a pointer to the info node in that list.
+ * It also decrements the lock count within that node, and if
+ * it reaches zero, it removes the node from the list.
+ * If nothing is found, it returns a null pointer.
+ */
+
+static pthread_readlock_info *
+rwlock_remove_from_list(pthread_descr self, pthread_rwlock_t *rwlock)
+{
+  pthread_readlock_info **pinfo;
+
+  for (pinfo = &self->p_readlock_list; *pinfo != NULL; pinfo = &(*pinfo)->pr_next)
+    {
+      if ((*pinfo)->pr_lock == rwlock)
+       {
+         pthread_readlock_info *info = *pinfo;
+         if (--info->pr_lock_count == 0)
+           *pinfo = info->pr_next;
+         return info;
+       }
+    }
+  
+  return NULL;
+}
+
+/*
+ * This function checks whether the conditions are right to place a read lock.
+ * It returns 1 if so, otherwise zero. The rwlock's internal lock must be
+ * locked upon entry.
+ */
+
+static int
+rwlock_can_rdlock(pthread_rwlock_t *rwlock, int have_lock_already)
+{
+  /* Can't readlock; it is write locked. */
+  if (rwlock->__rw_writer != NULL)
+    return 0;
+
+  /* Lock prefers readers; get it. */
+  if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP)
+    return 1;
+
+  /* Lock prefers writers, but none are waiting. */
+  if (queue_is_empty(&rwlock->__rw_write_waiting))
+    return 1;
+
+  /* Writers are waiting, but this thread already has a read lock */
+  if (have_lock_already)
+    return 1;
+
+  /* Writers are waiting, and this is a new lock */
+  return 0;
+}
+
+/*
+ * This function helps support brain-damaged recursive read locking
+ * semantics required by Unix 98, while maintaining write priority.
+ * This basically determines whether this thread already holds a read lock
+ * already. It returns 1 if so, otherwise it returns 0.  
+ *
+ * If the thread has any ``untracked read locks'' then it just assumes
+ * that this lock is among them, just to be safe, and returns 1.
+ *
+ * Also, if it finds the thread's lock in the list, it sets the pointer
+ * referenced by pexisting to refer to the list entry.
+ *
+ * If the thread has no untracked locks, and the lock is not found
+ * in its list, then it is added to the list. If this fails,
+ * then *pout_of_mem is set to 1.
+ */
+
+static int
+rwlock_have_already(pthread_descr *pself, pthread_rwlock_t *rwlock,
+    pthread_readlock_info **pexisting, int *pout_of_mem)
+{
+  pthread_readlock_info *existing = NULL;
+  int out_of_mem = 0, have_lock_already = 0;
+  pthread_descr self = *pself;
+
+  if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_WRITER_NP)
+    {
+      if (!self)
+       self = thread_self();
+
+      existing = rwlock_is_in_list(self, rwlock);
+
+      if (existing != NULL || self->p_untracked_readlock_count > 0)
+       have_lock_already = 1;
+      else
+       {
+         existing = rwlock_add_to_list(self, rwlock);
+         if (existing == NULL)
+           out_of_mem = 1;
+       }
+    }
+
+  *pout_of_mem = out_of_mem;
+  *pexisting = existing;
+  *pself = self;
+
+  return have_lock_already;
+}
+
+int
+pthread_rwlock_init (pthread_rwlock_t *rwlock,
+                    const pthread_rwlockattr_t *attr)
+{
+  __pthread_init_lock(&rwlock->__rw_lock);
+  rwlock->__rw_readers = 0;
+  rwlock->__rw_writer = NULL;
+  rwlock->__rw_read_waiting = NULL;
+  rwlock->__rw_write_waiting = NULL;
+
+  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;
+
+  __pthread_lock (&rwlock->__rw_lock, NULL);
+  readers = rwlock->__rw_readers;
+  writer = rwlock->__rw_writer;
+  __pthread_unlock (&rwlock->__rw_lock);
+
+  if (readers > 0 || writer != NULL)
+    return EBUSY;
+
+  return 0;
+}
+
+int
+pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
+{
+  pthread_descr self = NULL;
+  pthread_readlock_info *existing;
+  int out_of_mem, have_lock_already;
+
+  have_lock_already = rwlock_have_already(&self, rwlock,
+      &existing, &out_of_mem);
+
+  for (;;)
+    {
+      if (self == NULL)
+       self = thread_self ();
+
+      __pthread_lock (&rwlock->__rw_lock, self);
+
+      if (rwlock_can_rdlock(rwlock, have_lock_already))
+       break;
+
+      enqueue (&rwlock->__rw_read_waiting, self);
+      __pthread_unlock (&rwlock->__rw_lock);
+      suspend (self); /* This is not a cancellation point */
+    }
+
+  ++rwlock->__rw_readers;
+  __pthread_unlock (&rwlock->__rw_lock);
+
+  if (have_lock_already || out_of_mem)
+    {
+      if (existing != NULL)
+       existing->pr_lock_count++;
+      else
+       self->p_untracked_readlock_count++;
+    }
+  
+  return 0;
+}
+
+int
+pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
+{
+  pthread_descr self = thread_self();
+  pthread_readlock_info *existing;
+  int out_of_mem, have_lock_already;
+  int retval = EBUSY;
+
+  have_lock_already = rwlock_have_already(&self, rwlock,
+      &existing, &out_of_mem);
+
+  __pthread_lock (&rwlock->__rw_lock, self);
+
+  /* 0 is passed to here instead of have_lock_already.
+     This is to meet Single Unix Spec requirements: 
+     if writers are waiting, pthread_rwlock_tryrdlock
+     does not acquire a read lock, even if the caller has
+     one or more read locks already. */
+
+  if (rwlock_can_rdlock(rwlock, 0))
+    {
+      ++rwlock->__rw_readers;
+      retval = 0;
+    }
+
+  __pthread_unlock (&rwlock->__rw_lock);
+
+  if (retval == 0)
+    {
+      if (have_lock_already || out_of_mem)
+       {
+         if (existing != NULL)
+           existing->pr_lock_count++;
+         else
+           self->p_untracked_readlock_count++;
+       }
+    }
+  
+  return retval;
+}
+
+
+int
+pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
+{
+  pthread_descr self = thread_self ();
+
+  while(1)
+    {
+      __pthread_lock (&rwlock->__rw_lock, self);
+      if (rwlock->__rw_readers == 0 && rwlock->__rw_writer == NULL)
+       {
+         rwlock->__rw_writer = self;
+         __pthread_unlock (&rwlock->__rw_lock);
+         return 0;
+       }
+
+      /* Suspend ourselves, then try again */
+      enqueue (&rwlock->__rw_write_waiting, self);
+      __pthread_unlock (&rwlock->__rw_lock);
+      suspend (self); /* This is not a cancellation point */
+    }
+}
+
+
+int
+pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
+{
+  int result = EBUSY;
+
+  __pthread_lock (&rwlock->__rw_lock, NULL);
+  if (rwlock->__rw_readers == 0 && rwlock->__rw_writer == NULL)
+    {
+      rwlock->__rw_writer = thread_self ();
+      result = 0;
+    }
+  __pthread_unlock (&rwlock->__rw_lock);
+
+  return result;
+}
+
+
+int
+pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
+{
+  pthread_descr torestart;
+  pthread_descr th;
+
+  __pthread_lock (&rwlock->__rw_lock, NULL);
+  if (rwlock->__rw_writer != NULL)
+    {
+      /* Unlocking a write lock.  */
+      if (rwlock->__rw_writer != thread_self ())
+       {
+         __pthread_unlock (&rwlock->__rw_lock);
+         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;
+         rwlock->__rw_read_waiting = NULL;
+         __pthread_unlock (&rwlock->__rw_lock);
+         while ((th = dequeue (&torestart)) != NULL)
+           restart (th);
+       }
+      else
+       {
+         /* Restart one waiting writer.  */
+         __pthread_unlock (&rwlock->__rw_lock);
+         restart (th);
+       }
+    }
+  else
+    {
+      /* Unlocking a read lock.  */
+      if (rwlock->__rw_readers == 0)
+       {
+         __pthread_unlock (&rwlock->__rw_lock);
+         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;
+
+      __pthread_unlock (&rwlock->__rw_lock);
+      if (th != NULL)
+       restart (th);
+
+      /* Recursive lock fixup */
+
+      if (rwlock->__rw_kind == PTHREAD_RWLOCK_PREFER_WRITER_NP)
+       {
+         pthread_descr self = thread_self();
+         pthread_readlock_info *victim = rwlock_remove_from_list(self, rwlock);
+
+         if (victim != NULL)
+           {
+             if (victim->pr_lock_count == 0)
+               {
+                 victim->pr_next = self->p_readlock_free;
+                 self->p_readlock_free = victim;
+               }
+           }
+         else
+           {
+             if (self->p_untracked_readlock_count > 0)
+               self->p_untracked_readlock_count--;
+           }
+       }
+    }
+
+  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/libpthread/linuxthreads/semaphore.c b/libpthread/linuxthreads/semaphore.c
new file mode 100644 (file)
index 0000000..0297b3a
--- /dev/null
@@ -0,0 +1,209 @@
+/* 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 <errno.h>
+#include "pthread.h"
+#include "semaphore.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+#include "queue.h"
+
+int __new_sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+  if (value > SEM_VALUE_MAX) {
+    errno = EINVAL;
+    return -1;
+  }
+  if (pshared) {
+    errno = ENOSYS;
+    return -1;
+  }
+  __pthread_init_lock((struct _pthread_fastlock *) &sem->__sem_lock);
+  sem->__sem_value = value;
+  sem->__sem_waiting = NULL;
+  return 0;
+}
+
+/* Function called by pthread_cancel to remove the thread from
+   waiting inside __new_sem_wait. */
+
+static int new_sem_extricate_func(void *obj, pthread_descr th)
+{
+  volatile pthread_descr self = thread_self();
+  sem_t *sem = obj;
+  int did_remove = 0;
+
+  __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
+  did_remove = remove_from_queue(&sem->__sem_waiting, th);
+  __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+
+  return did_remove;
+}
+
+int __new_sem_wait(sem_t * sem)
+{
+  volatile pthread_descr self = thread_self();
+  pthread_extricate_if extr;
+  int already_canceled = 0;
+
+  /* Set up extrication interface */
+  extr.pu_object = sem;
+  extr.pu_extricate_func = new_sem_extricate_func;
+
+  __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
+  if (sem->__sem_value > 0) {
+    sem->__sem_value--;
+    __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+    return 0;
+  }
+  /* Register extrication interface */
+  __pthread_set_own_extricate_if(self, &extr); 
+  /* Enqueue only if not already cancelled. */
+  if (!(THREAD_GETMEM(self, p_canceled)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
+    enqueue(&sem->__sem_waiting, self);
+  else
+    already_canceled = 1;
+  __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+
+  if (already_canceled) {
+    __pthread_set_own_extricate_if(self, 0); 
+    pthread_exit(PTHREAD_CANCELED);
+  } 
+
+  /* Wait for sem_post or cancellation, or fall through if already canceled */
+  suspend(self);
+  __pthread_set_own_extricate_if(self, 0); 
+
+  /* Terminate only if the wakeup came from cancellation. */
+  /* Otherwise ignore cancellation because we got the semaphore. */
+
+  if (THREAD_GETMEM(self, p_woken_by_cancel)
+      && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
+    THREAD_SETMEM(self, p_woken_by_cancel, 0);
+    pthread_exit(PTHREAD_CANCELED);
+  }
+  /* We got the semaphore */
+  return 0;
+}
+
+int __new_sem_trywait(sem_t * sem)
+{
+  int retval;
+
+  __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, NULL);
+  if (sem->__sem_value == 0) {
+    errno = EAGAIN;
+    retval = -1;
+  } else {
+    sem->__sem_value--;
+    retval = 0;
+  }
+  __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+  return retval;
+}
+
+int __new_sem_post(sem_t * sem)
+{
+  pthread_descr self = thread_self();
+  pthread_descr th;
+  struct pthread_request request;
+
+  if (THREAD_GETMEM(self, p_in_sighandler) == NULL) {
+    __pthread_lock((struct _pthread_fastlock *) &sem->__sem_lock, self);
+    if (sem->__sem_waiting == NULL) {
+      if (sem->__sem_value >= SEM_VALUE_MAX) {
+        /* Overflow */
+        errno = ERANGE;
+        __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+        return -1;
+      }
+      sem->__sem_value++;
+      __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+    } else {
+      th = dequeue(&sem->__sem_waiting);
+      __pthread_unlock((struct _pthread_fastlock *) &sem->__sem_lock);
+      restart(th);
+    }
+  } else {
+    /* If we're in signal handler, delegate post operation to
+       the thread manager. */
+    if (__pthread_manager_request < 0) {
+      if (__pthread_initialize_manager() < 0) {
+        errno = EAGAIN;
+        return -1;
+      }
+    }
+    request.req_kind = REQ_POST;
+    request.req_args.post = sem;
+    __libc_write(__pthread_manager_request,
+                 (char *) &request, sizeof(request));
+  }
+  return 0;
+}
+
+int __new_sem_getvalue(sem_t * sem, int * sval)
+{
+  *sval = sem->__sem_value;
+  return 0;
+}
+
+int __new_sem_destroy(sem_t * sem)
+{
+  if (sem->__sem_waiting != NULL) {
+    __set_errno (EBUSY);
+    return -1;
+  }
+  return 0;
+}
+
+sem_t *sem_open(const char *name, int oflag, ...)
+{
+  __set_errno (ENOSYS);
+  return SEM_FAILED;
+}
+
+int sem_close(sem_t *sem)
+{
+  __set_errno (ENOSYS);
+  return -1;
+}
+
+int sem_unlink(const char *name)
+{
+  __set_errno (ENOSYS);
+  return -1;
+}
+
+#if defined PIC && DO_VERSIONING
+default_symbol_version (__new_sem_init, sem_init, GLIBC_2.1);
+default_symbol_version (__new_sem_wait, sem_wait, GLIBC_2.1);
+default_symbol_version (__new_sem_trywait, sem_trywait, GLIBC_2.1);
+default_symbol_version (__new_sem_post, sem_post, GLIBC_2.1);
+default_symbol_version (__new_sem_getvalue, sem_getvalue, GLIBC_2.1);
+default_symbol_version (__new_sem_destroy, sem_destroy, GLIBC_2.1);
+#else
+# ifdef weak_alias
+weak_alias (__new_sem_init, sem_init)
+weak_alias (__new_sem_wait, sem_wait)
+weak_alias (__new_sem_trywait, sem_trywait)
+weak_alias (__new_sem_post, sem_post)
+weak_alias (__new_sem_getvalue, sem_getvalue)
+weak_alias (__new_sem_destroy, sem_destroy)
+# endif
+#endif
+    
diff --git a/libpthread/linuxthreads/semaphore.h b/libpthread/linuxthreads/semaphore.h
new file mode 100644 (file)
index 0000000..8474223
--- /dev/null
@@ -0,0 +1,80 @@
+/* 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 <sys/types.h>
+
+#ifndef _PTHREAD_DESCR_DEFINED
+/* Thread descriptors.  Needed for `sem_t' definition.  */
+typedef struct _pthread_descr_struct *_pthread_descr;
+# define _PTHREAD_DESCR_DEFINED
+#endif
+
+/* System specific semaphore definition.  */
+typedef struct
+{
+  struct
+  {
+    long int status;
+    int spinlock;
+  } __sem_lock;
+  int __sem_value;
+  _pthread_descr __sem_waiting;
+} sem_t;
+
+
+
+/* Value returned if `sem_open' failed.  */
+#define SEM_FAILED     ((sem_t *) 0)
+
+/* Maximum value the semaphore can have.  */
+#define SEM_VALUE_MAX  ((int) ((~0u) >> 1))
+
+
+__BEGIN_DECLS
+
+/* Initialize semaphore object SEM to VALUE.  If PSHARED then share it
+   with other processes.  */
+extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
+
+/* Free resources associated with semaphore object SEM.  */
+extern int sem_destroy __P ((sem_t *__sem));
+
+/* Open a named semaphore NAME with open flaot OFLAG.  */
+extern sem_t *sem_open __P ((__const char *__name, int __oflag, ...));
+
+/* Close descriptor for named semaphore SEM.  */
+extern int sem_close __P ((sem_t *__sem));
+
+/* Remove named semaphore NAME.  */
+extern int sem_unlink __P ((__const char *__name));
+
+/* Wait for SEM being posted.  */
+extern int sem_wait __P ((sem_t *__sem));
+
+/* Test whether SEM is posted.  */
+extern int sem_trywait __P ((sem_t *__sem));
+
+/* Post SEM.  */
+extern int sem_post __P ((sem_t *__sem));
+
+/* Get current value of SEM and store it in *SVAL.  */
+extern int sem_getvalue __P ((sem_t *__sem, int *__sval));
+
+__END_DECLS
+
+#endif  /* semaphore.h */
diff --git a/libpthread/linuxthreads/signals.c b/libpthread/linuxthreads/signals.c
new file mode 100644 (file)
index 0000000..de156e1
--- /dev/null
@@ -0,0 +1,239 @@
+/* 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 <stdio.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include <ucontext.h>
+#include <sigcontextinfo.h>
+
+/* mods for uClibc: __libc_sigaction is not in any standard headers */
+extern int __libc_sigaction (int sig, const struct sigaction *act, struct sigaction *oact);
+
+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;
+
+  __pthread_lock(&handle->h_lock, NULL);
+  if (invalid_handle(handle, thread)) {
+    __pthread_unlock(&handle->h_lock);
+    return ESRCH;
+  }
+  pid = handle->h_descr->p_pid;
+  __pthread_unlock(&handle->h_lock);
+  if (kill(pid, signo) == -1)
+    return errno;
+  else
+    return 0;
+}
+
+/* User-provided signal handlers */
+typedef void (*arch_sighandler_t) __PMT ((int, SIGCONTEXT));
+static union
+{
+  arch_sighandler_t old;
+  void (*rt) (int, struct siginfo *, struct ucontext *);
+} sighandler[NSIG];
+
+/* The wrapper around user-provided signal handlers */
+static void pthread_sighandler(int signo, SIGCONTEXT ctx)
+{
+  pthread_descr self = thread_self();
+  char * in_sighandler;
+  /* If we're in a sigwait operation, just record the signal received
+     and return without calling the user's handler */
+  if (THREAD_GETMEM(self, p_sigwaiting)) {
+    THREAD_SETMEM(self, p_sigwaiting, 0);
+    THREAD_SETMEM(self, p_signal, signo);
+    return;
+  }
+  /* Record that we're in a signal handler and call the user's
+     handler function */
+  in_sighandler = THREAD_GETMEM(self, p_in_sighandler);
+  if (in_sighandler == NULL)
+    THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME);
+  sighandler[signo].old(signo, SIGCONTEXT_EXTRA_ARGS ctx);
+  if (in_sighandler == NULL)
+    THREAD_SETMEM(self, p_in_sighandler, NULL);
+}
+
+/* The same, this time for real-time signals.  */
+static void pthread_sighandler_rt(int signo, struct siginfo *si,
+                                 struct ucontext *uc)
+{
+  pthread_descr self = thread_self();
+  char * in_sighandler;
+  /* If we're in a sigwait operation, just record the signal received
+     and return without calling the user's handler */
+  if (THREAD_GETMEM(self, p_sigwaiting)) {
+    THREAD_SETMEM(self, p_sigwaiting, 0);
+    THREAD_SETMEM(self, p_signal, signo);
+    return;
+  }
+  /* Record that we're in a signal handler and call the user's
+     handler function */
+  in_sighandler = THREAD_GETMEM(self, p_in_sighandler);
+  if (in_sighandler == NULL)
+    THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME);
+  sighandler[signo].rt(signo, si, uc);
+  if (in_sighandler == NULL)
+    THREAD_SETMEM(self, p_in_sighandler, NULL);
+}
+
+/* The wrapper around sigaction.  Install our own signal handler
+   around the signal. */
+int sigaction(int sig, const struct sigaction * act,
+              struct sigaction * oact)
+{
+  struct sigaction newact;
+  struct sigaction *newactp;
+
+printf(__FUNCTION__": pthreads wrapper!\n");
+  if (sig == __pthread_sig_restart ||
+      sig == __pthread_sig_cancel ||
+      (sig == __pthread_sig_debug && __pthread_sig_debug > 0))
+    return EINVAL;
+  if (act)
+    {
+      newact = *act;
+      if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL
+         && sig > 0 && sig < NSIG)
+       {
+         if (act->sa_flags & SA_SIGINFO)
+           newact.sa_handler = (__sighandler_t) pthread_sighandler_rt;
+         else
+           newact.sa_handler = (__sighandler_t) pthread_sighandler;
+       }
+      newactp = &newact;
+    }
+  else
+    newactp = NULL;
+  if (__libc_sigaction(sig, newactp, oact) == -1)
+    return -1;
+printf(__FUNCTION__": signahdler installed, __sigaction successful\n");
+  if (sig > 0 && sig < NSIG)
+    {
+      if (oact != NULL)
+       oact->sa_handler = (__sighandler_t) sighandler[sig].old;
+      if (act)
+       /* For the assignment is does not matter whether it's a normal
+          or real-time signal.  */
+       sighandler[sig].old = (arch_sighandler_t) act->sa_handler;
+    }
+  return 0;
+}
+
+/* A signal handler that does nothing */
+static void pthread_null_sighandler(int sig) { }
+
+/* sigwait -- synchronously wait for a signal */
+int sigwait(const sigset_t * set, int * sig)
+{
+  volatile pthread_descr self = thread_self();
+  sigset_t mask;
+  int s;
+  sigjmp_buf jmpbuf;
+  struct sigaction sa;
+
+  /* Get ready to block all signals except those in set
+     and the cancellation signal.
+     Also check that handlers are installed on all signals in set,
+     and if not, install our dummy handler.  This is conformant to
+     POSIX: "The effect of sigwait() on the signal actions for the
+     signals in set is unspecified." */
+  sigfillset(&mask);
+  sigdelset(&mask, __pthread_sig_cancel);
+  for (s = 1; s <= NSIG; s++) {
+    if (sigismember(set, s) &&
+        s != __pthread_sig_restart &&
+        s != __pthread_sig_cancel &&
+        s != __pthread_sig_debug) {
+      sigdelset(&mask, s);
+      if (sighandler[s].old == NULL ||
+         sighandler[s].old == (arch_sighandler_t) SIG_DFL ||
+         sighandler[s].old == (arch_sighandler_t) SIG_IGN) {
+        sa.sa_handler = pthread_null_sighandler;
+        sigemptyset(&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction(s, &sa, NULL);
+      }
+    }
+  }
+  /* Test for cancellation */
+  if (sigsetjmp(jmpbuf, 1) == 0) {
+    THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
+    if (! (THREAD_GETMEM(self, p_canceled)
+          && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) {
+      /* Reset the signal count */
+      THREAD_SETMEM(self, p_signal, 0);
+      /* Say we're in sigwait */
+      THREAD_SETMEM(self, p_sigwaiting, 1);
+      /* Unblock the signals and wait for them */
+      sigsuspend(&mask);
+    }
+  }
+  THREAD_SETMEM(self, p_cancel_jmp, NULL);
+  /* The signals are now reblocked.  Check for cancellation */
+  pthread_testcancel();
+  /* We should have self->p_signal != 0 and equal to the signal received */
+  *sig = THREAD_GETMEM(self, p_signal);
+  return 0;
+}
+
+/* Redefine raise() to send signal to calling thread only,
+   as per POSIX 1003.1c */
+int raise (int sig)
+{
+  int retcode = pthread_kill(pthread_self(), sig);
+  if (retcode == 0)
+    return 0;
+  else {
+    errno = retcode;
+    return -1;
+  }
+}
diff --git a/libpthread/linuxthreads/specific.c b/libpthread/linuxthreads/specific.c
new file mode 100644 (file)
index 0000000..14c4b29
--- /dev/null
@@ -0,0 +1,179 @@
+/* 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.                 */
+
+/* mods for uClibc: removed strong_alias'es */
+
+/* Thread-specific data */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include "pthread.h"
+#include "internals.h"
+
+/* Table of keys. */
+
+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;
+}
+//strong_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 the thread already is terminated don't modify the memory.  */
+    if (!th->p_terminated && 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 (THREAD_GETMEM_NC(self, p_specific[idx1st]) == NULL) {
+    void *newp = calloc(PTHREAD_KEY_2NDLEVEL_SIZE, sizeof (void *));
+    if (newp == NULL)
+      return ENOMEM;
+    THREAD_SETMEM_NC(self, p_specific[idx1st], newp);
+  }
+  THREAD_GETMEM_NC(self, p_specific[idx1st])[idx2nd] = (void *) pointer;
+  return 0;
+}
+//strong_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 (THREAD_GETMEM_NC(self, p_specific[idx1st]) == NULL
+      || !pthread_keys[key].in_use)
+    return NULL;
+  return THREAD_GETMEM_NC(self, p_specific[idx1st])[idx2nd];
+}
+//strong_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 (THREAD_GETMEM_NC(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 = THREAD_GETMEM_NC(self, p_specific[i])[j];
+          if (destr != NULL && data != NULL) {
+            THREAD_GETMEM_NC(self, p_specific[i])[j] = NULL;
+            destr(data);
+            found_nonzero = 1;
+          }
+        }
+  }
+  for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++) {
+    if (THREAD_GETMEM_NC(self, p_specific[i]) != NULL)
+      free(THREAD_GETMEM_NC(self, p_specific[i]));
+  }
+}
+
+/* Thread-specific data for libc. */
+
+static int
+libc_internal_tsd_set(enum __libc_tsd_key_t key, const void * pointer)
+{
+  pthread_descr self = thread_self();
+
+  THREAD_SETMEM_NC(self, p_libc_specific[key], (void *) pointer);
+  return 0;
+}
+int (*__libc_internal_tsd_set)(enum __libc_tsd_key_t key, const void * pointer)
+     = libc_internal_tsd_set;
+
+static void *
+libc_internal_tsd_get(enum __libc_tsd_key_t key)
+{
+  pthread_descr self = thread_self();
+
+  return THREAD_GETMEM_NC(self, p_libc_specific[key]);
+}
+void * (*__libc_internal_tsd_get)(enum __libc_tsd_key_t key)
+     = libc_internal_tsd_get;
diff --git a/libpthread/linuxthreads/spinlock.c b/libpthread/linuxthreads/spinlock.c
new file mode 100644 (file)
index 0000000..b1a99d9
--- /dev/null
@@ -0,0 +1,195 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1998 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 locks */
+
+#include <errno.h>
+#include <sched.h>
+#include <time.h>
+#include "pthread.h"
+#include "internals.h"
+#include "spinlock.h"
+#include "restart.h"
+
+/* The status field of a fastlock has the following meaning:
+     0: fastlock is free
+     1: fastlock is taken, no thread is waiting on it
+  ADDR: fastlock is taken, ADDR is address of thread descriptor for
+        first waiting thread, other waiting threads are linked via
+        their p_nextlock field.
+   The waiting list is not sorted by priority order.
+   Actually, we always insert at top of list (sole insertion mode
+   that can be performed without locking).
+   For __pthread_unlock, we perform a linear search in the list
+   to find the highest-priority, oldest waiting thread.
+   This is safe because there are no concurrent __pthread_unlock
+   operations -- only the thread that locked the mutex can unlock it. */
+
+void internal_function __pthread_lock(struct _pthread_fastlock * lock,
+                                     pthread_descr self)
+{
+  long oldstatus, newstatus;
+  int spurious_wakeup_count = 0;
+
+  do {
+    oldstatus = lock->__status;
+    if (oldstatus == 0) {
+      newstatus = 1;
+    } else {
+      if (self == NULL)
+       self = thread_self();
+      newstatus = (long) self;
+    }
+    if (self != NULL) {
+      ASSERT(self->p_nextlock == NULL);
+      THREAD_SETMEM(self, p_nextlock, (pthread_descr) oldstatus);
+    }
+  } while(! compare_and_swap(&lock->__status, oldstatus, newstatus,
+                             &lock->__spinlock));
+
+  /* Suspend with guard against spurious wakeup. 
+     This can happen in pthread_cond_timedwait_relative, when the thread
+     wakes up due to timeout and is still on the condvar queue, and then
+     locks the queue to remove itself. At that point it may still be on the
+     queue, and may be resumed by a condition signal. */
+
+  if (oldstatus != 0) {
+    for (;;) {
+      suspend(self);
+      if (self->p_nextlock != NULL) {
+       /* Count resumes that don't belong to us. */
+       spurious_wakeup_count++;
+       continue;
+      }
+      break;
+    }
+  }
+
+  /* Put back any resumes we caught that don't belong to us. */
+  while (spurious_wakeup_count--)
+    restart(self);
+}
+
+void internal_function __pthread_unlock(struct _pthread_fastlock * lock)
+{
+  long oldstatus;
+  pthread_descr thr, * ptr, * maxptr;
+  int maxprio;
+
+again:
+  oldstatus = lock->__status;
+  if (oldstatus == 0 || oldstatus == 1) {
+    /* No threads are waiting for this lock.  Please note that we also
+       enter this case if the lock is not taken at all.  If this wouldn't
+       be done here we would crash further down.  */
+    if (! compare_and_swap(&lock->__status, oldstatus, 0, &lock->__spinlock))
+      goto again;
+    return;
+  }
+  /* Find thread in waiting queue with maximal priority */
+  ptr = (pthread_descr *) &lock->__status;
+  thr = (pthread_descr) oldstatus;
+  maxprio = 0;
+  maxptr = ptr;
+  while (thr != (pthread_descr) 1) {
+    if (thr->p_priority >= maxprio) {
+      maxptr = ptr;
+      maxprio = thr->p_priority;
+    }
+    ptr = &(thr->p_nextlock);
+    thr = *ptr;
+  }
+  /* Remove max prio thread from waiting list. */
+  if (maxptr == (pthread_descr *) &lock->__status) {
+    /* If max prio thread is at head, remove it with compare-and-swap
+       to guard against concurrent lock operation */
+    thr = (pthread_descr) oldstatus;
+    if (! compare_and_swap(&lock->__status,
+                           oldstatus, (long)(thr->p_nextlock),
+                           &lock->__spinlock))
+      goto again;
+  } else {
+    /* No risk of concurrent access, remove max prio thread normally */
+    thr = *maxptr;
+    *maxptr = thr->p_nextlock;
+  }
+  /* Wake up the selected waiting thread */
+  thr->p_nextlock = NULL;
+  restart(thr);
+}
+
+/* Compare-and-swap emulation with a spinlock */
+
+#ifdef TEST_FOR_COMPARE_AND_SWAP
+int __pthread_has_cas = 0;
+#endif
+
+#if !defined HAS_COMPARE_AND_SWAP || defined TEST_FOR_COMPARE_AND_SWAP
+
+static void __pthread_acquire(int * spinlock);
+
+int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
+                               int * spinlock)
+{
+  int res;
+  if (testandset(spinlock)) __pthread_acquire(spinlock);
+  if (*ptr == oldval) {
+    *ptr = newval; res = 1;
+  } else {
+    res = 0;
+  }
+  *spinlock = 0;
+  return res;
+}
+
+/* This function is called if the inlined test-and-set
+   in __pthread_compare_and_swap() failed */
+
+/* The retry strategy is as follows:
+   - We test and set the spinlock MAX_SPIN_COUNT times, calling
+     sched_yield() each time.  This gives ample opportunity for other
+     threads with priority >= our priority to make progress and
+     release the spinlock.
+   - If a thread with priority < our priority owns the spinlock,
+     calling sched_yield() repeatedly is useless, since we're preventing
+     the owning thread from making progress and releasing the spinlock.
+     So, after MAX_SPIN_LOCK attemps, we suspend the calling thread
+     using nanosleep().  This again should give time to the owning thread
+     for releasing the spinlock.
+     Notice that the nanosleep() interval must not be too small,
+     since the kernel does busy-waiting for short intervals in a realtime
+     process (!).  The smallest duration that guarantees thread
+     suspension is currently 2ms.
+   - When nanosleep() returns, we try again, doing MAX_SPIN_COUNT
+     sched_yield(), then sleeping again if needed. */
+
+static void __pthread_acquire(int * spinlock)
+{
+  int cnt = 0;
+  struct timespec tm;
+
+  while (testandset(spinlock)) {
+    if (cnt < MAX_SPIN_COUNT) {
+      sched_yield();
+      cnt++;
+    } else {
+      tm.tv_sec = 0;
+      tm.tv_nsec = SPIN_SLEEP_DURATION;
+      nanosleep(&tm, NULL);
+      cnt = 0;
+    }
+  }
+}
+
+#endif
diff --git a/libpthread/linuxthreads/spinlock.h b/libpthread/linuxthreads/spinlock.h
new file mode 100644 (file)
index 0000000..aae18a2
--- /dev/null
@@ -0,0 +1,102 @@
+/* Linuxthreads - a simple clone()-based implementation of Posix        */
+/* threads for Linux.                                                   */
+/* Copyright (C) 1998 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.                 */
+
+#if defined(TEST_FOR_COMPARE_AND_SWAP)
+
+extern int __pthread_has_cas;
+extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
+                                      int * spinlock);
+
+static inline int compare_and_swap(long * ptr, long oldval, long newval,
+                                   int * spinlock)
+{
+  if (__builtin_expect (__pthread_has_cas, 1))
+    return __compare_and_swap(ptr, oldval, newval);
+  else
+    return __pthread_compare_and_swap(ptr, oldval, newval, spinlock);
+}
+
+#elif defined(HAS_COMPARE_AND_SWAP)
+
+static inline int compare_and_swap(long * ptr, long oldval, long newval,
+                                   int * spinlock)
+{
+  return __compare_and_swap(ptr, oldval, newval);
+}
+
+#else
+
+extern int __pthread_compare_and_swap(long * ptr, long oldval, long newval,
+                                      int * spinlock);
+
+static inline int compare_and_swap(long * ptr, long oldval, long newval,
+                                   int * spinlock)
+{
+  return __pthread_compare_and_swap(ptr, oldval, newval, spinlock);
+}
+
+#endif
+
+/* Internal locks */
+
+extern void internal_function __pthread_lock(struct _pthread_fastlock * lock,
+                                            pthread_descr self);
+extern void internal_function __pthread_unlock(struct _pthread_fastlock *lock);
+
+static inline void __pthread_init_lock(struct _pthread_fastlock * lock)
+{
+  lock->__status = 0;
+  lock->__spinlock = 0;
+}
+
+static inline int __pthread_trylock (struct _pthread_fastlock * lock)
+{
+  long oldstatus;
+
+  do {
+    oldstatus = lock->__status;
+    if (oldstatus != 0) return EBUSY;
+  } while(! compare_and_swap(&lock->__status, 0, 1, &lock->__spinlock));
+  return 0;
+}
+
+#define LOCK_INITIALIZER {0, 0}
+
+/* Operations on pthread_atomic, which is defined in internals.h */
+
+static inline long atomic_increment(struct pthread_atomic *pa)
+{
+    long oldval;
+
+    do {
+       oldval = pa->p_count;
+    } while (!compare_and_swap(&pa->p_count, oldval, oldval + 1, &pa->p_spinlock));
+
+    return oldval;
+}
+
+
+static inline long atomic_decrement(struct pthread_atomic *pa)
+{
+    long oldval;
+
+    do {
+       oldval = pa->p_count;
+    } while (!compare_and_swap(&pa->p_count, oldval, oldval - 1, &pa->p_spinlock));
+
+    return oldval;
+}
+
+#define ATOMIC_INITIALIZER { 0, 0 }
+
diff --git a/libpthread/linuxthreads/sysdeps/alpha/pt-machine.h b/libpthread/linuxthreads/sysdeps/alpha/pt-machine.h
new file mode 100644 (file)
index 0000000..e59c690
--- /dev/null
@@ -0,0 +1,108 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   Alpha version.
+   Copyright (C) 1996, 1997, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+#include <asm/pal.h>
+
+
+/* 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");
+
+
+/* Spinlock implementation; required.  */
+PT_EI long int
+testandset (int *spinlock)
+{
+  long int 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
+
+
+/* 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, nr) \
+{                                                                            \
+  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
+PT_EI int
+__compare_and_swap (long int *p, long int oldval, long int newval)
+{
+  long int 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/libpthread/linuxthreads/sysdeps/alpha/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/alpha/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..eb6f4f0
--- /dev/null
@@ -0,0 +1,25 @@
+/* Copyright (C) 1999 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#define SIGCONTEXT struct sigcontext
+#define SIGCONTEXT_EXTRA_ARGS
+#define GET_PC(ctx)    ((void *) (ctx).sc_pc)
+#define GET_FRAME(ctx) ((void *) (ctx).sc_regs[15])
+#define GET_STACK(ctx) ((void *) (ctx).sc_regs[30])
+#define CALL_SIGHANDLER(handler, signo, ctx) \
+  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
diff --git a/libpthread/linuxthreads/sysdeps/arm/bits/armsigctx.h b/libpthread/linuxthreads/sysdeps/arm/bits/armsigctx.h
new file mode 100644 (file)
index 0000000..4530cdb
--- /dev/null
@@ -0,0 +1,73 @@
+/* Definition of `struct sigcontext' for Linux/ARM
+   Copyright (C) 1996, 1997, 1998, 1999, 2000 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* The format of struct sigcontext changed between 2.0 and 2.1 kernels.
+   Fortunately 2.0 puts a magic number in the first word and this is not
+   a legal value for `trap_no', so we can tell them apart.  */
+
+/* Early 2.2 and 2.3 kernels do not have the `fault_address' member in
+   the sigcontext structure.  Unfortunately there is no reliable way
+   to test for its presence and this word will contain garbage for too-old
+   kernels.  Versions 2.2.14 and 2.3.35 (plus later versions) are known to
+   include this element.  */
+
+#ifndef __ARMSIGCTX_H
+#define __ARMSIGCTX_H  1
+
+#include <asm/ptrace.h>
+
+union k_sigcontext
+  {
+    struct
+      {
+       unsigned long int trap_no;
+       unsigned long int error_code;
+       unsigned long int oldmask;
+       unsigned long int arm_r0;
+       unsigned long int arm_r1;
+       unsigned long int arm_r2;
+       unsigned long int arm_r3;
+       unsigned long int arm_r4;
+       unsigned long int arm_r5;
+       unsigned long int arm_r6;
+       unsigned long int arm_r7;
+       unsigned long int arm_r8;
+       unsigned long int arm_r9;
+       unsigned long int arm_r10;
+       unsigned long int arm_fp;
+       unsigned long int arm_ip;
+       unsigned long int arm_sp;
+       unsigned long int arm_lr;
+       unsigned long int arm_pc;
+       unsigned long int arm_cpsr;
+       unsigned long fault_address;
+      } v21;
+    struct
+      {
+       unsigned long int magic;
+       struct pt_regs reg;
+       unsigned long int trap_no;
+       unsigned long int error_code;
+       unsigned long int oldmask;
+      } v20;
+};
+
+#define SIGCONTEXT_2_0_MAGIC   0x4B534154
+
+#endif /* bits/armsigctx.h */
diff --git a/libpthread/linuxthreads/sysdeps/arm/pt-machine.h b/libpthread/linuxthreads/sysdeps/arm/pt-machine.h
new file mode 100644 (file)
index 0000000..d4dc4c4
--- /dev/null
@@ -0,0 +1,48 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   ARM version.
+   Copyright (C) 1997, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+
+/* 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.  */
+PT_EI 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/libpthread/linuxthreads/sysdeps/i386/i686/pt-machine.h b/libpthread/linuxthreads/sysdeps/i386/i686/pt-machine.h
new file mode 100644 (file)
index 0000000..8d9ea70
--- /dev/null
@@ -0,0 +1,67 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   i686 version.
+   Copyright (C) 1996, 1997, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+
+/* 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");
+
+
+/* Spinlock implementation; required.  */
+PT_EI int
+testandset (int *spinlock)
+{
+  int ret;
+
+  __asm__ __volatile__ (
+       "xchgl %0, %1"
+       : "=r"(ret), "=m"(*spinlock)
+       : "0"(1), "m"(*spinlock)
+       : "memory");
+
+  return ret;
+}
+
+
+/* Compare-and-swap for semaphores.  It's always available on i686.  */
+#define HAS_COMPARE_AND_SWAP
+
+PT_EI 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)
+                       : "memory");
+  return ret;
+}
+
+
+/* Use the LDT implementation only if the kernel is fixed.  */
+//#include "../useldt.h"
diff --git a/libpthread/linuxthreads/sysdeps/i386/pt-machine.h b/libpthread/linuxthreads/sysdeps/i386/pt-machine.h
new file mode 100644 (file)
index 0000000..f542bb2
--- /dev/null
@@ -0,0 +1,99 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   i386 version.
+   Copyright (C) 1996, 1997, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+/* 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");
+
+
+/* Spinlock implementation; required.  */
+PT_EI int
+testandset (int *spinlock)
+{
+  int ret;
+
+  __asm__ __volatile__(
+       "xchgl %0, %1"
+       : "=r"(ret), "=m"(*spinlock)
+       : "0"(1), "m"(*spinlock)
+       : "memory");
+
+  return ret;
+}
+
+
+/* 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
+
+PT_EI 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)
+                       : "memory");
+  return ret;
+}
+
+
+PT_EI int
+get_eflags (void)
+{
+  int res;
+  __asm__ __volatile__ ("pushfl; popl %0" : "=r" (res) : );
+  return res;
+}
+
+
+PT_EI void
+set_eflags (int newflags)
+{
+  __asm__ __volatile__ ("pushl %0; popfl" : : "r" (newflags) : "cc");
+}
+
+
+PT_EI 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/libpthread/linuxthreads/sysdeps/i386/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/i386/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..42c18b2
--- /dev/null
@@ -0,0 +1,24 @@
+/* Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by 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.  */
+
+#define SIGCONTEXT struct sigcontext
+#define SIGCONTEXT_EXTRA_ARGS
+#define GET_PC(ctx)    ((void *) ctx.eip)
+#define GET_FRAME(ctx) ((void *) ctx.ebp)
+#define GET_STACK(ctx) ((void *) ctx.esp_at_signal)
diff --git a/libpthread/linuxthreads/sysdeps/i386/useldt.h b/libpthread/linuxthreads/sysdeps/i386/useldt.h
new file mode 100644 (file)
index 0000000..1a789e2
--- /dev/null
@@ -0,0 +1,170 @@
+/* Special definitions for ix86 machine using segment register based
+   thread descriptor.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>.
+
+   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 <stddef.h>    /* For offsetof.  */
+
+
+/* We don't want to include the kernel header.  So duplicate the
+   information.  */
+
+/* Structure passed on `modify_ldt' call.  */
+struct modify_ldt_ldt_s
+{
+  unsigned int entry_number;
+  unsigned long int base_addr;
+  unsigned int limit;
+  unsigned int seg_32bit:1;
+  unsigned int contents:2;
+  unsigned int read_exec_only:1;
+  unsigned int limit_in_pages:1;
+  unsigned int seg_not_present:1;
+  unsigned int useable:1;
+  unsigned int empty:25;
+};
+
+/* System call to set LDT entry.  */
+extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t);
+
+
+/* Return the thread descriptor for the current thread.
+
+   The contained asm must *not* be marked volatile since otherwise
+   assignments like
+       pthread_descr self = thread_self();
+   do not get optimized away.  */
+#define THREAD_SELF \
+({                                                                           \
+  register pthread_descr __self;                                             \
+  __asm__ ("movl %%gs:%c1,%0" : "=r" (__self)                                \
+          : "i" (offsetof (struct _pthread_descr_struct, p_self)));          \
+  __self;                                                                    \
+})
+
+/* Initialize the thread-unique value.  */
+#define INIT_THREAD_SELF(descr, nr) \
+{                                                                            \
+  struct modify_ldt_ldt_s ldt_entry =                                        \
+    { nr, (unsigned long int) descr, sizeof (*descr), 1, 0, 0, 0, 0, 1, 0 };  \
+  if (__modify_ldt (1, &ldt_entry, sizeof (ldt_entry)) != 0)                 \
+    abort ();                                                                \
+  __asm__ __volatile__ ("movw %w0, %%gs" : : "r" (nr * 8 + 7));                      \
+}
+
+/* Free resources associated with thread descriptor.  */
+#define FREE_THREAD_SELF(descr, nr) \
+{                                                                            \
+  struct modify_ldt_ldt_s ldt_entry =                                        \
+    { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 };                                       \
+  __asm__ __volatile__ ("movw %w0,%%gs" : : "r" (0));                        \
+  __modify_ldt (1, &ldt_entry, sizeof (ldt_entry));                          \
+}
+
+/* Read member of the thread descriptor directly.  */
+#define THREAD_GETMEM(descr, member) \
+({                                                                           \
+  __typeof__ (descr->member) __value;                                        \
+  if (sizeof (__value) == 1)                                                 \
+    __asm__ __volatile__ ("movb %%gs:%P2,%b0"                                \
+                         : "=r" (__value)                                    \
+                         : "0" (0),                                          \
+                           "i" (offsetof (struct _pthread_descr_struct,      \
+                                          member)));                         \
+  else                                                                       \
+    {                                                                        \
+      if (sizeof (__value) != 4)                                             \
+       /* There should not be any value with a size other than 1 or 4.  */   \
+       abort ();                                                             \
+                                                                             \
+      __asm__ __volatile__ ("movl %%gs:%P1,%0"                               \
+                           : "=r" (__value)                                  \
+                           : "i" (offsetof (struct _pthread_descr_struct,    \
+                                            member)));                       \
+    }                                                                        \
+  __value;                                                                   \
+})
+
+/* Same as THREAD_GETMEM, but the member offset can be non-constant.  */
+#define THREAD_GETMEM_NC(descr, member) \
+({                                                                           \
+  __typeof__ (descr->member) __value;                                        \
+  if (sizeof (__value) == 1)                                                 \
+    __asm__ __volatile__ ("movb %%gs:(%2),%b0"                               \
+                         : "=r" (__value)                                    \
+                         : "0" (0),                                          \
+                           "r" (offsetof (struct _pthread_descr_struct,      \
+                                          member)));                         \
+  else                                                                       \
+    {                                                                        \
+      if (sizeof (__value) != 4)                                             \
+       /* There should not be any value with a size other than 1 or 4.  */   \
+       abort ();                                                             \
+                                                                             \
+      __asm__ __volatile__ ("movl %%gs:(%1),%0"                                      \
+                           : "=r" (__value)                                  \
+                           : "r" (offsetof (struct _pthread_descr_struct,    \
+                                            member)));                       \
+    }                                                                        \
+  __value;                                                                   \
+})
+
+/* Same as THREAD_SETMEM, but the member offset can be non-constant.  */
+#define THREAD_SETMEM(descr, member, value) \
+({                                                                           \
+  __typeof__ (descr->member) __value = (value);                                      \
+  if (sizeof (__value) == 1)                                                 \
+    __asm__ __volatile__ ("movb %0,%%gs:%P1" :                               \
+                         : "r" (__value),                                    \
+                           "i" (offsetof (struct _pthread_descr_struct,      \
+                                          member)));                         \
+  else                                                                       \
+    {                                                                        \
+      if (sizeof (__value) != 4)                                             \
+       /* There should not be any value with a size other than 1 or 4.  */   \
+       abort ();                                                             \
+                                                                             \
+      __asm__ __volatile__ ("movl %0,%%gs:%P1" :                             \
+                           : "r" (__value),                                  \
+                             "i" (offsetof (struct _pthread_descr_struct,    \
+                                            member)));                       \
+    }                                                                        \
+})
+
+/* Set member of the thread descriptor directly.  */
+#define THREAD_SETMEM_NC(descr, member, value) \
+({                                                                           \
+  __typeof__ (descr->member) __value = (value);                                      \
+  if (sizeof (__value) == 1)                                                 \
+    __asm__ __volatile__ ("movb %0,%%gs:(%1)" :                                      \
+                         : "r" (__value),                                    \
+                           "r" (offsetof (struct _pthread_descr_struct,      \
+                                          member)));                         \
+  else                                                                       \
+    {                                                                        \
+      if (sizeof (__value) != 4)                                             \
+       /* There should not be any value with a size other than 1 or 4.  */   \
+       abort ();                                                             \
+                                                                             \
+      __asm__ __volatile__ ("movl %0,%%gs:(%1)" :                            \
+                           : "r" (__value),                                  \
+                             "r" (offsetof (struct _pthread_descr_struct,    \
+                                            member)));                       \
+    }                                                                        \
+})
diff --git a/libpthread/linuxthreads/sysdeps/m68k/pt-machine.h b/libpthread/linuxthreads/sysdeps/m68k/pt-machine.h
new file mode 100644 (file)
index 0000000..38ea681
--- /dev/null
@@ -0,0 +1,62 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   m68k version.
+   Copyright (C) 1996, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+
+/* Spinlock implementation; required.  */
+PT_EI 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
+PT_EI 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/libpthread/linuxthreads/sysdeps/m68k/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/m68k/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..b7e08cf
--- /dev/null
@@ -0,0 +1,26 @@
+/* Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Schwab <schwab@issan.informatik.uni-dortmund.de>, 1998.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#define SIGCONTEXT int _code, struct sigcontext *
+#define SIGCONTEXT_EXTRA_ARGS _code,
+#define GET_PC(ctx)    ((void *) (ctx)->sc_pc)
+#define GET_FRAME(ctx) ((void *) __builtin_frame_address (1))
+#define GET_STACK(ctx) ((void *) (ctx)->sc_usp)
+#define CALL_SIGHANDLER(handler, signo, ctx) \
+  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
diff --git a/libpthread/linuxthreads/sysdeps/mips/pt-machine.h b/libpthread/linuxthreads/sysdeps/mips/pt-machine.h
new file mode 100644 (file)
index 0000000..5273923
--- /dev/null
@@ -0,0 +1,90 @@
+/* Machine-dependent pthreads configuration and inline functions.
+
+   Copyright (C) 1996, 1997, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+
+/* Spinlock implementation; required.  */
+PT_EI long int
+testandset (int *spinlock)
+{
+  long int 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
+PT_EI int
+__compare_and_swap (long int *p, long int oldval, long int 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/libpthread/linuxthreads/sysdeps/mips/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/mips/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..a51c6f0
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Andreas Jaeger <aj@suse.de>, 2000.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+
+#define SIGCONTEXT unsigned long _code, struct sigcontext *
+#define SIGCONTEXT_EXTRA_ARGS _code,
+#define GET_PC(ctx)    ((void *) ctx->sc_pc)
+#define GET_FRAME(ctx) ((void *) ctx->sc_regs[30])
+#define GET_STACK(ctx) ((void *) ctx->sc_regs[29])
+#define CALL_SIGHANDLER(handler, signo, ctx) \
+  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
diff --git a/libpthread/linuxthreads/sysdeps/powerpc/pt-machine.h b/libpthread/linuxthreads/sysdeps/powerpc/pt-machine.h
new file mode 100644 (file)
index 0000000..578369a
--- /dev/null
@@ -0,0 +1,69 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   powerpc version.
+   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.  */
+
+/* These routines are from Appendix G of the 'PowerPC 601 RISC Microprocessor
+   User's Manual', by IBM and Motorola.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+/* 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
+
+/* 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
+#if BROKEN_PPC_ASM_CR0
+static
+#else
+PT_EI
+#endif
+int
+__compare_and_swap (long int *p, long int oldval, long 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/libpthread/linuxthreads/sysdeps/powerpc/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/powerpc/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..138a15c
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright (C) 1998, 1999, 2001 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 Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <signal.h>
+
+#define SIGCONTEXT struct sigcontext *
+#define SIGCONTEXT_EXTRA_ARGS
+#define GET_PC(ctx)    ((void *)((ctx)->regs->nip))
+#define GET_FRAME(ctx) (*(void **)((ctx)->regs->gpr[1]))
+#define GET_STACK(ctx) ((void *)((ctx)->regs->gpr[1]))
+#define CALL_SIGHANDLER(handler, signo, ctx) \
+  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h b/libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h
new file mode 100644 (file)
index 0000000..a14cea1
--- /dev/null
@@ -0,0 +1,214 @@
+/* libc-internal interface for mutex locks.  LinuxThreads version.
+   Copyright (C) 1996, 1997, 1998, 1999 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.
+
+   For the C library we take a deeper look at the initializer.  For this
+   implementation all fields are initialized to zero.  Therefore we
+   don't initialize the variable which allows putting it into the BSS
+   section.  */
+#define __libc_lock_define_initialized(CLASS,NAME) \
+  CLASS __libc_lock_t NAME;
+
+/* 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_settype (&__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.  */
+#if PTHREAD_ONCE_INIT == 0
+/* Special case for static variables where we can avoid the initialization
+   if it is zero.  */
+# define __libc_once_define(CLASS, NAME) \
+  CLASS pthread_once_t NAME
+#else
+# define __libc_once_define(CLASS, NAME) \
+  CLASS pthread_once_t NAME = PTHREAD_ONCE_INIT
+#endif
+
+/* 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;                                  \
+    int _avail = _pthread_cleanup_push_defer != NULL;                        \
+    if (_avail) {                                                            \
+      _pthread_cleanup_push_defer (&_buffer, (FCT), (ARG));                  \
+    }
+
+/* End critical region with cleanup.  */
+#define __libc_cleanup_region_end(DOIT) \
+    if (_avail) {                                                            \
+      _pthread_cleanup_pop_restore (&_buffer, (DOIT));                       \
+    }                                                                        \
+  }
+
+/* Sometimes we have to exit the block in the middle.  */
+#define __libc_cleanup_end(DOIT) \
+    if (_avail) {                                                            \
+      _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)
+
+
+/* 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.  */
+#ifndef __NO_WEAK_PTHREAD_ALIASES
+# 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_settype)
+weak_extern (__pthread_key_create)
+weak_extern (__pthread_setspecific)
+weak_extern (__pthread_getspecific)
+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_settype
+#  pragma weak __pthread_key_create
+#  pragma weak __pthread_setspecific
+#  pragma weak __pthread_getspecific
+#  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
+#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/libpthread/linuxthreads/sysdeps/pthread/bits/libc-tsd.h b/libpthread/linuxthreads/sysdeps/pthread/bits/libc-tsd.h
new file mode 100644 (file)
index 0000000..e38cdf5
--- /dev/null
@@ -0,0 +1,44 @@
+/* libc-internal interface for thread-specific data.  LinuxThreads version.
+   Copyright (C) 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.  */
+
+#ifndef _BITS_LIBC_TSD_H
+#define _BITS_LIBC_TSD_H 1
+
+#include <features.h>
+
+/* Fast thread-specific data internal to libc.  */
+enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0,
+                       _LIBC_TSD_KEY_DL_ERROR,
+                       _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 *));
+
+#define __libc_tsd_define(CLASS, KEY)  CLASS void *__libc_tsd_##KEY##_data;
+#define __libc_tsd_get(KEY) \
+  (__libc_internal_tsd_get != NULL \
+   ? __libc_internal_tsd_get (_LIBC_TSD_KEY_##KEY) \
+   : __libc_tsd_##KEY##_data)
+#define __libc_tsd_set(KEY, VALUE) \
+  (__libc_internal_tsd_set != NULL \
+   ? __libc_internal_tsd_set (_LIBC_TSD_KEY_##KEY, (VALUE)) \
+   : ((__libc_tsd_##KEY##_data = (VALUE)), 0))
+
+#endif /* bits/libc-tsd.h */
diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/pthreadtypes.h b/libpthread/linuxthreads/sysdeps/pthread/bits/pthreadtypes.h
new file mode 100644 (file)
index 0000000..db4c379
--- /dev/null
@@ -0,0 +1,122 @@
+/* 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.                 */
+
+#if !defined _BITS_TYPES_H && !defined _PTHREAD_H
+# error "Never include <bits/pthreadtypes.h> directly; use <sys/types.h> instead."
+#endif
+
+#ifndef _BITS_PTHREADTYPES_H
+#define _BITS_PTHREADTYPES_H   1
+
+#define __need_schedparam
+#include <bits/sched.h>
+
+/* Fast locks (not abstract because mutexes and conditions aren't abstract). */
+struct _pthread_fastlock
+{
+  long int __status;            /* "Free" or "taken" or head of waiting list */
+  int __spinlock;               /* For compare-and-swap emulation */
+};
+
+#ifndef _PTHREAD_DESCR_DEFINED
+/* Thread descriptors */
+typedef struct _pthread_descr_struct *_pthread_descr;
+# define _PTHREAD_DESCR_DEFINED
+#endif
+
+
+/* Attributes for threads.  */
+typedef struct
+{
+  int __detachstate;
+  int __schedpolicy;
+  struct __sched_param __schedparam;
+  int __inheritsched;
+  int __scope;
+  size_t __guardsize;
+  int __stackaddr_set;
+  void *__stackaddr;
+  size_t __stacksize;
+} pthread_attr_t;
+
+
+/* Conditions (not abstract because of PTHREAD_COND_INITIALIZER */
+typedef struct
+{
+  struct _pthread_fastlock __c_lock; /* Protect against concurrent access */
+  _pthread_descr __c_waiting;        /* Threads waiting on this condition */
+} pthread_cond_t;
+
+
+/* Attribute for conditionally variables.  */
+typedef struct
+{
+  int __dummy;
+} pthread_condattr_t;
+
+/* Keys for thread-specific data */
+typedef unsigned int pthread_key_t;
+
+
+/* Mutexes (not abstract because of PTHREAD_MUTEX_INITIALIZER).  */
+/* (The layout is unnatural to maintain binary compatibility
+    with earlier releases of LinuxThreads.) */
+typedef struct
+{
+  int __m_reserved;               /* Reserved for future use */
+  int __m_count;                  /* Depth of recursive locking */
+  _pthread_descr __m_owner;       /* Owner thread (if recursive or errcheck) */
+  int __m_kind;                   /* Mutex kind: fast, recursive or errcheck */
+  struct _pthread_fastlock __m_lock; /* Underlying fast lock */
+} pthread_mutex_t;
+
+
+/* Attribute for mutex.  */
+typedef struct
+{
+  int __mutexkind;
+} pthread_mutexattr_t;
+
+
+/* Once-only execution */
+typedef int pthread_once_t;
+
+
+#ifdef __USE_UNIX98
+/* Read-write locks.  */
+typedef struct _pthread_rwlock_t
+{
+  struct _pthread_fastlock __rw_lock; /* Lock to guarantee mutual exclusion */
+  int __rw_readers;                   /* Number of readers */
+  _pthread_descr __rw_writer;         /* Identity of writer, or NULL if none */
+  _pthread_descr __rw_read_waiting;   /* Threads waiting for reading */
+  _pthread_descr __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;
+
+
+/* Attribute for read-write locks.  */
+typedef struct
+{
+  int __lockkind;
+  int __pshared;
+} pthread_rwlockattr_t;
+#endif
+
+
+/* Thread identifiers */
+typedef unsigned long int pthread_t;
+
+#endif /* bits/pthreadtypes.h */
diff --git a/libpthread/linuxthreads/sysdeps/pthread/bits/stdio-lock.h b/libpthread/linuxthreads/sysdeps/pthread/bits/stdio-lock.h
new file mode 100644 (file)
index 0000000..edc69f6
--- /dev/null
@@ -0,0 +1,39 @@
+/* Thread package specific definitions of stream lock type.
+   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.  */
+
+#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)
+#define _IO_lock_lock(_name) \
+     __libc_lock_lock (_name)
+#define _IO_lock_unlock(_name) \
+     __libc_lock_unlock (_name)
diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h b/libpthread/linuxthreads/sysdeps/sparc/sparc32/pt-machine.h
new file mode 100644 (file)
index 0000000..69af857
--- /dev/null
@@ -0,0 +1,66 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   sparc version.
+   Copyright (C) 1996, 1997, 1998 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+/* Spinlock implementation; required.  */
+PT_EI int
+testandset (int *spinlock)
+{
+  int ret;
+
+  __asm__ __volatile__("ldstub %1,%0"
+       : "=r"(ret), "=m"(*spinlock)
+       : "m"(*spinlock));
+
+  return ret;
+}
+
+
+/* Spinlock release; default is just set to zero.  */
+#define RELEASE(spinlock) \
+  __asm__ __volatile__("stbar; stb %1,%0" : "=m"(*(spinlock)) : "r"(0));
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char * stack_pointer __asm__("%sp");
+
+
+/* Registers %g6 and %g7 are reserved by the ABI for "system use".  It
+   happens that Solaris uses %g6 for the thread pointer -- we do the same.  */
+struct _pthread_descr_struct;
+register struct _pthread_descr_struct *__thread_self __asm__("%g6");
+
+/* Return the thread descriptor for the current thread.  */
+#define THREAD_SELF  __thread_self
+
+/* Initialize the thread-unique value.  */
+#define INIT_THREAD_SELF(descr, nr)  (__thread_self = (descr))
+
+/* Access to data in the thread descriptor is easy.  */
+#define THREAD_GETMEM(descr, member) __thread_self->member
+#define THREAD_GETMEM_NC(descr, member) __thread_self->member
+#define THREAD_SETMEM(descr, member, value) __thread_self->member = (value)
+#define THREAD_SETMEM_NC(descr, member, value) __thread_self->member = (value)
diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc32/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/sparc/sparc32/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..2c2770d
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 1999.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#define SIGCONTEXT struct sigcontext *
+#define SIGCONTEXT_EXTRA_ARGS
+#define GET_PC(__ctx)  ((void *) ((__ctx)->si_regs.pc))
+#define ADVANCE_STACK_FRAME(__next) \
+       ((void *) (((unsigned *)(__next))+14))
+
+#define GET_STACK(__ctx)       ((void *) (__ctx)->si_regs.u_regs[14])
+#define GET_FRAME(__ctx)       ADVANCE_STACK_FRAME (GET_STACK(__ctx))
+#define CALL_SIGHANDLER(handler, signo, ctx) \
+  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h b/libpthread/linuxthreads/sysdeps/sparc/sparc64/pt-machine.h
new file mode 100644 (file)
index 0000000..5560003
--- /dev/null
@@ -0,0 +1,77 @@
+/* Machine-dependent pthreads configuration and inline functions.
+   Sparc v9 version.
+   Copyright (C) 1997, 1998, 1999 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.  */
+
+#ifndef PT_EI
+# define PT_EI extern inline
+#endif
+
+
+/* Spinlock implementation; required.  */
+PT_EI int
+testandset (int *spinlock)
+{
+  int ret;
+
+  __asm__ __volatile__("ldstub %1,%0"
+       : "=r"(ret), "=m"(*spinlock) : "m"(*spinlock));
+
+  return ret;
+}
+
+
+/* Get some notion of the current stack.  Need not be exactly the top
+   of the stack, just something somewhere in the current frame.  */
+#define CURRENT_STACK_FRAME  stack_pointer
+register char *stack_pointer __asm__ ("%sp");
+
+
+/* Registers %g6 and %g7 are reserved by the ABI for "system use".  It
+   happens that Solaris uses %g6 for the thread pointer -- we do the same.  */
+struct _pthread_descr_struct;
+register struct _pthread_descr_struct *__thread_self __asm__("%g6");
+
+/* Return the thread descriptor for the current thread.  */
+#define THREAD_SELF  __thread_self
+
+/* Initialize the thread-unique value.  */
+#define INIT_THREAD_SELF(descr, nr)  (__thread_self = (descr))
+
+
+/* Compare-and-swap for semaphores. */
+
+#define HAS_COMPARE_AND_SWAP
+PT_EI int
+__compare_and_swap (long int *p, long int oldval, long int newval)
+{
+  long int readval;
+
+  __asm__ __volatile__ ("casx  [%4], %2, %0"
+                       : "=r"(readval), "=m"(*p)
+                       : "r"(oldval), "m"(*p), "r"(p), "0"(newval));
+
+  return readval == oldval;
+}
+
+/* Access to data in the thread descriptor is easy.  */
+#define THREAD_GETMEM(descr, member) __thread_self->member
+#define THREAD_GETMEM_NC(descr, member) __thread_self->member
+#define THREAD_SETMEM(descr, member, value) __thread_self->member = (value)
+#define THREAD_SETMEM_NC(descr, member, value) __thread_self->member = (value)
diff --git a/libpthread/linuxthreads/sysdeps/sparc/sparc64/sigcontextinfo.h b/libpthread/linuxthreads/sysdeps/sparc/sparc64/sigcontextinfo.h
new file mode 100644 (file)
index 0000000..47e0d27
--- /dev/null
@@ -0,0 +1,32 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jj@ultra.linux.cz>, 1999.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef STACK_BIAS
+#define STACK_BIAS 2047
+#endif
+#define SIGCONTEXT struct sigcontext *
+#define SIGCONTEXT_EXTRA_ARGS
+#define GET_PC(__ctx)  ((void *) ((__ctx)->sigc_regs.tpc))
+#define ADVANCE_STACK_FRAME(__next) \
+       ((void *) (((unsigned long *) (((unsigned long int) (__next))     \
+                                          + STACK_BIAS))+14))
+#define GET_STACK(__ctx)       ((void *) ((__ctx)->sigc_regs.u_regs[14]))
+#define GET_FRAME(__ctx)       ADVANCE_STACK_FRAME (GET_STACK (__ctx))
+#define CALL_SIGHANDLER(handler, signo, ctx) \
+  (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
diff --git a/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h
new file mode 100644 (file)
index 0000000..545a90b
--- /dev/null
@@ -0,0 +1,55 @@
+/* Minimum guaranteed maximum values for system limits.  Linux version.
+   Copyright (C) 1993, 94, 95, 96, 97, 98 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* The kernel header pollutes the namespace with the NR_OPEN symbol.
+   Remove this after including the header if necessary.  */
+#ifndef NR_OPEN
+# define __undef_NR_OPEN
+#endif
+
+/* The kernel sources contain a file with all the needed information.  */
+#include <linux/limits.h>
+
+/* Have to remove NR_OPEN?  */
+#ifdef __undef_NR_OPEN
+# undef NR_OPEN
+# undef __undef_NR_OPEN
+#endif
+
+/* The number of data keys per process.  */
+#define _POSIX_THREAD_KEYS_MAX 128
+/* This is the value this implementation supports.  */
+#define PTHREAD_KEYS_MAX       1024
+
+/* Controlling the iterations of destructors for thread-specific data.  */
+#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS    4
+/* Number of iterations this implementation does.  */
+#define PTHREAD_DESTRUCTOR_ITERATIONS  _POSIX_THREAD_DESTRUCTOR_ITERATIONS
+
+/* The number of threads per process.  */
+#define _POSIX_THREAD_THREADS_MAX      64
+/* This is the value this implementation supports.  */
+#define PTHREAD_THREADS_MAX    1024
+
+/* Maximum amount by which a process can descrease its asynchronous I/O
+   priority level.  */
+#define AIO_PRIO_DELTA_MAX     20
+
+/* Minimum size for a thread.  We are free to choose a reasonable value.  */
+#define PTHREAD_STACK_MIN      16384
diff --git a/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/posix_opt.h
new file mode 100644 (file)
index 0000000..15683b7
--- /dev/null
@@ -0,0 +1,110 @@
+/* Define POSIX options for Linux.
+   Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef        _POSIX_OPT_H
+#define        _POSIX_OPT_H    1
+
+/* Job control is supported.  */
+#define        _POSIX_JOB_CONTROL      1
+
+/* Processes have a saved set-user-ID and a saved set-group-ID.  */
+#define        _POSIX_SAVED_IDS        1
+
+/* Priority scheduling is supported.  */
+#define        _POSIX_PRIORITY_SCHEDULING      1
+
+/* Synchronizing file data is supported.  */
+#define        _POSIX_SYNCHRONIZED_IO  1
+
+/* The fsync function is present.  */
+#define        _POSIX_FSYNC    1
+
+/* Mapping of files to memory is supported.  */
+#define        _POSIX_MAPPED_FILES     1
+
+/* Locking of all memory is supported.  */
+#define        _POSIX_MEMLOCK  1
+
+/* Locking of ranges of memory is supported.  */
+#define        _POSIX_MEMLOCK_RANGE    1
+
+/* Setting of memory protections is supported.  */
+#define        _POSIX_MEMORY_PROTECTION        1
+
+/* Implementation supports `poll' function.  */
+#define        _POSIX_POLL     1
+
+/* Implementation supports `select' and `pselect' functions.  */
+#define        _POSIX_SELECT   1
+
+/* Only root can change owner of file.  */
+#define        _POSIX_CHOWN_RESTRICTED 1
+
+/* `c_cc' member of 'struct termios' structure can be disabled by
+   using the value _POSIX_VDISABLE.  */
+#define        _POSIX_VDISABLE '\0'
+
+/* Filenames are not silently truncated.  */
+#define        _POSIX_NO_TRUNC 1
+
+/* X/Open realtime support is available.  */
+#define _XOPEN_REALTIME        1
+
+/* X/Open realtime thread support is available.  */
+#define _XOPEN_REALTIME_THREADS        1
+
+/* XPG4.2 shared memory is supported.  */
+#define        _XOPEN_SHM      1
+
+/* Tell we have POSIX threads.  */
+#define _POSIX_THREADS 1
+
+/* We have the reentrant functions described in POSIX.  */
+#define _POSIX_REENTRANT_FUNCTIONS      1
+#define _POSIX_THREAD_SAFE_FUNCTIONS   1
+
+/* We provide priority scheduling for threads.  */
+#define        _POSIX_THREAD_PRIORITY_SCHEDULING       1
+
+/* We support user-defined stack sizes.  */
+#define _POSIX_THREAD_ATTR_STACKSIZE   1
+
+/* We support user-defined stacks.  */
+#define _POSIX_THREAD_ATTR_STACKADDR   1
+
+/* We support POSIX.1b semaphores, but only the non-shared form for now.  */
+/*#define _POSIX_SEMAPHORES    1       XXX We are not quite there now.  */
+
+/* Real-time signals are supported.  */
+#define _POSIX_REALTIME_SIGNALS        1
+
+/* We support asynchronous I/O.  */
+#define _POSIX_ASYNCHRONOUS_IO 1
+/* Alternative name for Unix98.  */
+#define _LFS_ASYNCHRONOUS_IO   1
+
+/* The LFS support in asynchronous I/O is also available.  */
+#define _LFS64_ASYNCHRONOUS_IO 1
+
+/* The rest of the LFS is also available.  */
+#define _LFS_LARGEFILE         1
+#define _LFS64_LARGEFILE       1
+#define _LFS64_STDIO           1
+
+#endif /* posix_opt.h */
diff --git a/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h b/libpthread/linuxthreads/sysdeps/unix/sysv/linux/bits/sigthread.h
new file mode 100644 (file)
index 0000000..c9b1dcf
--- /dev/null
@@ -0,0 +1,37 @@
+/* Signal handling function for threaded programs.
+   Copyright (C) 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.  */
+
+#ifndef _BITS_SIGTHREAD_H 
+#define _BITS_SIGTHREAD_H      1
+
+#if !defined _SIGNAL_H && !defined _PTHREAD_H
+# error "Never include this file directly.  Use <pthread.h> instead"
+#endif
+
+/* Functions for handling signals. */
+
+/* Modify the signal mask for the calling thread.  The arguments have
+   the same meaning as for sigprocmask(2). */
+extern int pthread_sigmask __P ((int __how, __const __sigset_t *__newmask,
+                                __sigset_t *__oldmask));
+
+/* Send signal SIGNO to the given thread. */
+extern int pthread_kill __P ((pthread_t __thread, int __signo));
+
+#endif /* bits/sigthread.h */
diff --git a/libpthread/linuxthreads/testrtsig.h b/libpthread/linuxthreads/testrtsig.h
new file mode 100644 (file)
index 0000000..fb8b011
--- /dev/null
@@ -0,0 +1,40 @@
+/* Test whether RT signals are really available.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+   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 <limits.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+static int
+kernel_has_rtsig (void)
+{
+  return 0; /* hacked to test old uClibc that doesn't know about RT signals. 0.9.9 should work with RT signals. Make this proper in the end! */
+#ifdef RTSIG_MAX
+  return 1;
+#else
+  return 0;
+#endif
+
+/*
+  struct utsname name;
+
+  return uname (&name) == 0 && __strverscmp (name.release, "2.1.70") >= 0;
+*/
+}
diff --git a/libpthread/linuxthreads/weaks.c b/libpthread/linuxthreads/weaks.c
new file mode 100644 (file)
index 0000000..5281a2a
--- /dev/null
@@ -0,0 +1,120 @@
+/* The weak pthread functions for Linux.
+   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.  */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+extern int __pthread_return_0 __P ((void));
+extern int __pthread_return_1 __P ((void));
+extern void __pthread_return_void __P ((void));
+
+/* NOTE: these require a strong alias in attr.c. I don't think we need this
+ * versioning stuff with uClibc. -StS
+ */
+
+/* Those are pthread functions which return 0 if successful. */
+//#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING
+//weak_alias (__pthread_return_0, __libc_pthread_attr_init_2_0)
+//symbol_version (__libc_pthread_attr_init_2_0, pthread_attr_init, GLIBC_2.0);
+//weak_alias (__pthread_return_0, __libc_pthread_attr_init_2_1)
+//default_symbol_version (__libc_pthread_attr_init_2_1, pthread_attr_init,
+//                     GLIBC_2.1);
+//#else
+weak_alias (__pthread_return_0, pthread_attr_init)
+//#endif
+weak_alias (__pthread_return_0, pthread_attr_destroy)
+weak_alias (__pthread_return_0, pthread_attr_setdetachstate)
+weak_alias (__pthread_return_0, pthread_attr_getdetachstate)
+weak_alias (__pthread_return_0, pthread_attr_setschedparam)
+weak_alias (__pthread_return_0, pthread_attr_getschedparam)
+weak_alias (__pthread_return_0, pthread_attr_setschedpolicy)
+weak_alias (__pthread_return_0, pthread_attr_getschedpolicy)
+weak_alias (__pthread_return_0, pthread_attr_setinheritsched)
+weak_alias (__pthread_return_0, pthread_attr_getinheritsched)
+weak_alias (__pthread_return_0, pthread_attr_setscope)
+weak_alias (__pthread_return_0, pthread_attr_getscope)
+weak_alias (__pthread_return_0, pthread_attr_setstackaddr)
+weak_alias (__pthread_return_0, pthread_attr_getstackaddr)
+weak_alias (__pthread_return_0, pthread_attr_setstacksize)
+weak_alias (__pthread_return_0, pthread_attr_getstacksize)
+weak_alias (__pthread_return_0, pthread_mutex_init)
+weak_alias (__pthread_return_0, pthread_mutex_destroy)
+weak_alias (__pthread_return_0, pthread_mutex_lock)
+weak_alias (__pthread_return_0, pthread_mutex_trylock)
+weak_alias (__pthread_return_0, pthread_mutex_unlock)
+weak_alias (__pthread_return_0, pthread_mutexattr_init)
+weak_alias (__pthread_return_0, pthread_mutexattr_destroy)
+weak_alias (__pthread_return_0, pthread_mutexattr_settype)
+weak_alias (__pthread_return_0, pthread_mutexattr_gettype)
+weak_alias (__pthread_return_0, pthread_condattr_init)
+weak_alias (__pthread_return_0, pthread_condattr_destroy)
+weak_alias (__pthread_return_0, pthread_setschedparam)
+weak_alias (__pthread_return_0, pthread_getschedparam)
+weak_alias (__pthread_return_0, pthread_getcancelstate)
+weak_alias (__pthread_return_0, pthread_setcancelstate)
+weak_alias (__pthread_return_0, pthread_setcanceltype)
+weak_alias (__pthread_return_0, pthread_setconcurrency)
+weak_alias (__pthread_return_0, pthread_getconcurrency)
+weak_alias (__pthread_return_0, pthread_self)
+weak_alias (__pthread_return_0, pthread_cond_init)
+weak_alias (__pthread_return_0, pthread_cond_destroy)
+weak_alias (__pthread_return_0, pthread_cond_wait)
+weak_alias (__pthread_return_0, pthread_cond_timedwait)
+weak_alias (__pthread_return_0, pthread_cond_signal)
+weak_alias (__pthread_return_0, pthread_cond_broadcast)
+weak_alias (__pthread_return_0, pthread_rwlock_init)
+weak_alias (__pthread_return_0, pthread_rwlock_destroy)
+weak_alias (__pthread_return_0, pthread_rwlock_rdlock)
+weak_alias (__pthread_return_0, pthread_rwlock_wrlock)
+weak_alias (__pthread_return_0, pthread_rwlock_tryrdlock)
+weak_alias (__pthread_return_0, pthread_rwlock_trywrlock)
+weak_alias (__pthread_return_0, pthread_rwlock_unlock)
+weak_alias (__pthread_return_0, pthread_rwlockattr_init)
+weak_alias (__pthread_return_0, pthread_rwlockattr_destroy)
+weak_alias (__pthread_return_0, pthread_rwlockattr_setpshared)
+weak_alias (__pthread_return_0, pthread_rwlockattr_getpshared)
+
+
+/* Those are pthread functions which return 1 if successful. */
+weak_alias (__pthread_return_1, pthread_equal)
+
+/* pthread_exit () is a special case. */
+void weak_function
+pthread_exit (void *retval)
+{
+  exit (EXIT_SUCCESS);
+}
+
+int
+__pthread_return_0 (void)
+{
+  return 0;
+}
+
+int
+__pthread_return_1 (void)
+{
+  return 1;
+}
+
+void
+__pthread_return_void (void)
+{
+}
diff --git a/libpthread/linuxthreads/wrapsyscall.c b/libpthread/linuxthreads/wrapsyscall.c
new file mode 100644 (file)
index 0000000..3b9ada1
--- /dev/null
@@ -0,0 +1,178 @@
+/* Wrapper arpund system calls to provide cancelation points.
+   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#define __FORCE_GLIBC
+#include <features.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+
+
+#ifndef PIC
+/* We need a hook to force this file to be linked in when static
+   libpthread is used.  */
+const int __pthread_provide_wrappers = 0;
+#endif
+
+
+#define CANCELABLE_SYSCALL(res_type, name, param_list, params) \
+res_type __libc_##name param_list;                                           \
+res_type                                                                     \
+name param_list                                                                      \
+{                                                                            \
+  res_type result;                                                           \
+  int oldtype;                                                               \
+  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);             \
+  result = __libc_##name params;                                             \
+  pthread_setcanceltype (oldtype, NULL);                                     \
+  return result;                                                             \
+}
+
+#define CANCELABLE_SYSCALL_VA(res_type, name, param_list, params, last_arg) \
+res_type __libc_##name param_list;                                           \
+res_type                                                                     \
+name param_list                                                                      \
+{                                                                            \
+  res_type result;                                                           \
+  int oldtype;                                                               \
+  va_list ap;                                                                \
+  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);             \
+  va_start (ap, last_arg);                                                   \
+  result = __libc_##name params;                                             \
+  va_end (ap);                                                               \
+  pthread_setcanceltype (oldtype, NULL);                                     \
+  return result;                                                             \
+}
+
+
+/* close(2).  */
+CANCELABLE_SYSCALL (int, close, (int fd), (fd))
+
+
+/* fcntl(2).  */
+CANCELABLE_SYSCALL_VA (int, fcntl, (int fd, int cmd, ...),
+                      (fd, cmd, va_arg (ap, long int)), cmd)
+
+
+/* fsync(2).  */
+CANCELABLE_SYSCALL (int, fsync, (int fd), (fd))
+
+
+/* lseek(2).  */
+CANCELABLE_SYSCALL (off_t, lseek, (int fd, off_t offset, int whence),
+                   (fd, offset, whence))
+
+
+/* msync(2).  */
+/* This syscall not implemented in uClibc
+CANCELABLE_SYSCALL (int, msync, (__ptr_t addr, size_t length, int flags),
+                   (addr, length, flags))
+*/
+
+/* nanosleep(2).  */
+CANCELABLE_SYSCALL (int, nanosleep, (const struct timespec *requested_time,
+                                    struct timespec *remaining),
+                   (requested_time, remaining))
+
+
+/* open(2).  */
+CANCELABLE_SYSCALL_VA (int, open, (const char *pathname, int flags, ...),
+                      (pathname, flags, va_arg (ap, mode_t)), flags)
+
+
+/* pause(2).  */
+CANCELABLE_SYSCALL (int, pause, (void), ())
+
+
+/* read(2).  */
+CANCELABLE_SYSCALL (ssize_t, read, (int fd, void *buf, size_t count),
+                   (fd, buf, count))
+
+
+/* system(3).  */
+CANCELABLE_SYSCALL (int, system, (const char *line), (line))
+
+/* tcdrain(2).  */
+CANCELABLE_SYSCALL (int, tcdrain, (int fd), (fd))
+
+/* wait(2).  */
+CANCELABLE_SYSCALL (__pid_t, wait, (__WAIT_STATUS_DEFN stat_loc), (stat_loc))
+
+
+/* waitpid(2).  */
+CANCELABLE_SYSCALL (__pid_t, waitpid, (__pid_t pid, int *stat_loc,
+                                      int options),
+                   (pid, stat_loc, options))
+
+
+/* write(2).  */
+CANCELABLE_SYSCALL (ssize_t, write, (int fd, const void *buf, size_t n),
+                   (fd, buf, n))
+
+
+/* The following system calls are thread cancellation points specified
+   in XNS.  */
+
+/* accept(2).  */
+CANCELABLE_SYSCALL (int, accept, (int fd, __SOCKADDR_ARG addr,
+                                 socklen_t *addr_len),
+                   (fd, addr, addr_len))
+
+/* connect(2).  */
+CANCELABLE_SYSCALL (int, connect, (int fd, __CONST_SOCKADDR_ARG addr,
+                                    socklen_t len),
+                   (fd, addr, len))
+
+/* recv(2).  */
+CANCELABLE_SYSCALL (int, recv, (int fd, __ptr_t buf, size_t n, int flags),
+                   (fd, buf, n, flags))
+
+/* recvfrom(2).  */
+CANCELABLE_SYSCALL (int, recvfrom, (int fd, __ptr_t buf, size_t n, int flags,
+                                   __SOCKADDR_ARG addr, socklen_t *addr_len),
+                   (fd, buf, n, flags, addr, addr_len))
+
+/* recvmsg(2).  */
+CANCELABLE_SYSCALL (int, recvmsg, (int fd, struct msghdr *message, int flags),
+                   (fd, message, flags))
+
+/* send(2).  */
+CANCELABLE_SYSCALL (int, send, (int fd, const __ptr_t buf, size_t n,
+                               int flags),
+                   (fd, buf, n, flags))
+
+/* sendmsg(2).  */
+CANCELABLE_SYSCALL (int, sendmsg, (int fd, const struct msghdr *message,
+                                  int flags),
+                   (fd, message, flags))
+
+/* sendto(2).  */
+CANCELABLE_SYSCALL (int, sendto, (int fd, const __ptr_t buf, size_t n,
+                                 int flags, __CONST_SOCKADDR_ARG addr,
+                                 socklen_t addr_len),
+                   (fd, buf, n, flags, addr, addr_len))
diff --git a/libpthread/linuxthreads_db/ChangeLog b/libpthread/linuxthreads_db/ChangeLog
new file mode 100644 (file)
index 0000000..4d29ae0
--- /dev/null
@@ -0,0 +1,208 @@
+2000-01-19  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_thr_getgregs.c: Correct size parameter of memset call.
+
+1999-12-02  Ulrich Drepper  <drepper@cygnus.com>
+
+       * proc_service.h: Fix typoes in last added declaractions.
+
+1999-12-01  Ulrich Drepper  <drepper@cygnus.com>
+
+       * proc_service.h: Add ps_pstop, ps_pcontinue, ps_lstop, and
+       ps_lcontinue prototypes.
+
+1999-11-23  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Makefile: Correct dependency for shared object.
+
+1999-11-22  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_ta_map_lwp2thr.c: Add missing brace in comparison.
+
+       * thread_dbP.h (LOG): Only print message if __td_debug is nonzero.
+       * td_init.c: Add __td_debug.
+
+1999-11-12  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_ta_thr_iter.c: Start copying list of descriptors from right
+       position in target process.
+
+       * td_ta_thr_iter.c: Fix loop starting point over all but main and
+       manager thread.
+
+       * td_ta_thr_iter.c: Read descriptors for main and manager thread
+       special since after this we can assume that no new threads will be
+       created anymore (at least in the gdb implementation).
+
+       * Makefile: Define version correctly.
+
+1999-11-10  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_ta_map_lwp2thr.c: If p_pid field is zero, this is before the
+       thread library is initialized and we get the PID from the
+       debugger.
+
+1999-11-08  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_thr_get_info.c: Make sure ti_lid is never zero.
+
+       * proc_service.h: Add ps_getpid prototype.
+
+1999-11-03  Ulrich Drepper  <drepper@cygnus.com>
+
+       * thread_dbP.h (ta_ok): New function.
+       * td_ta_new.c: Add new handle to list.
+       * td_ta_delete.c: Remove handle from list.
+       * td_ta_clear_event.c: Use ta_ok to check for correct ta parameter.
+       * td_ta_enable_stats.c: Likewise.
+       * td_ta_event_addr.c: Likewise.
+       * td_ta_event_getmsg.c: Likewise.
+       * td_ta_get_nthreads.c: Likewise.
+       * td_ta_get_ph.c: Likewise.
+       * td_ta_get_stats.c: Likewise.
+       * td_ta_map_id2thr.c: Likewise.
+       * td_ta_map_lwp2thr.c: Likewise.
+       * td_ta_reset_stats.c: Likewise.
+       * td_ta_set_event.c: Likewise.
+       * td_ta_setconcurrency.c: Likewise.
+       * td_ta_thr_iter.c: Likewise.
+
+       * td_ta_tsd_iter.c: Optimize memory retrieving.
+
+       * Versions: New file.
+
+       * td_thr_get_info.c (td_thr_get_info): Initialize ti_traceme.
+
+1999-11-02  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_ta_thr_iter.c (td_ta_thr_iter): Optimize a bit.  Read all
+       handles at once.
+
+       * thread_dbP.h (struct th_thragent): Add pthread_handle_num.
+       * td_ta_new.c: Initialize pthread_handle_num.
+       * td_ta_event_getmsg.c: If last event was already reported search
+       for another unreported event.
+
+       * td_thr_get_info.c (td_thr_get_info): Initialize ti_events.
+
+       * Makefile (libthread_db-routines): Add td_ta_set_event,
+       td_ta_event_getmsg, and td_ta_clear_event.
+       * td_ta_clear_event.c: New file.
+       * td_ta_event_getmsg.c: New file.
+       * td_ta_new.c: Get address of __pthread_last_event in target.
+       * td_ta_set_event.c: Don't overwrite old mask, set additional bits.
+       * td_thr_set_event.c: Likewise.
+       * td_thr_clear_event.c: Implement.
+       * thread_db.h: Declare td_ta_clear_event and td_ta_event_getmsg.
+       * thread_dbP.h (struct td_thragent): Add pthread_last_event.
+
+       * td_ta_new.c: Don't test for __pthread_threads_debug.  Get address
+       of __pthread_threads_events and fail if this is not possible.
+       * td_ta_event_addr.c: Implement.
+       * td_thr_event_enable.c: Implement.
+       * td_thr_event_getmsg.c: Implement.
+       * td_thr_set_event.c: Implement.
+       * td_ta_set_event.c: New file.
+       * thread_db.h (td_eventbuf_t): Define.
+       Declare td_ta_set_event.
+       * thread_dbP.h (struct td_thragent): Add pthread_threads_eventsp.
+
+       * td_thr_getfpregs.c: For terminated threads return empty structure.
+       * td_thr_getgregs.c: Likewise.
+       * td_thr_setfpregs.c: Likewise.
+       * td_thr_setgregs.c: Likewise.
+
+1999-11-01  Ulrich Drepper  <drepper@cygnus.com>
+
+       * thread_db.h: Shuffle types around to make things work for gdb.
+       * thread_dbP.h: Include proc_service.h before thread_db.h.
+
+       * thread_db.h: It's TD_NOLIBTHREAD, not TD_LIBTHREAD.
+       * td_ta_new.c: Likewise.
+
+1999-10-14  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_ta_new.c: p_startfct does not exist anymore.
+
+       * td_thr_get_info.c: Always initialize start function.
+
+       * td_ta_thr_iter.c: Don't return threads which exited (but are not
+       joined).
+
+       * td_thr_validate.c: Don't skip manager thread.
+
+1999-10-13  Ulrich Drepper  <drepper@cygnus.com>
+
+       * td_ta_thr_iter.c: Use size of descriptor from *TA.
+       Don't return manager thread before it's actually running.
+       Actually use state parameter to distingusih at least a few states.
+
+       * td_thr_get_info.c: Handle manager thread special.  Fill in ti_lid,
+       ti_state, and ti_startfunc fields.
+
+1999-10-12  Andreas Jaeger  <aj@suse.de>
+
+       * thread_dbP.h: Include <string.h> for strlen declaration.  Remove
+       __libc_write prototype since this is already declared in
+       linuxthreads/internals.h.
+
+1999-10-11  Ulrich Drepper  <drepper@cygnus.com>
+
+       * thread_db.h: Fix comment for ti_type.
+
+       * td_thr_get_info.c: Initialize ti_type field.
+
+       * td_ta_thr_iter.c: Also report the manager thread.
+
+1999-10-08  Andreas Jaeger  <aj@suse.de>
+
+       * thread_db.h: Fix typos in comments.
+
+       * td_ta_get_nthreads.c (td_ta_get_nthreads): Don't hardcode
+       libpthread library name, get it from <gnu/lib-names.h> instead.
+       * td_ta_new.c (td_ta_new): Likewise.
+
+1999-10-08  Ulrich Drepper  <drepper@cygnus.com>
+
+       * shlib-versions: New file.
+
+1999-10-07  Ulrich Drepper  <drepper@cygnus.com>
+
+       * Makefile: New file.
+       * proc_service.h: New file.
+       * td_init.c: New file.
+       * td_log.c: New file.
+       * td_ta_delete.c: New file.
+       * td_ta_enable_stats.c: New file.
+       * td_ta_event_addr.c: New file.
+       * td_ta_get_nthreads.c: New file.
+       * td_ta_get_ph.c: New file.
+       * td_ta_get_stats.c: New file.
+       * td_ta_map_id2thr.c: New file.
+       * td_ta_map_lwp2thr.c: New file.
+       * td_ta_new.c: New file.
+       * td_ta_reset_stats.c: New file.
+       * td_ta_setconcurrency.c: New file.
+       * td_ta_thr_iter.c: New file.
+       * td_ta_tsd_iter.c: New file.
+       * td_thr_clear_event.c: New file.
+       * td_thr_dbresume.c: New file.
+       * td_thr_dbsuspend.c: New file.
+       * td_thr_event_enable.c: New file.
+       * td_thr_event_getmsg.c: New file.
+       * td_thr_get_info.c: New file.
+       * td_thr_getfpregs.c: New file.
+       * td_thr_getgregs.c: New file.
+       * td_thr_getxregs.c: New file.
+       * td_thr_getxregsize.c: New file.
+       * td_thr_set_event.c: New file.
+       * td_thr_setfpregs.c: New file.
+       * td_thr_setgregs.c: New file.
+       * td_thr_setprio.c: New file.
+       * td_thr_setsigpending.c: New file.
+       * td_thr_setxregs.c: New file.
+       * td_thr_sigsetmask.c: New file.
+       * td_thr_tsd.c: New file.
+       * td_thr_validate.c: New file.
+       * thread_db.h: New file.
+       * thread_dbP.h: New file.
diff --git a/libpthread/linuxthreads_db/Makefile b/libpthread/linuxthreads_db/Makefile
new file mode 100644 (file)
index 0000000..eea12c7
--- /dev/null
@@ -0,0 +1,68 @@
+# Makefile for uClibc's pthread library
+#
+# Copyright (C) 2002 Erik Andersen <andersen@uclibc.org>
+#
+# 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.
+#
+# You should have received a copy of the GNU Library 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
+# Makefile for uClibc
+
+TOPDIR=../../
+include $(TOPDIR)Rules.mak
+
+#Adjust the soname version to avoid namespace collisions with glibc's libpthread
+PT_VERSION=$(MAJOR_VERSION).$(MINOR_VERSION)
+LIBPTHREAD=../libpthread.a
+
+# set up system dependencies include dirs (NOTE: order matters!)
+PTDIR = $(TOPDIR)libpthread/linuxthreads/
+SYSDEPINC = -I$(PTDIR)sysdeps/unix/sysv/linux \
+            -I$(PTDIR)sysdeps/pthread \
+            -I$(PTDIR)sysdeps/unix/sysv \
+            -I$(PTDIR)sysdeps/unix/unix \
+            -I$(PTDIR)sysdeps/$(TARGET_ARCH) \
+            -I$(PTDIR)sysdeps \
+            -I$(TOPDIR)libc/sysdeps/linux/$(TARGET_ARCH)
+CFLAGS += $(SYSDEPINC) -DLIBPTHREAD_SO="\"libpthread.so.$(PT_VERSION)\"" -D_GNU_SOURCE
+
+CSRC=td_init.c td_log.c td_ta_clear_event.c td_ta_delete.c \
+       td_ta_enable_stats.c td_ta_event_addr.c td_ta_event_getmsg.c \
+       td_ta_get_nthreads.c td_ta_get_ph.c td_ta_get_stats.c \
+       td_ta_map_id2thr.c td_ta_map_lwp2thr.c td_ta_new.c td_ta_reset_stats.c \
+       td_ta_set_event.c td_ta_setconcurrency.c td_ta_thr_iter.c \
+       td_ta_tsd_iter.c td_thr_clear_event.c td_thr_dbresume.c \
+       td_thr_dbsuspend.c td_thr_event_enable.c td_thr_event_getmsg.c \
+       td_thr_get_info.c td_thr_getfpregs.c td_thr_getgregs.c \
+       td_thr_getxregs.c td_thr_getxregsize.c td_thr_set_event.c \
+       td_thr_setfpregs.c td_thr_setgregs.c td_thr_setprio.c \
+       td_thr_setsigpending.c td_thr_setxregs.c td_thr_sigsetmask.c \
+       td_thr_tsd.c td_thr_validate.c
+COBJS=$(patsubst %.c,%.o, $(CSRC))
+OBJS=$(COBJS)
+
+all: $(OBJS) $(LIBPTHREAD)
+
+$(LIBPTHREAD): ar-target
+
+ar-target: $(OBJS)
+       $(AR) $(ARFLAGS) $(LIBPTHREAD) $(OBJS)
+
+$(COBJS): %.o : %.c
+       $(CC) $(CFLAGS) -c $< -o $@
+       $(STRIPTOOL) -x -R .note -R .comment $*.o
+
+clean:
+       rm -f *.[oa] *~ core
+
+
+
diff --git a/libpthread/linuxthreads_db/Versions b/libpthread/linuxthreads_db/Versions
new file mode 100644 (file)
index 0000000..83b30ee
--- /dev/null
@@ -0,0 +1,15 @@
+libthread_db {
+  GLIBC_2.1.3 {
+    # t*
+    td_init; td_log; td_ta_clear_event; td_ta_delete; td_ta_enable_stats;
+    td_ta_event_addr; td_ta_event_getmsg; td_ta_get_nthreads; td_ta_get_ph;
+    td_ta_get_stats; td_ta_map_id2thr; td_ta_map_lwp2thr; td_ta_new;
+    td_ta_reset_stats; td_ta_set_event; td_ta_setconcurrency;
+    td_ta_thr_iter; td_ta_tsd_iter; td_thr_clear_event; td_thr_dbresume;
+    td_thr_dbsuspend; td_thr_event_enable; td_thr_event_getmsg;
+    td_thr_get_info; td_thr_getfpregs; td_thr_getgregs; td_thr_getxregs;
+    td_thr_getxregsize; td_thr_set_event; td_thr_setfpregs; td_thr_setgregs;
+    td_thr_setprio; td_thr_setsigpending; td_thr_setxregs; td_thr_sigsetmask;
+    td_thr_tsd; td_thr_validate;
+  }
+}
diff --git a/libpthread/linuxthreads_db/proc_service.h b/libpthread/linuxthreads_db/proc_service.h
new file mode 100644 (file)
index 0000000..8f9a7d9
--- /dev/null
@@ -0,0 +1,70 @@
+/* Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* The definitions in this file must correspond to those in the debugger.  */
+#include <sys/procfs.h>
+
+typedef enum
+{
+  PS_OK,          /* generic "call succeeded" */
+  PS_ERR,         /* generic. */
+  PS_BADPID,      /* bad process handle */
+  PS_BADLID,      /* bad lwp identifier */
+  PS_BADADDR,     /* bad address */
+  PS_NOSYM,       /* p_lookup() could not find given symbol */
+        PS_NOFREGS
+  /*
+   * FPU register set not available for given
+   * lwp
+   */
+}       ps_err_e;
+
+
+struct ps_prochandle;          /* user defined. */
+
+
+extern ps_err_e ps_pdread(struct ps_prochandle *,
+                        psaddr_t, void *, size_t);
+extern ps_err_e ps_pdwrite(struct ps_prochandle *,
+                        psaddr_t, const void *, size_t);
+extern ps_err_e ps_ptread(struct ps_prochandle *,
+                        psaddr_t, void *, size_t);
+extern ps_err_e ps_ptwrite(struct ps_prochandle *,
+                        psaddr_t, const void *, size_t);
+
+extern ps_err_e ps_pglobal_lookup(struct ps_prochandle *,
+        const char *object_name, const char *sym_name, psaddr_t *sym_addr);
+
+
+extern ps_err_e ps_lgetregs(struct ps_prochandle *,
+                        lwpid_t, prgregset_t);
+extern ps_err_e ps_lsetregs(struct ps_prochandle *,
+                        lwpid_t, const prgregset_t);
+extern ps_err_e ps_lgetfpregs(struct ps_prochandle *,
+                        lwpid_t, prfpregset_t *);
+extern ps_err_e ps_lsetfpregs(struct ps_prochandle *,
+                        lwpid_t, const prfpregset_t *);
+
+extern pid_t ps_getpid (struct ps_prochandle *);
+
+
+extern ps_err_e ps_pstop (const struct ps_prochandle *);
+extern ps_err_e ps_pcontinue (const struct ps_prochandle *);
+
+extern ps_err_e ps_lstop (const struct ps_prochandle *, lwpid_t);
+extern ps_err_e ps_lcontinue (const struct ps_prochandle *, lwpid_t);
diff --git a/libpthread/linuxthreads_db/td_init.c b/libpthread/linuxthreads_db/td_init.c
new file mode 100644 (file)
index 0000000..683aec4
--- /dev/null
@@ -0,0 +1,32 @@
+/* Initialization function of thread debugger support library.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+int __td_debug;
+
+
+td_err_e
+td_init (void)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_log.c b/libpthread/linuxthreads_db/td_log.c
new file mode 100644 (file)
index 0000000..0c4a367
--- /dev/null
@@ -0,0 +1,32 @@
+/* Noop, left for historical reasons.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_log (void)
+{
+  /* This interface is deprecated in the Sun interface.  We provide it
+     for compatibility but don't do anyhting ourself.  We might in
+     future do some logging if this seems reasonable.  */
+  LOG (__FUNCTION__);
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_clear_event.c b/libpthread/linuxthreads_db/td_ta_clear_event.c
new file mode 100644 (file)
index 0000000..02d8336
--- /dev/null
@@ -0,0 +1,53 @@
+/* Globally disable events.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_clear_event (ta, event)
+     const td_thragent_t *ta;
+     td_thr_events_t *event;
+{
+  td_thr_events_t old_event;
+  int i;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdread (ta->ph, ta->pthread_threads_eventsp,
+                &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Remove the set bits in.  */
+  for (i = 0; i < TD_EVENTSIZE; ++i)
+    old_event.event_bits[i] &= ~event->event_bits[i];
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdwrite (ta->ph, ta->pthread_threads_eventsp,
+                 &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_delete.c b/libpthread/linuxthreads_db/td_ta_delete.c
new file mode 100644 (file)
index 0000000..e983577
--- /dev/null
@@ -0,0 +1,58 @@
+/* Detach to target process.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stdlib.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_ta_delete (td_thragent_t *ta)
+{
+  LOG (__FUNCTION__);
+
+  /* Safety check.  */
+  if (ta == NULL || __td_agent_list == NULL)
+    return TD_BADTA;
+
+  /* Remove the handle from the list.  */
+  if (ta == __td_agent_list->ta)
+    /* It's the first element of the list.  */
+    __td_agent_list = __td_agent_list->next;
+  else
+    {
+      /* We have to search for it.  */
+      struct agent_list *runp = __td_agent_list;
+
+      while (runp->next != NULL && runp->next->ta != ta)
+       runp = runp->next;
+
+      if (runp->next == NULL)
+       /* It's not a valid decriptor since it is not in the list.  */
+       return TD_BADTA;
+
+      runp->next = runp->next->next;
+    }
+
+  /* The handle was allocated in `td_ta_new'.  */
+  free (ta);
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_enable_stats.c b/libpthread/linuxthreads_db/td_ta_enable_stats.c
new file mode 100644 (file)
index 0000000..abf4d20
--- /dev/null
@@ -0,0 +1,35 @@
+/* Enable collection of statistics for process.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_enable_stats (const td_thragent_t *ta, int enable)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_event_addr.c b/libpthread/linuxthreads_db/td_ta_event_addr.c
new file mode 100644 (file)
index 0000000..e610e44
--- /dev/null
@@ -0,0 +1,75 @@
+/* Get event address.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <gnu/lib-names.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_ta_event_addr (const td_thragent_t *ta, td_event_e event, td_notify_t *addr)
+{
+  td_err_e res = TD_NOEVENT;
+  const char *symbol = NULL;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  switch (event)
+    {
+    case TD_CREATE:
+      symbol = "__linuxthreads_create_event";
+      break;
+
+    case TD_DEATH:
+      symbol = "__linuxthreads_death_event";
+      break;
+
+    case TD_REAP:
+      symbol = "__linuxthreads_reap_event";
+      break;
+
+    default:
+      /* Event cannot be handled.  */
+      break;
+    }
+
+  /* Now get the address.  */
+  if (symbol != NULL)
+    {
+      psaddr_t taddr;
+
+      if (ps_pglobal_lookup (ta->ph, LIBPTHREAD_SO, symbol, &taddr) == PS_OK)
+       {
+         /* Success, we got the address.  */
+         addr->type = NOTIFY_BPT;
+         addr->u.bptaddr = taddr;
+
+         res = TD_OK;
+       }
+      else
+       res = TD_ERR;
+    }
+
+  return res;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_event_getmsg.c b/libpthread/linuxthreads_db/td_ta_event_getmsg.c
new file mode 100644 (file)
index 0000000..4c635dc
--- /dev/null
@@ -0,0 +1,128 @@
+/* Retrieve event.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+#include <string.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_ta_event_getmsg (const td_thragent_t *ta, td_event_msg_t *msg)
+{
+  /* XXX I cannot think of another way but using a static variable.  */
+  static td_thrhandle_t th;
+  td_eventbuf_t event;
+  psaddr_t addr;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  /* Get the pointer to the thread descriptor with the last event.  */
+  if (ps_pdread (ta->ph, ta->pthread_last_event,
+                &addr, sizeof (void *)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* If the pointer is NULL no event occurred.  */
+  if (addr == 0)
+    return TD_NOMSG;
+
+  /* Read the even structure from the target.  */
+  if (ps_pdread (ta->ph,
+                ((char *) addr
+                 + offsetof (struct _pthread_descr_struct, p_eventbuf)),
+                &event, sizeof (td_eventbuf_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Check whether an event occurred.  */
+  if (event.eventnum == TD_EVENT_NONE)
+    {
+      /* Oh well, this means the last event was already read.  So
+        we have to look for any other event.  */
+      struct pthread_handle_struct handles[ta->pthread_threads_max];
+      int num;
+      int i;
+
+      /* Read the number of currently active threads.  */
+      if (ps_pdread (ta->ph, ta->pthread_handles_num, &num, sizeof (int))
+         != PS_OK)
+       return TD_ERR;  /* XXX Other error value?  */
+
+      /* Now read the handles.  */
+      if (ps_pdread (ta->ph, ta->handles, handles,
+                    ta->pthread_threads_max * sizeof (handles[0])) != PS_OK)
+       return TD_ERR;  /* XXX Other error value?  */
+
+      for (i = 0; i < ta->pthread_threads_max && num > 0; ++i)
+       {
+         if (handles[i].h_descr == NULL)
+           /* No entry here.  */
+           continue;
+
+         /* First count this active thread.  */
+         --num;
+
+         if (handles[i].h_descr == addr)
+           /* We already handled this.  */
+           continue;
+
+         /* Read the event data for this thread.  */
+         if (ps_pdread (ta->ph,
+                        ((char *) handles[i].h_descr
+                         + offsetof (struct _pthread_descr_struct,
+                                     p_eventbuf)),
+                        &event, sizeof (td_eventbuf_t)) != PS_OK)
+           return TD_ERR;
+
+         if (event.eventnum != TD_EVENT_NONE)
+           {
+             /* We found a thread with an unreported event.  */
+             addr = handles[i].h_descr;
+             break;
+           }
+       }
+
+      /* If we haven't found any other event signal this to the user.  */
+      if (event.eventnum == TD_EVENT_NONE)
+       return TD_NOMSG;
+    }
+
+  /* Generate the thread descriptor.  */
+  th.th_ta_p = (td_thragent_t *) ta;
+  th.th_unique = addr;
+
+  /* Fill the user's data structure.  */
+  msg->event = event.eventnum;
+  msg->th_p = &th;
+  msg->msg.data = (uintptr_t) event.eventdata;
+
+  /* And clear the event message in the target.  */
+  memset (&event, '\0', sizeof (td_eventbuf_t));
+  if (ps_pdwrite (ta->ph,
+                 ((char *) addr
+                  + offsetof (struct _pthread_descr_struct, p_eventbuf)),
+                 &event, sizeof (td_eventbuf_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_get_nthreads.c b/libpthread/linuxthreads_db/td_ta_get_nthreads.c
new file mode 100644 (file)
index 0000000..7800487
--- /dev/null
@@ -0,0 +1,44 @@
+/* Get the number of threads in the process.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+//#include <gnu/lib-names.h>
+
+td_err_e
+td_ta_get_nthreads (const td_thragent_t *ta, int *np)
+{
+  psaddr_t addr;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  /* Access the variable `__pthread_handles_num'.  */
+  if (ps_pglobal_lookup (ta->ph, LIBPTHREAD_SO, "__pthread_handles_num",
+                        &addr) != PS_OK)
+     return TD_ERR;    /* XXX Other error value?  */
+
+  if (ps_pdread (ta->ph, addr, np, sizeof (int)) != PS_OK)
+     return TD_ERR;    /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_get_ph.c b/libpthread/linuxthreads_db/td_ta_get_ph.c
new file mode 100644 (file)
index 0000000..b748916
--- /dev/null
@@ -0,0 +1,36 @@
+/* Get external process handle.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_get_ph (const td_thragent_t *ta, struct ps_prochandle **ph)
+{
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  *ph = ta->ph;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_get_stats.c b/libpthread/linuxthreads_db/td_ta_get_stats.c
new file mode 100644 (file)
index 0000000..1741d81
--- /dev/null
@@ -0,0 +1,35 @@
+/* Retrieve statistics for process.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_get_stats (const td_thragent_t *ta, td_ta_stats_t *statsp)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_map_id2thr.c b/libpthread/linuxthreads_db/td_ta_map_id2thr.c
new file mode 100644 (file)
index 0000000..6fb1ba9
--- /dev/null
@@ -0,0 +1,63 @@
+/* Map thread ID to thread handle.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_map_id2thr (const td_thragent_t *ta, pthread_t pt, td_thrhandle_t *th)
+{
+  struct pthread_handle_struct phc;
+  struct _pthread_descr_struct pds;
+  int pthread_threads_max;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  /* Make the following expression a bit smaller.  */
+  pthread_threads_max = ta->pthread_threads_max;
+
+  /* We can compute the entry in the handle array we want.  */
+  if (ps_pdread (ta->ph, ta->handles + pt % pthread_threads_max, &phc,
+                sizeof (struct pthread_handle_struct)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Test whether this entry is in use.  */
+  if (phc.h_descr == NULL)
+    return TD_BADTH;
+
+  /* Next test: get the descriptor to see whether this is not an old
+     thread handle.  */
+  if (ps_pdread (ta->ph, phc.h_descr, &pds,
+                sizeof (struct _pthread_descr_struct)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  if (pds.p_tid != pt)
+    return TD_BADTH;
+
+  /* Create the `td_thrhandle_t' object.  */
+  th->th_ta_p = (td_thragent_t *) ta;
+  th->th_unique = phc.h_descr;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_map_lwp2thr.c b/libpthread/linuxthreads_db/td_ta_map_lwp2thr.c
new file mode 100644 (file)
index 0000000..8c93429
--- /dev/null
@@ -0,0 +1,81 @@
+/* Which thread is running on an lwp?
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_map_lwp2thr (const td_thragent_t *ta, lwpid_t lwpid, td_thrhandle_t *th)
+{
+  int pthread_threads_max = ta->pthread_threads_max;
+  size_t sizeof_descr = ta->sizeof_descr;
+  struct pthread_handle_struct phc[pthread_threads_max];
+  size_t cnt;
+#ifdef ALL_THREADS_STOPPED
+  int num;
+#else
+# define num 1
+#endif
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  /* Read all the descriptors.  */
+  if (ps_pdread (ta->ph, ta->handles, phc,
+                sizeof (struct pthread_handle_struct) * pthread_threads_max)
+      != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+#ifdef ALL_THREADS_STOPPED
+  /* Read the number of currently active threads.  */
+  if (ps_pdread (ta->ph, ta->pthread_handles_num, &num, sizeof (int)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+#endif
+
+  /* Get the entries one after the other and find out whether the ID
+     matches.  */
+  for (cnt = 0; cnt < pthread_threads_max && num > 0; ++cnt)
+    if (phc[cnt].h_descr != NULL)
+      {
+       struct _pthread_descr_struct pds;
+
+#ifdef ALL_THREADS_STOPPED
+       /* First count this active thread.  */
+       --num;
+#endif
+
+       if (ps_pdread (ta->ph, phc[cnt].h_descr, &pds, sizeof_descr) != PS_OK)
+         return TD_ERR;        /* XXX Other error value?  */
+
+       if ((pds.p_pid ?: ps_getpid (ta->ph)) == lwpid)
+         {
+           /* Found it.  Now fill in the `td_thrhandle_t' object.  */
+           th->th_ta_p = (td_thragent_t *) ta;
+           th->th_unique = phc[cnt].h_descr;
+
+           return TD_OK;
+         }
+    }
+
+  return TD_NOLWP;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_new.c b/libpthread/linuxthreads_db/td_ta_new.c
new file mode 100644 (file)
index 0000000..32b9b04
--- /dev/null
@@ -0,0 +1,154 @@
+/* Attach to target process.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+#include <stdlib.h>
+//#include <gnu/lib-names.h>
+
+#include "thread_dbP.h"
+
+
+/* Datatype for the list of known thread agents.  Normally there will
+   be exactly one so we don't spend much though on making it fast.  */
+struct agent_list *__td_agent_list;
+
+
+td_err_e
+td_ta_new (struct ps_prochandle *ps, td_thragent_t **ta)
+{
+  psaddr_t addr;
+  struct agent_list *elemp;
+
+  LOG (__FUNCTION__);
+
+  /* Get the global event mask.  This is one of the variables which
+     are new in the thread library to enable debugging.  If it is
+     not available we cannot debug.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__pthread_threads_events", &addr) != PS_OK)
+    return TD_NOLIBTHREAD;
+
+  /* Fill in the appropriate information.  */
+  *ta = (td_thragent_t *) malloc (sizeof (td_thragent_t));
+  if (*ta == NULL)
+    return TD_MALLOC;
+
+  /* Store the proc handle which we will pass to the callback functions
+     back into the debugger.  */
+  (*ta)->ph = ps;
+
+  /* Remember the address.  */
+  (*ta)->pthread_threads_eventsp = (td_thr_events_t *) addr;
+
+  /* Get the pointer to the variable pointing to the thread descriptor
+     with the last event.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__pthread_last_event",
+                        &(*ta)->pthread_last_event) != PS_OK)
+    {
+    free_return:
+      free (*ta);
+      return TD_ERR;
+    }
+
+  /* Get the pointer to the variable containing the number of active
+     threads.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__pthread_handles_num",
+                        &(*ta)->pthread_handles_num) != PS_OK)
+    goto free_return;
+
+  /* See whether the library contains the necessary symbols.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, "__pthread_handles",
+                        &addr) != PS_OK)
+    goto free_return;
+
+  (*ta)->handles = (struct pthread_handle_struct *) addr;
+
+
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO, "pthread_keys",
+                        &addr) != PS_OK)
+    goto free_return;
+
+  /* Cast to the right type.  */
+  (*ta)->keys = (struct pthread_key_struct *) addr;
+
+  /* Find out about the maximum number of threads.  Old implementations
+     don't provide this information.  In this case we assume that the
+     debug  library is compiled with the same values.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__linuxthreads_pthread_threads_max", &addr) != PS_OK)
+    (*ta)->pthread_threads_max = PTHREAD_THREADS_MAX;
+  else
+    {
+      if (ps_pdread (ps, addr, &(*ta)->pthread_threads_max, sizeof (int))
+         != PS_OK)
+       goto free_return;
+    }
+
+  /* Similar for the maximum number of thread local data keys.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__linuxthreads_pthread_keys_max", &addr) != PS_OK)
+    (*ta)->pthread_keys_max = PTHREAD_KEYS_MAX;
+  else
+    {
+      if (ps_pdread (ps, addr, &(*ta)->pthread_keys_max, sizeof (int))
+         != PS_OK)
+       goto free_return;
+    }
+
+  /* And for the size of the second level arrays for the keys.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__linuxthreads_pthread_sizeof_descr", &addr)
+      != PS_OK)
+    (*ta)->sizeof_descr = sizeof (struct _pthread_descr_struct);
+  else
+    {
+      if (ps_pdread (ps, addr, &(*ta)->sizeof_descr, sizeof (int)) != PS_OK)
+       goto free_return;
+    }
+
+  /* Similar for the maximum number of thread local data keys.  */
+  if (ps_pglobal_lookup (ps, LIBPTHREAD_SO,
+                        "__linuxthreads_pthread_keys_max", &addr) != PS_OK)
+    (*ta)->pthread_keys_max = PTHREAD_KEYS_MAX;
+  else
+    {
+      if (ps_pdread (ps, addr, &(*ta)->pthread_keys_max, sizeof (int))
+         != PS_OK)
+       goto free_return;
+    }
+
+  /* Now add the new agent descriptor to the list.  */
+  elemp = (struct agent_list *) malloc (sizeof (struct agent_list));
+  if (elemp == NULL)
+    {
+      /* Argh, now that everything else worked...  */
+      free (*ta);
+      return TD_MALLOC;
+    }
+
+  /* We don't care for thread-safety here.  */
+  elemp->ta = *ta;
+  elemp->next = __td_agent_list;
+  __td_agent_list = elemp;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_reset_stats.c b/libpthread/linuxthreads_db/td_ta_reset_stats.c
new file mode 100644 (file)
index 0000000..11401b9
--- /dev/null
@@ -0,0 +1,35 @@
+/* Reset statistics.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_reset_stats (const td_thragent_t *ta)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_set_event.c b/libpthread/linuxthreads_db/td_ta_set_event.c
new file mode 100644 (file)
index 0000000..4d87fe1
--- /dev/null
@@ -0,0 +1,53 @@
+/* Globally enable events.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_set_event (ta, event)
+     const td_thragent_t *ta;
+     td_thr_events_t *event;
+{
+  td_thr_events_t old_event;
+  int i;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdread (ta->ph, ta->pthread_threads_eventsp,
+                &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Or the new bits in.  */
+  for (i = 0; i < TD_EVENTSIZE; ++i)
+    old_event.event_bits[i] |= event->event_bits[i];
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdwrite (ta->ph, ta->pthread_threads_eventsp,
+                 &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_setconcurrency.c b/libpthread/linuxthreads_db/td_ta_setconcurrency.c
new file mode 100644 (file)
index 0000000..5bb2601
--- /dev/null
@@ -0,0 +1,35 @@
+/* Set suggested concurrency level for process.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_ta_setconcurrency (const td_thragent_t *ta, int level)
+{
+  /* This is something LinuxThreads does not support.  */
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  return TD_NOCAPAB;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_thr_iter.c b/libpthread/linuxthreads_db/td_ta_thr_iter.c
new file mode 100644 (file)
index 0000000..f1223d1
--- /dev/null
@@ -0,0 +1,143 @@
+/* Iterate over a process's threads.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stdlib.h>
+#include "thread_dbP.h"
+
+
+static int
+handle_descr (const td_thragent_t *ta, td_thr_iter_f *callback,
+             void *cbdata_p, td_thr_state_e state, int ti_pri,
+             size_t cnt, pthread_descr descr)
+{
+  struct _pthread_descr_struct pds;
+  size_t sizeof_descr = ta->sizeof_descr;
+  td_thrhandle_t th;
+
+  if (ps_pdread (ta->ph, descr, &pds, sizeof_descr) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* The manager thread must be handled special.  The descriptor
+     exists but the thread only gets created when the first
+     `pthread_create' call is issued.  A clear indication that this
+     happened is when the p_pid field is non-zero.  */
+  if (cnt == 1 && pds.p_pid == 0)
+    return TD_OK;
+
+  /* Now test whether this thread matches the specified
+     conditions.  */
+
+  /* Only if the priority level is as high or higher.  */
+  if (pds.p_priority < ti_pri)
+    return TD_OK;
+
+  /* Test the state.
+     XXX This is incomplete.  */
+  if (state != TD_THR_ANY_STATE)
+    return TD_OK;
+
+  /* XXX For now we ignore threads which are not running anymore.
+     The reason is that gdb tries to get the registers and fails.
+     In future we should have a special mode of the thread library
+     in which we keep the process around until the actual join
+     operation happened.  */
+  if (pds.p_exited != 0)
+    return TD_OK;
+
+  /* Yep, it matches.  Call the callback function.  */
+  th.th_ta_p = (td_thragent_t *) ta;
+  th.th_unique = descr;
+  if (callback (&th, cbdata_p) != 0)
+    return TD_DBERR;
+
+  /* All done successfully.  */
+  return TD_OK;
+}
+
+
+td_err_e
+td_ta_thr_iter (const td_thragent_t *ta, td_thr_iter_f *callback,
+               void *cbdata_p, td_thr_state_e state, int ti_pri,
+               sigset_t *ti_sigmask_p, unsigned int ti_user_flags)
+{
+  int pthread_threads_max;
+  struct pthread_handle_struct *phc;
+  td_err_e result = TD_OK;
+  int cnt;
+#ifdef ALL_THREADS_STOPPED
+  int num;
+#else
+# define num 1
+#endif
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  pthread_threads_max = ta->pthread_threads_max;
+  phc = (struct pthread_handle_struct *) alloca (sizeof (phc[0])
+                                                * pthread_threads_max);
+
+  /* First read only the main thread and manager thread information.  */
+  if (ps_pdread (ta->ph, ta->handles, phc,
+                sizeof (struct pthread_handle_struct) * 2) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Now handle these descriptors.  */
+  result = handle_descr (ta, callback, cbdata_p, state, ti_pri, 0,
+                        phc[0].h_descr);
+  if (result != TD_OK)
+    return result;
+  result = handle_descr (ta, callback, cbdata_p, state, ti_pri, 1,
+                        phc[1].h_descr);
+  if (result != TD_OK)
+    return result;
+
+  /* Read all the descriptors.  */
+  if (ps_pdread (ta->ph, ta->handles + 2, &phc[2],
+                (sizeof (struct pthread_handle_struct)
+                 * (pthread_threads_max - 2))) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+#ifdef ALL_THREADS_STOPPED
+  /* Read the number of currently active threads.  */
+  if (ps_pdread (ta->ph, ta->pthread_handles_num, &num, sizeof (int)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+#endif
+
+  /* Now get all descriptors, one after the other.  */
+  for (cnt = 2; cnt < pthread_threads_max && num > 0; ++cnt)
+    if (phc[cnt].h_descr != NULL)
+      {
+#ifdef ALL_THREADS_STOPPED
+       /* First count this active thread.  */
+       --num;
+#endif
+
+       result = handle_descr (ta, callback, cbdata_p, state, ti_pri, cnt,
+                              phc[cnt].h_descr);
+       if (result != TD_OK)
+         break;
+      }
+
+  return result;
+}
diff --git a/libpthread/linuxthreads_db/td_ta_tsd_iter.c b/libpthread/linuxthreads_db/td_ta_tsd_iter.c
new file mode 100644 (file)
index 0000000..b761be1
--- /dev/null
@@ -0,0 +1,56 @@
+/* Iterate over a process's thread-specific data.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stdlib.h>
+#include "thread_dbP.h"
+
+
+td_err_e
+td_ta_tsd_iter (const td_thragent_t *ta, td_key_iter_f *callback,
+               void *cbdata_p)
+{
+  struct pthread_key_struct *keys;
+  int pthread_keys_max;
+  int cnt;
+
+  LOG (__FUNCTION__);
+
+  /* Test whether the TA parameter is ok.  */
+  if (! ta_ok (ta))
+    return TD_BADTA;
+
+  pthread_keys_max = ta->pthread_keys_max;
+  keys = (struct pthread_key_struct *) alloca (sizeof (keys[0])
+                                              * pthread_keys_max);
+
+  /* Read all the information about the keys.  */
+  if (ps_pdread (ta->ph, ta->keys, keys,
+                sizeof (keys[0]) * pthread_keys_max) != PS_OK)
+       return TD_ERR;  /* XXX Other error value?  */
+
+  /* Now get all descriptors, one after the other.  */
+  for (cnt = 0; cnt < pthread_keys_max; ++cnt)
+    if (keys[cnt].in_use
+       /* Return with an error if the callback returns a nonzero value.  */
+       && callback (cnt, keys[cnt].destr, cbdata_p) != 0)
+      return TD_DBERR;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_clear_event.c b/libpthread/linuxthreads_db/td_thr_clear_event.c
new file mode 100644 (file)
index 0000000..8ef8cbb
--- /dev/null
@@ -0,0 +1,57 @@
+/* Disable specific event for thread.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_thr_clear_event (th, event)
+     const td_thrhandle_t *th;
+     td_thr_events_t *event;
+{
+  td_thr_events_t old_event;
+  int i;
+
+  LOG (__FUNCTION__);
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdread (th->th_ta_p->ph,
+                ((char *) th->th_unique
+                 + offsetof (struct _pthread_descr_struct,
+                             p_eventbuf.eventmask)),
+                &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Remove the set bits in.  */
+  for (i = 0; i < TD_EVENTSIZE; ++i)
+    old_event.event_bits[i] &= ~event->event_bits[i];
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdwrite (th->th_ta_p->ph,
+                 ((char *) th->th_unique
+                  + offsetof (struct _pthread_descr_struct,
+                              p_eventbuf.eventmask)),
+                 &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_dbresume.c b/libpthread/linuxthreads_db/td_thr_dbresume.c
new file mode 100644 (file)
index 0000000..692943a
--- /dev/null
@@ -0,0 +1,30 @@
+/* Resume execution of given thread.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_dbresume (const td_thrhandle_t *th)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+  return TD_NOCAPAB;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_dbsuspend.c b/libpthread/linuxthreads_db/td_thr_dbsuspend.c
new file mode 100644 (file)
index 0000000..9e7ceb6
--- /dev/null
@@ -0,0 +1,30 @@
+/* Suspend execution of given thread.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_dbsuspend (const td_thrhandle_t *th)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+  return TD_NOCAPAB;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_event_enable.c b/libpthread/linuxthreads_db/td_thr_event_enable.c
new file mode 100644 (file)
index 0000000..79e7d3a
--- /dev/null
@@ -0,0 +1,41 @@
+/* Enable event process-wide.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_thr_event_enable (th, onoff)
+     const td_thrhandle_t *th;
+     int onoff;
+{
+  LOG (__FUNCTION__);
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdwrite (th->th_ta_p->ph,
+                 ((char *) th->th_unique
+                  + offsetof (struct _pthread_descr_struct, p_report_events)),
+                 &onoff, sizeof (int)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_event_getmsg.c b/libpthread/linuxthreads_db/td_thr_event_getmsg.c
new file mode 100644 (file)
index 0000000..4812ece
--- /dev/null
@@ -0,0 +1,60 @@
+/* Retrieve event.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+#include <string.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_thr_event_getmsg (const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+  td_eventbuf_t event;
+
+  LOG (__FUNCTION__);
+
+  /* Read the even structure from the target.  */
+  if (ps_pdread (th->th_ta_p->ph,
+                ((char *) th->th_unique
+                 + offsetof (struct _pthread_descr_struct, p_eventbuf)),
+                &event, sizeof (td_eventbuf_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Check whether an event occurred.  */
+  if (event.eventnum == TD_EVENT_NONE)
+    /* Nothing.  */
+    return TD_NOMSG;
+
+  /* Fill the user's data structure.  */
+  msg->event = event.eventnum;
+  msg->th_p = th;
+  msg->msg.data = (uintptr_t) event.eventdata;
+
+  /* And clear the event message in the target.  */
+  memset (&event, '\0', sizeof (td_eventbuf_t));
+  if (ps_pdwrite (th->th_ta_p->ph,
+                 ((char *) th->th_unique
+                  + offsetof (struct _pthread_descr_struct, p_eventbuf)),
+                 &event, sizeof (td_eventbuf_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_get_info.c b/libpthread/linuxthreads_db/td_thr_get_info.c
new file mode 100644 (file)
index 0000000..25ad340
--- /dev/null
@@ -0,0 +1,75 @@
+/* Get thread information.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+#include <string.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_thr_get_info (const td_thrhandle_t *th, td_thrinfo_t *infop)
+{
+  struct _pthread_descr_struct pds;
+
+  LOG (__FUNCTION__);
+
+  /* Get the thread descriptor.  */
+  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
+                th->th_ta_p->sizeof_descr) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Fill in information.  Clear first to provide reproducable
+     results for the fields we do not fill in.  */
+  memset (infop, '\0', sizeof (td_thrinfo_t));
+
+  /* We have to handle the manager thread special since the thread
+     descriptor in older versions is not fully initialized.  */
+  if (pds.p_nr == 1)
+    {
+      infop->ti_tid = th->th_ta_p->pthread_threads_max * 2 + 1;
+      infop->ti_type = TD_THR_SYSTEM;
+      infop->ti_state = TD_THR_RUN;
+    }
+  else
+    {
+      infop->ti_tid = pds.p_tid;
+      infop->ti_tls = (char *) pds.p_specific;
+      infop->ti_pri = pds.p_priority;
+      infop->ti_type = TD_THR_USER;
+
+      if (pds.p_exited)
+       /* This should not happen.  */
+       infop->ti_state = TD_THR_ZOMBIE;
+      else
+       /* XXX For now there is no way to get more information.  */
+       infop->ti_state = TD_THR_RUN;
+    }
+
+  /* Initialization which are the same in both cases.  */
+  infop->ti_lid = pds.p_pid ?: ps_getpid (th->th_ta_p->ph);
+  infop->ti_ta_p = th->th_ta_p;
+  infop->ti_startfunc = pds.p_start_args.start_routine;
+  memcpy (&infop->ti_events, &pds.p_eventbuf.eventmask,
+         sizeof (td_thr_events_t));
+  infop->ti_traceme = pds.p_report_events != 0;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_getfpregs.c b/libpthread/linuxthreads_db/td_thr_getfpregs.c
new file mode 100644 (file)
index 0000000..e6635d2
--- /dev/null
@@ -0,0 +1,44 @@
+/* Get a thread's floating-point register set.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_getfpregs (const td_thrhandle_t *th, prfpregset_t *regset)
+{
+  struct _pthread_descr_struct pds;
+
+  LOG (__FUNCTION__);
+
+  /* We have to get the state and the PID for this thread.  */
+  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
+                sizeof (struct _pthread_descr_struct)) != PS_OK)
+    return TD_ERR;
+
+  /* If the thread already terminated we return all zeroes.  */
+  if (pds.p_terminated)
+    memset (regset, '\0', sizeof (*regset));
+  /* Otherwise get the register content through the callback.  */
+  else if (ps_lgetfpregs (th->th_ta_p->ph, pds.p_pid, regset) != PS_OK)
+    return TD_ERR;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_getgregs.c b/libpthread/linuxthreads_db/td_thr_getgregs.c
new file mode 100644 (file)
index 0000000..a4d8619
--- /dev/null
@@ -0,0 +1,44 @@
+/* Get a thread's general register set.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_getgregs (const td_thrhandle_t *th, prgregset_t gregs)
+{
+  struct _pthread_descr_struct pds;
+
+  LOG (__FUNCTION__);
+
+  /* We have to get the state and the PID for this thread.  */
+  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
+                sizeof (struct _pthread_descr_struct)) != PS_OK)
+    return TD_ERR;
+
+  /* If the thread already terminated we return all zeroes.  */
+  if (pds.p_terminated)
+    memset (gregs, '\0', sizeof (prgregset_t));
+  /* Otherwise get the register content through the callback.  */
+  else if (ps_lgetregs (th->th_ta_p->ph, pds.p_pid, gregs) != PS_OK)
+    return TD_ERR;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_getxregs.c b/libpthread/linuxthreads_db/td_thr_getxregs.c
new file mode 100644 (file)
index 0000000..d8d5683
--- /dev/null
@@ -0,0 +1,30 @@
+/* Get a thread's extra state register set.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_getxregs (const td_thrhandle_t *th, void *xregs)
+{
+  /* XXX This might be platform specific.  */
+  LOG (__FUNCTION__);
+  return TD_NOXREGS;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_getxregsize.c b/libpthread/linuxthreads_db/td_thr_getxregsize.c
new file mode 100644 (file)
index 0000000..f704a31
--- /dev/null
@@ -0,0 +1,30 @@
+/* Get the size of the extra state register set for this architecture.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_getxregsize (const td_thrhandle_t *th, int *sizep)
+{
+  /* XXX This might be platform specific.  */
+  LOG (__FUNCTION__);
+  return TD_NOXREGS;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_set_event.c b/libpthread/linuxthreads_db/td_thr_set_event.c
new file mode 100644 (file)
index 0000000..583a2f8
--- /dev/null
@@ -0,0 +1,57 @@
+/* Enable specific event for thread.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 <stddef.h>
+
+#include "thread_dbP.h"
+
+
+td_err_e
+td_thr_set_event (th, event)
+     const td_thrhandle_t *th;
+     td_thr_events_t *event;
+{
+  td_thr_events_t old_event;
+  int i;
+
+  LOG (__FUNCTION__);
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdread (th->th_ta_p->ph,
+                ((char *) th->th_unique
+                 + offsetof (struct _pthread_descr_struct,
+                             p_eventbuf.eventmask)),
+                &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Or the new bits in.  */
+  for (i = 0; i < TD_EVENTSIZE; ++i)
+    old_event.event_bits[i] |= event->event_bits[i];
+
+  /* Write the new value into the thread data structure.  */
+  if (ps_pdwrite (th->th_ta_p->ph,
+                 ((char *) th->th_unique
+                  + offsetof (struct _pthread_descr_struct,
+                              p_eventbuf.eventmask)),
+                 &old_event, sizeof (td_thrhandle_t)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_setfpregs.c b/libpthread/linuxthreads_db/td_thr_setfpregs.c
new file mode 100644 (file)
index 0000000..0c426b3
--- /dev/null
@@ -0,0 +1,42 @@
+/* Set a thread's floating-point register set.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_setfpregs (const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+  struct _pthread_descr_struct pds;
+
+  LOG (__FUNCTION__);
+
+  /* We have to get the state and the PID for this thread.  */
+  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
+                 sizeof (struct _pthread_descr_struct)) != PS_OK)
+    return TD_ERR;
+
+  /* Only set the registers if the thread hasn't yet terminated.  */
+  if (pds.p_terminated == 0
+      && ps_lsetfpregs (th->th_ta_p->ph, pds.p_pid, fpregs) != PS_OK)
+    return TD_ERR;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_setgregs.c b/libpthread/linuxthreads_db/td_thr_setgregs.c
new file mode 100644 (file)
index 0000000..72f95b1
--- /dev/null
@@ -0,0 +1,42 @@
+/* Set a thread's general register set.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_setgregs (const td_thrhandle_t *th, prgregset_t gregs)
+{
+  struct _pthread_descr_struct pds;
+
+  LOG (__FUNCTION__);
+
+  /* We have to get the state and the PID for this thread.  */
+  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
+                 sizeof (struct _pthread_descr_struct)) != PS_OK)
+    return TD_ERR;
+
+  /* Only set the registers if the thread hasn't yet terminated.  */
+  if (pds.p_terminated == 0
+      && ps_lsetregs (th->th_ta_p->ph, pds.p_pid, gregs) != PS_OK)
+    return TD_ERR;
+
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_setprio.c b/libpthread/linuxthreads_db/td_thr_setprio.c
new file mode 100644 (file)
index 0000000..03a503d
--- /dev/null
@@ -0,0 +1,30 @@
+/* Set a thread's priority.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_setprio (const td_thrhandle_t *th, int prio)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_setsigpending.c b/libpthread/linuxthreads_db/td_thr_setsigpending.c
new file mode 100644 (file)
index 0000000..815cd22
--- /dev/null
@@ -0,0 +1,31 @@
+/* Raise a signal for a thread.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_setsigpending (const td_thrhandle_t *th, unsigned char n,
+                     const sigset_t *ss)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_setxregs.c b/libpthread/linuxthreads_db/td_thr_setxregs.c
new file mode 100644 (file)
index 0000000..4b4fc34
--- /dev/null
@@ -0,0 +1,30 @@
+/* Set a thread's extra state register set.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_setxregs (const td_thrhandle_t *ta, const void *addr)
+{
+  /* XXX This might have to be platform specific.  */
+  LOG (__FUNCTION__);
+  return TD_NOXREGS;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_sigsetmask.c b/libpthread/linuxthreads_db/td_thr_sigsetmask.c
new file mode 100644 (file)
index 0000000..8811597
--- /dev/null
@@ -0,0 +1,30 @@
+/* Set a thread's signal mask.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_sigsetmask (const td_thrhandle_t *th, const sigset_t *ss)
+{
+  /* XXX We have to figure out what has to be done.  */
+  LOG (__FUNCTION__);
+  return TD_OK;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_tsd.c b/libpthread/linuxthreads_db/td_thr_tsd.c
new file mode 100644 (file)
index 0000000..0453c98
--- /dev/null
@@ -0,0 +1,76 @@
+/* Get a thread-specific data pointer for a thread.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_tsd (const td_thrhandle_t *th, const thread_key_t tk, void **data)
+{
+  struct _pthread_descr_struct pds;
+  struct pthread_key_struct *keys = th->th_ta_p->keys;
+  struct pthread_key_struct key;
+  int pthread_keys_max = th->th_ta_p->pthread_keys_max;
+  int pthread_key_2ndlevel_size = th->th_ta_p->pthread_key_2ndlevel_size;
+  unsigned int idx1st;
+  unsigned int idx2nd;
+  void *p;
+
+  LOG (__FUNCTION__);
+
+  /* Get the thread descriptor.  */
+  if (ps_pdread (th->th_ta_p->ph, th->th_unique, &pds,
+                sizeof (struct _pthread_descr_struct)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Check correct value of key.  */
+  if (tk >= pthread_keys_max)
+    return TD_BADKEY;
+
+  /* Get the key entry.  */
+  if (ps_pdread (th->th_ta_p->ph, keys, &key,
+                sizeof (struct pthread_key_struct)) != PS_OK)
+    return TD_ERR;     /* XXX Other error value?  */
+
+  /* Fail if this key is not at all used.  */
+  if (! key.in_use)
+    return TD_BADKEY;
+
+  /* Compute the indeces.  */
+  idx1st = tk / pthread_key_2ndlevel_size;
+  idx2nd = tk % pthread_key_2ndlevel_size;
+
+  /* Check the pointer to the second level array.  */
+  if (pds.p_specific[idx1st] == NULL)
+    return TD_NOTSD;
+
+  /* Now get the real key.
+     XXX I don't know whether it's correct but there is currently no
+     easy way to determine whether a key was never set or the value
+     is NULL.  We return an error whenever the value is NULL.  */
+  if (ps_pdread (th->th_ta_p->ph, &pds.p_specific[idx1st][idx2nd], &p,
+                sizeof (void *)) != PS_OK)
+    return TD_ERR;
+
+  if (p != NULL)
+    *data = p;
+
+  return p != NULL ? TD_OK : TD_NOTSD;
+}
diff --git a/libpthread/linuxthreads_db/td_thr_validate.c b/libpthread/linuxthreads_db/td_thr_validate.c
new file mode 100644 (file)
index 0000000..81c3b50
--- /dev/null
@@ -0,0 +1,51 @@
+/* Validate a thread handle.
+   Copyright (C) 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.
+
+   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 "thread_dbP.h"
+
+
+td_err_e
+td_thr_validate (const td_thrhandle_t *th)
+{
+  struct pthread_handle_struct *handles = th->th_ta_p->handles;
+  int pthread_threads_max = th->th_ta_p->pthread_threads_max;
+  int cnt;
+
+  LOG (__FUNCTION__);
+
+  /* Now get all descriptors, one after the other.  */
+  for (cnt = 0; cnt < pthread_threads_max; ++cnt, ++handles)
+    {
+      struct pthread_handle_struct phc;
+
+      if (ps_pdread (th->th_ta_p->ph, handles, &phc,
+                    sizeof (struct pthread_handle_struct)) != PS_OK)
+       return TD_ERR;  /* XXX Other error value?  */
+
+      if (phc.h_descr != NULL && phc.h_descr == th->th_unique)
+       {
+         /* XXX There should be another test using the TID but this is
+            currently not available.  */
+         return TD_OK;
+       }
+    }
+
+  return TD_ERR;
+}
diff --git a/libpthread/linuxthreads_db/thread_db.h b/libpthread/linuxthreads_db/thread_db.h
new file mode 100644 (file)
index 0000000..6301d7f
--- /dev/null
@@ -0,0 +1,436 @@
+/* Copyright (C) 1999 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 _THREAD_DB_H
+#define _THREAD_DB_H   1
+
+/* This is the debugger interface for the LinuxThreads library.  It is
+   modelled closely after the interface with same names in Solaris with
+   the goal to share the same code in the debugger.  */
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/procfs.h>
+
+
+/* Error codes of the library.  */
+typedef enum
+{
+  TD_OK,         /* No error.  */
+  TD_ERR,        /* No further specified error.  */
+  TD_NOTHR,      /* No matching thread found.  */
+  TD_NOSV,       /* No matching synchronization handle found.  */
+  TD_NOLWP,      /* No matching light-weighted process found.  */
+  TD_BADPH,      /* Invalid process handle.  */
+  TD_BADTH,      /* Invalid thread handle.  */
+  TD_BADSH,      /* Invalid synchronization handle.  */
+  TD_BADTA,      /* Invalid thread agent.  */
+  TD_BADKEY,     /* Invalid key.  */
+  TD_NOMSG,      /* No event available.  */
+  TD_NOFPREGS,   /* No floating-point register content available.  */
+  TD_NOLIBTHREAD, /* Application not linked with thread library.  */
+  TD_NOEVENT,    /* Requested event is not supported.  */
+  TD_NOCAPAB,    /* Capability not available.  */
+  TD_DBERR,      /* Internal debug library error.  */
+  TD_NOAPLIC,    /* Operation is not applicable.  */
+  TD_NOTSD,      /* No thread-specific data available.  */
+  TD_MALLOC,     /* Out of memory.  */
+  TD_PARTIALREG,  /* Not entire register set was read or written.  */
+  TD_NOXREGS     /* X register set not available for given thread.  */
+} td_err_e;
+
+
+/* Possible thread states.  TD_THR_ANY_STATE is a pseudo-state used to
+   select threads regardless of state in td_ta_thr_iter().  */
+typedef enum
+{
+  TD_THR_ANY_STATE,
+  TD_THR_UNKNOWN,
+  TD_THR_STOPPED,
+  TD_THR_RUN,
+  TD_THR_ACTIVE,
+  TD_THR_ZOMBIE,
+  TD_THR_SLEEP,
+  TD_THR_STOPPED_ASLEEP
+} td_thr_state_e;
+
+/* Thread type: user or system.  TD_THR_ANY_TYPE is a pseudo-type used
+   to select threads regardless of type in td_ta_thr_iter().  */
+typedef enum
+{
+  TD_THR_ANY_TYPE,
+  TD_THR_USER,
+  TD_THR_SYSTEM
+} td_thr_type_e;
+
+
+/* Types of the debugging library.  */
+
+/* Handle for a process.  This type is opaque.  */
+typedef struct td_thragent td_thragent_t;
+
+/* The actual thread handle type.  This is also opaque.  */
+typedef struct td_thrhandle
+{
+  td_thragent_t *th_ta_p;
+  psaddr_t th_unique;
+} td_thrhandle_t;
+
+
+/* Flags for `td_ta_thr_iter'.  */
+#define TD_THR_ANY_USER_FLAGS  0xffffffff
+#define TD_THR_LOWEST_PRIORITY -20
+#define TD_SIGNO_MASK          NULL
+
+
+#define TD_EVENTSIZE   2
+#define BT_UISHIFT     5 /* log base 2 of BT_NBIPUI, to extract word index */
+#define BT_NBIPUI      (1 << BT_UISHIFT)       /* n bits per uint */
+#define BT_UIMASK      (BT_NBIPUI - 1)         /* to extract bit index */
+
+/* Bitmask of enabled events. */
+typedef struct td_thr_events
+{
+  uint32_t event_bits[TD_EVENTSIZE];
+} td_thr_events_t;
+
+/* Event set manipulation macros. */
+#define __td_eventmask(n) \
+  (UINT32_C (1) << (((n) - 1) & BT_UIMASK))
+#define __td_eventword(n) \
+  ((UINT32_C ((n) - 1)) >> BT_UISHIFT)
+
+#define td_event_emptyset(setp) \
+  do {                                                                       \
+    int __i;                                                                 \
+    for (__i = TD_EVENTSIZE; __i > 0; --__i)                                 \
+      (setp)->event_bits[__i - 1] = 0;                                       \
+  } while (0)
+
+#define td_event_fillset(setp) \
+  do {                                                                       \
+    int __i;                                                                 \
+    for (__i = TD_EVENTSIZE; __i > 0; --__i)                                 \
+      (setp)->event_bits[__i - 1] = UINT32_C (0xffffffff);                   \
+  } while (0)
+
+#define td_event_addset(setp, n) \
+  (((setp)->event_bits[__td_eventword (n)]) |= __td_eventmask (n))
+#define td_event_delset(setp, n) \
+  (((setp)->event_bits[__td_eventword (n)]) &= ~__td_eventmask (n))
+#define td_eventismember(setp, n) \
+  (__td_eventmask (n) & ((setp)->event_bits[__td_eventword (n)]))
+#if TD_EVENTSIZE == 2
+# define td_eventisempty(setp) \
+  (!((setp)->event_bits[0]) && !((setp)->event_bits[1]))
+#else
+# error "td_eventisempty must be changed to match TD_EVENTSIZE"
+#endif
+
+/* Events reportable by the thread implementation.  */
+typedef enum
+{
+  TD_ALL_EVENTS,                /* Pseudo-event number.  */
+  TD_EVENT_NONE = TD_ALL_EVENTS, /* Depends on context.  */
+  TD_READY,                     /* Is executable now. */
+  TD_SLEEP,                     /* Blocked in a synchronization obj.  */
+  TD_SWITCHTO,                  /* Now assigned to a process.  */
+  TD_SWITCHFROM,                /* Not anymore assigned to a process.  */
+  TD_LOCK_TRY,                  /* Trying to get an unavailable lock.  */
+  TD_CATCHSIG,                  /* Signal posted to the thread.  */
+  TD_IDLE,                      /* Process getting idle.  */
+  TD_CREATE,                    /* New thread created.  */
+  TD_DEATH,                     /* Thread terminated.  */
+  TD_PREEMPT,                   /* Preempted.  */
+  TD_PRI_INHERIT,               /* Inherited elevated priority.  */
+  TD_REAP,                      /* Reaped.  */
+  TD_CONCURRENCY,               /* Number of processes changing.  */
+  TD_TIMEOUT,                   /* Conditional variable wait timed out.  */
+  TD_MIN_EVENT_NUM = TD_READY,
+  TD_MAX_EVENT_NUM = TD_TIMEOUT,
+  TD_EVENTS_ENABLE = 31                /* Event reporting enabled.  */
+} td_event_e;
+
+/* Values representing the different ways events are reported.  */
+typedef enum
+{
+  NOTIFY_BPT,                  /* User must insert breakpoint at u.bptaddr. */
+  NOTIFY_AUTOBPT,              /* Breakpoint at u.bptaddr is automatically
+                                  inserted.  */
+  NOTIFY_SYSCALL               /* System call u.syscallno will be invoked.  */
+} td_notify_e;
+
+/* Description how event type is reported.  */
+typedef struct td_notify
+{
+  td_notify_e type;            /* Way the event is reported.  */
+  union
+  {
+    psaddr_t bptaddr;          /* Address of breakpoint.  */
+    int syscallno;             /* Number of system call used.  */
+  } u;
+} td_notify_t;
+
+/* Structure used to report event.  */
+typedef struct td_event_msg
+{
+  td_event_e event;            /* Event type being reported.  */
+  const td_thrhandle_t *th_p;  /* Thread reporting the event.  */
+  union
+  {
+# if 0
+    td_synchandle_t *sh;       /* Handle of synchronization object.  */
+#endif
+    uintptr_t data;            /* Event specific data.  */
+  } msg;
+} td_event_msg_t;
+
+/* Structure containing event data available in each thread structure.  */
+typedef struct
+{
+  td_thr_events_t eventmask;   /* Mask of enabled events.  */
+  td_event_e eventnum;         /* Number of last event.  */
+  void *eventdata;             /* Data associated with event.  */
+} td_eventbuf_t;
+
+
+/* Gathered statistics about the process.  */
+typedef struct td_ta_stats
+{
+  int nthreads;                /* Total number of threads in use.  */
+  int r_concurrency;           /* Concurrency level requested by user.  */
+  int nrunnable_num;           /* Average runnable threads, numerator.  */
+  int nrunnable_den;           /* Average runnable threads, denominator.  */
+  int a_concurrency_num;       /* Achieved concurrency level, numerator.  */
+  int a_concurrency_den;       /* Achieved concurrency level, denominator.  */
+  int nlwps_num;               /* Average number of processes in use,
+                                  numerator.  */
+  int nlwps_den;               /* Average number of processes in use,
+                                  denominator.  */
+  int nidle_num;               /* Average number of idling processes,
+                                  numerator.  */
+  int nidle_den;               /* Average number of idling processes,
+                                  denominator.  */
+} td_ta_stats_t;
+
+
+/* Since Sun's library is based on Solaris threads we have to define a few
+   types to map them to POSIX threads.  */
+typedef pthread_t thread_t;
+typedef pthread_key_t thread_key_t;
+
+
+/* Callback for iteration over threads.  */
+typedef int td_thr_iter_f (const td_thrhandle_t *, void *);
+
+/* Callback for iteration over thread local data.  */
+typedef int td_key_iter_f (thread_key_t, void (*) (void *), void *);
+
+
+
+/* Forward declaration.  This has to be defined by the user.  */
+struct ps_prochandle;
+
+
+/* Information about the thread.  */
+typedef struct td_thrinfo
+{
+  td_thragent_t *ti_ta_p;              /* Process handle.  */
+  unsigned int ti_user_flags;          /* Unused.  */
+  thread_t ti_tid;                     /* Thread ID returned by
+                                          pthread_create().  */
+  char *ti_tls;                                /* Pointer to thread-local data.  */
+  psaddr_t ti_startfunc;               /* Start function passed to
+                                          pthread_create().  */
+  psaddr_t ti_stkbase;                 /* Base of thread's stack.  */
+  long int ti_stksize;                 /* Size of thread's stack.  */
+  psaddr_t ti_ro_area;                 /* Unused.  */
+  int ti_ro_size;                      /* Unused.  */
+  td_thr_state_e ti_state;             /* Thread state.  */
+  unsigned char ti_db_suspended;       /* Nonzero if suspended by debugger. */
+  td_thr_type_e ti_type;               /* Type of the thread (system vs
+                                          user thread).  */
+  intptr_t ti_pc;                      /* Unused.  */
+  intptr_t ti_sp;                      /* Unused.  */
+  short int ti_flags;                  /* Unused.  */
+  int ti_pri;                          /* Thread priority.  */
+  lwpid_t ti_lid;                      /* Unused.  */
+  sigset_t ti_sigmask;                 /* Signal mask.  */
+  unsigned char ti_traceme;            /* Nonzero if event reporting
+                                          enabled.  */
+  unsigned char ti_preemptflag;                /* Unused.  */
+  unsigned char ti_pirecflag;          /* Unused.  */
+  sigset_t ti_pending;                 /* Set of pending signals.  */
+  td_thr_events_t ti_events;           /* Set of enabled events.  */
+} td_thrinfo_t;
+
+
+
+/* Prototypes for exported library functions.  */
+
+/* Initialize the thread debug support library.  */
+extern td_err_e td_init (void);
+
+/* Historical relict.  Should not be used anymore.  */
+extern td_err_e td_log (void);
+
+/* Generate new thread debug library handle for process PS.  */
+extern td_err_e td_ta_new (struct ps_prochandle *__ps, td_thragent_t **__ta);
+
+/* Free resources allocated for TA.  */
+extern td_err_e td_ta_delete (td_thragent_t *__ta);
+
+/* Get number of currently running threads in process associated with TA.  */
+extern td_err_e td_ta_get_nthreads (const td_thragent_t *__ta, int *__np);
+
+/* Return process handle passed in `td_ta_new' for process associated with
+   TA.  */
+extern td_err_e td_ta_get_ph (const td_thragent_t *__ta,
+                             struct ps_prochandle **__ph);
+
+/* Map thread library handle PT to thread debug library handle for process
+   associated with TA and store result in *TH.  */
+extern td_err_e td_ta_map_id2thr (const td_thragent_t *__ta, pthread_t __pt,
+                                 td_thrhandle_t *__th);
+
+/* Map process ID LWPID to thread debug library handle for process
+   associated with TA and store result in *TH.  */
+extern td_err_e td_ta_map_lwp2thr (const td_thragent_t *__ta, lwpid_t __lwpid,
+                                  td_thrhandle_t *__th);
+
+
+/* Call for each thread in a process associated with TA the callback function
+   CALLBACK.  */
+extern td_err_e td_ta_thr_iter (const td_thragent_t *__ta,
+                               td_thr_iter_f *__callback, void *__cbdata_p,
+                               td_thr_state_e __state, int __ti_pri,
+                               sigset_t *__ti_sigmask_p,
+                               unsigned int __ti_user_flags);
+
+/* Call for each defined thread local data entry the callback function KI.  */
+extern td_err_e td_ta_tsd_iter (const td_thragent_t *__ta, td_key_iter_f *__ki,
+                               void *__p);
+
+
+/* Get event address for EVENT.  */
+extern td_err_e td_ta_event_addr (const td_thragent_t *__ta,
+                                 td_event_e __event, td_notify_t *__ptr);
+
+/* Enable EVENT in global mask.  */
+extern td_err_e td_ta_set_event (const td_thragent_t *__ta,
+                                td_thr_events_t *__event);
+
+/* Disable EVENT in global mask.  */
+extern td_err_e td_ta_clear_event (const td_thragent_t *__ta,
+                                  td_thr_events_t *__event);
+
+/* Return information about last event.  */
+extern td_err_e td_ta_event_getmsg (const td_thragent_t *__ta,
+                                   td_event_msg_t *msg);
+
+
+/* Set suggested concurrency level for process associated with TA.  */
+extern td_err_e td_ta_setconcurrency (const td_thragent_t *__ta, int __level);
+
+
+/* Enable collecting statistics for process associated with TA.  */
+extern td_err_e td_ta_enable_stats (const td_thragent_t *__ta, int __enable);
+
+/* Reset statistics.  */
+extern td_err_e td_ta_reset_stats (const td_thragent_t *__ta);
+
+/* Retrieve statistics from process associated with TA.  */
+extern td_err_e td_ta_get_stats (const td_thragent_t *__ta,
+                                td_ta_stats_t *__statsp);
+
+
+/* Validate that TH is a thread handle.  */
+extern td_err_e td_thr_validate (const td_thrhandle_t *__th);
+
+/* Return information about thread TH.  */
+extern td_err_e td_thr_get_info (const td_thrhandle_t *__th,
+                                td_thrinfo_t *__infop);
+
+/* Retrieve floating-point register contents of process running thread TH.  */
+extern td_err_e td_thr_getfpregs (const td_thrhandle_t *__th,
+                                 prfpregset_t *__regset);
+
+/* Retrieve general register contents of process running thread TH.  */
+extern td_err_e td_thr_getgregs (const td_thrhandle_t *__th,
+                                prgregset_t __gregs);
+
+/* Retrieve extended register contents of process running thread TH.  */
+extern td_err_e td_thr_getxregs (const td_thrhandle_t *__th, void *__xregs);
+
+/* Get size of extended register set of process running thread TH.  */
+extern td_err_e td_thr_getxregsize (const td_thrhandle_t *__th, int *__sizep);
+
+/* Set floating-point register contents of process running thread TH.  */
+extern td_err_e td_thr_setfpregs (const td_thrhandle_t *__th,
+                                 const prfpregset_t *__fpregs);
+
+/* Set general register contents of process running thread TH.  */
+extern td_err_e td_thr_setgregs (const td_thrhandle_t *__th,
+                                prgregset_t __gregs);
+
+/* Set extended register contents of process running thread TH.  */
+extern td_err_e td_thr_setxregs (const td_thrhandle_t *__th,
+                                const void *__addr);
+
+
+/* Enable reporting for EVENT for thread TH.  */
+extern td_err_e td_thr_event_enable (const td_thrhandle_t *__th, int __event);
+
+/* Enable EVENT for thread TH.  */
+extern td_err_e td_thr_set_event (const td_thrhandle_t *__th,
+                                 td_thr_events_t *__event);
+
+/* Disable EVENT for thread TH.  */
+extern td_err_e td_thr_clear_event (const td_thrhandle_t *__th,
+                                   td_thr_events_t *__event);
+
+/* Get event message for thread TH.  */
+extern td_err_e td_thr_event_getmsg (const td_thrhandle_t *__th,
+                                    td_event_msg_t *__msg);
+
+
+/* Set priority of thread TH.  */
+extern td_err_e td_thr_setprio (const td_thrhandle_t *__th, int __prio);
+
+
+/* Set pending signals for thread TH.  */
+extern td_err_e td_thr_setsigpending (const td_thrhandle_t *__th,
+                                     unsigned char __n, const sigset_t *__ss);
+
+/* Set signal mask for thread TH.  */
+extern td_err_e td_thr_sigsetmask (const td_thrhandle_t *__th,
+                                  const sigset_t *__ss);
+
+
+/* Return thread local data associated with key TK in thread TH.  */
+extern td_err_e td_thr_tsd (const td_thrhandle_t *__th,
+                           const thread_key_t __tk, void **__data);
+
+
+/* Suspend execution of thread TH.  */
+extern td_err_e td_thr_dbsuspend (const td_thrhandle_t *__th);
+
+/* Resume execution of thread TH.  */
+extern td_err_e td_thr_dbresume (const td_thrhandle_t *__th);
+
+#endif /* thread_db.h */
diff --git a/libpthread/linuxthreads_db/thread_dbP.h b/libpthread/linuxthreads_db/thread_dbP.h
new file mode 100644 (file)
index 0000000..13e534a
--- /dev/null
@@ -0,0 +1,83 @@
+/* Private header for thread debug library.  */
+#ifndef _THREAD_DBP_H
+#define _THREAD_DBP_H  1
+
+#include <string.h>
+#include "proc_service.h"
+#include "thread_db.h"
+#include "../linuxthreads/internals.h"
+
+
+/* Comment out the following for less verbose output.  */
+#ifndef NDEBUG
+# define LOG(c) if (__td_debug) __libc_write (2, c "\n", strlen (c "\n"))
+extern int __td_debug;
+#else
+# define LOG(c)
+#endif
+
+
+/* Handle for a process.  This type is opaque.  */
+struct td_thragent
+{
+  /* Delivered by the debugger and we have to pass it back in the
+     proc callbacks.  */
+  struct ps_prochandle *ph;
+
+  /* Some cached information.  */
+
+  /* Address of the `__pthread_handles' array.  */
+  struct pthread_handle_struct *handles;
+
+  /* Address of the `pthread_kyes' array.  */
+  struct pthread_key_struct *keys;
+
+  /* Maximum number of threads.  */
+  int pthread_threads_max;
+
+  /* Maximum number of thread-local data keys.  */
+  int pthread_keys_max;
+
+  /* Size of 2nd level array for thread-local data keys.  */
+  int pthread_key_2ndlevel_size;
+
+  /* Sizeof struct _pthread_descr_struct.  */
+  int sizeof_descr;
+
+  /* Pointer to the `__pthread_threads_events' variable in the target.  */
+  psaddr_t pthread_threads_eventsp;
+
+  /* Pointer to the `__pthread_last_event' variable in the target.  */
+  psaddr_t pthread_last_event;
+
+  /* Pointer to the `__pthread_handles_num' variable.  */
+  psaddr_t pthread_handles_num;
+};
+
+
+/* Type used internally to keep track of thread agent descriptors.  */
+struct agent_list
+{
+  td_thragent_t *ta;
+  struct agent_list *next;
+};
+
+/* List of all known descriptors.  */
+extern struct agent_list *__td_agent_list;
+
+/* Function used to test for correct thread agent pointer.  */
+static inline int
+ta_ok (const td_thragent_t *ta)
+{
+  struct agent_list *runp = __td_agent_list;
+
+  if (ta == NULL)
+    return 0;
+
+  while (runp != NULL && runp->ta != ta)
+    runp = runp->next;
+
+  return runp != NULL;
+}
+
+#endif /* thread_dbP.h */
index bc27aee..e91fe01 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * A simple clone based pthread implementation
  *
- * Copyright (C) 2001 by Erik Andersen <andersee@debian.org>
+ * Copyright (C) 2001,2002 by Erik Andersen <andersee@debian.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by