about summary refs log tree commit diff
path: root/linuxthreads
diff options
context:
space:
mode:
Diffstat (limited to 'linuxthreads')
-rw-r--r--linuxthreads/ChangeLog22
-rw-r--r--linuxthreads/README5
-rw-r--r--linuxthreads/attr.c90
-rw-r--r--linuxthreads/internals.h3
-rw-r--r--linuxthreads/libpthread.map9
-rw-r--r--linuxthreads/manager.c78
-rw-r--r--linuxthreads/pthread.c50
-rw-r--r--linuxthreads/sysdeps/pthread/pthread.h56
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h5
9 files changed, 292 insertions, 26 deletions
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index 696d15178d..eb866f0491 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,8 +1,24 @@
+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>
 
-	* linuxthreads/manager.c: Enable resetting of the thread
-	scheduling policy to SCHED_OTHER when the parent thread
-	has a different one.
+	* 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>
 
diff --git a/linuxthreads/README b/linuxthreads/README
index e824dd5b50..955bd59e7a 100644
--- a/linuxthreads/README
+++ b/linuxthreads/README
@@ -71,7 +71,7 @@ STATUS:
 - libc 6 (glibc 2) provides much better thread support than libc 5,
   and comes with a specially-adapted version of LinuxThreads.
   For serious multithreaded programming, you should consider switching
-  to glibc 2. It is available from prep.ai.mit.edu:/pub/gnu and its mirrors.
+  to glibc 2. It is available from ftp.gnu.org:/pub/gnu and its mirrors.
 
 
 WARNING:
@@ -107,6 +107,9 @@ KNOWN BUGS AND LIMITATIONS:
   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
diff --git a/linuxthreads/attr.c b/linuxthreads/attr.c
index 9622b5706b..76c137660e 100644
--- a/linuxthreads/attr.c
+++ b/linuxthreads/attr.c
@@ -15,10 +15,30 @@
 /* Handling of thread attributes */
 
 #include <unistd.h>
+#include <sys/param.h>
 #include "pthread.h"
 #include "internals.h"
 
-int pthread_attr_init(pthread_attr_t *attr)
+
+int __pthread_attr_init_2_1(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;
+}
+#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;
@@ -27,6 +47,10 @@ int pthread_attr_init(pthread_attr_t *attr)
   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
 
 int pthread_attr_destroy(pthread_attr_t *attr)
 {
@@ -115,3 +139,67 @@ 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 current implementation of LinuxThreads allocates 2MB stack space
+     for each thread.  So the maximum guardsize is 2MB - pagesize.  */
+  if (guardsize >= STACK_SIZE - ps)
+    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_etstackaddr)
+
+int __pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
+{
+  size_t ps = __getpagesize ();
+
+  /* We don't accept value smaller than PTHREAD_STACK_MIN or bigger than
+     2MB - pagesize.  */
+  if (stacksize < PTHREAD_STACK_MIN || stacksize > STACK_SIZE - ps)
+    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/linuxthreads/internals.h b/linuxthreads/internals.h
index 7939605cfe..0649e0d460 100644
--- a/linuxthreads/internals.h
+++ b/linuxthreads/internals.h
@@ -84,6 +84,9 @@ struct _pthread_descr_struct {
   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 thread */
+  void *p_guardaddr;		/* address of guard area or NULL */
+  size_t p_guardsize;		/* size of guard area */
 };
 
 /* The type of thread handles. */
diff --git a/linuxthreads/libpthread.map b/linuxthreads/libpthread.map
index 36767af3ed..403cea859f 100644
--- a/linuxthreads/libpthread.map
+++ b/linuxthreads/libpthread.map
@@ -53,10 +53,19 @@ GLIBC_2.0 {
 
 GLIBC_2.1 {
   global:
+    # 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;
 } GLIBC_2.0;
diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c
index 325955db4a..a650bbe4dd 100644
--- a/linuxthreads/manager.c
+++ b/linuxthreads/manager.c
@@ -172,27 +172,54 @@ static int pthread_start_thread(void *arg)
 }
 
 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)
+				 void * (*start_routine)(void *), void *arg,
+				 sigset_t * mask, int father_pid)
 {
   size_t sseg;
   int pid;
   pthread_descr new_thread;
   pthread_t new_thread_id;
   int i;
+  void *guardaddr = NULL;
 
   /* Find a free stack segment for the current stack */
-  for (sseg = 1; ; sseg++) {
-    if (sseg >= PTHREAD_THREADS_MAX) return EAGAIN;
-    if (__pthread_handles[sseg].h_descr != NULL) continue;
-    new_thread = thread_segment(sseg);
-    /* Allocate space for stack and thread descriptor. */
-    if (mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
-	     INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
-	     MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0)
-        != MAP_FAILED) break;
-    /* It seems part of this segment is already mapped. Try the next. */
-  }
+  for (sseg = 1; ; sseg++)
+    {
+      if (sseg >= PTHREAD_THREADS_MAX)
+	return EAGAIN;
+      if (__pthread_handles[sseg].h_descr != NULL)
+	continue;
+
+      if (attr == NULL || !attr->stackaddr_set)
+	{
+	  new_thread = thread_segment(sseg);
+	  /* Allocate space for stack and thread descriptor. */
+	  if (mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
+		   INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
+		   MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN,
+		   -1, 0) != MAP_FAILED)
+	    {
+	      /* We manage to get a stack.  Now see whether we need a guard
+		 and allocate it if necessary.  */
+	      if (attr->guardsize != 0)
+		{
+		  guardaddr = mmap ((caddr_t)((char *)(new_thread+1)
+					      - 2*1024*1024),
+				    attr->guardsize, 0, MAP_FIXED, -1, 0);
+		  if (guardaddr == MAP_FAILED)
+		    /* We don't make this an error.  */
+		    guardaddr = NULL;
+		}
+	      break;
+	    }
+	  /* It seems part of this segment is already mapped. Try the next. */
+	}
+      else
+	{
+	  new_thread = (pthread_descr) attr->stackaddr - 1;
+	  break;
+	}
+    }
   /* Allocate new thread identifier */
   pthread_threads_counter += PTHREAD_THREADS_MAX;
   new_thread_id = sseg + pthread_threads_counter;
@@ -217,6 +244,10 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
   new_thread->p_errno = 0;
   new_thread->p_h_errnop = &new_thread->p_h_errno;
   new_thread->p_h_errno = 0;
+  new_thread->p_guardaddr = guardaddr;
+  new_thread->p_guardsize = (attr == NULL || !attr->stackaddr_set
+			     ? attr->guardsize : 0);
+  new_thread->p_userstack = attr != NULL && attr->stackaddr_set;
   for (i = 0; i < PTHREAD_KEY_1STLEVEL_SIZE; i++)
     new_thread->p_specific[i] = NULL;
   /* Initialize the thread handle */
@@ -249,9 +280,14 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
                 new_thread);
   /* Check if cloning succeeded */
   if (pid == -1) {
-    /* Free the stack */
-    munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
-           INITIAL_STACK_SIZE);
+    /* Free the stack if we allocated it */
+    if (attr == NULL || !attr->stackaddr_set)
+      {
+	munmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),
+	       INITIAL_STACK_SIZE);
+	if (attr->guardsize != 0)
+	  munmap(new_thread->p_guardaddr, new_thread->p_guardsize);
+      }
     __pthread_handles[sseg].h_descr = NULL;
     return errno;
   }
@@ -268,6 +304,7 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
   return 0;
 }
 
+
 /* Free the resources of a thread. */
 
 static void pthread_free(pthread_descr th)
@@ -281,8 +318,13 @@ static void pthread_free(pthread_descr th)
   release(&handle->h_spinlock);
   /* If initial thread, nothing to free */
   if (th == &__pthread_initial_thread) return;
-  /* Free the stack and thread descriptor area */
-  munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE);
+  if (!th->p_userstack)
+    {
+      /* Free the stack and thread descriptor area */
+      munmap((caddr_t) ((char *)(th+1) - STACK_SIZE), STACK_SIZE);
+      if (th->p_guardsize != 0)
+	munmap(th->p_guardaddr, th->p_guardsize);
+    }
 }
 
 /* Handle threads that have exited */
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
index 994233ebc7..729222e589 100644
--- a/linuxthreads/pthread.c
+++ b/linuxthreads/pthread.c
@@ -234,8 +234,8 @@ static int pthread_initialize_manager(void)
 
 /* Thread creation */
 
-int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
-                   void * (*start_routine)(void *), void *arg)
+int __pthread_create_2_1(pthread_t *thread, const pthread_attr_t *attr,
+			 void * (*start_routine)(void *), void *arg)
 {
   pthread_descr self = thread_self();
   struct pthread_request request;
@@ -255,6 +255,35 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
   return self->p_retcode;
 }
 
+#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING
+default_symbol_version (__pthread_create_2_1, pthread_create, GLIBC_2.1);
+
+int __pthread_create_2_0(pthread_t *thread, const pthread_attr_t *attr,
+			 void * (*start_routine)(void *), void *arg)
+{
+  /* The ATTR attribute is not really of type `pthread_attr_t *'.  It has
+     the old size and access to the new members might crash the program.
+     We convert the struct now.  */
+  pthread_attr_t new_attr;
+
+  if (attr != NULL)
+    {
+      size_t ps = __getpagesize ();
+
+      memcpy (&new_attr, attr, (size_t) &(((pthread_attr_t*)NULL)->guardsize));
+      new_attr.guardsize = ps;
+      new_attr.stackaddr_set = 0;
+      new_attr.stackaddr = NULL;
+      new_attr.stacksize = STACK_SIZE - ps;
+      attr = &new_attr;
+    }
+  return __pthread_create_2_1 (thread, attr, start_routine, arg);
+}
+symbol_version (__pthread_create_2_0, pthread_create, GLIBC_2.0);
+#else
+strong_alias (__pthread_create_2_1, pthread_create)
+#endif
+
 /* Simple operations on thread identifiers */
 
 pthread_t pthread_self(void)
@@ -417,6 +446,23 @@ void __pthread_kill_other_threads_np(void)
 }
 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)
+
 /* Debugging aid */
 
 #ifdef DEBUG
diff --git a/linuxthreads/sysdeps/pthread/pthread.h b/linuxthreads/sysdeps/pthread/pthread.h
index b62706a811..9f5e9deef2 100644
--- a/linuxthreads/sysdeps/pthread/pthread.h
+++ b/linuxthreads/sysdeps/pthread/pthread.h
@@ -119,6 +119,10 @@ typedef struct
   struct sched_param schedparam;
   int inheritsched;
   int scope;
+  size_t guardsize;
+  int stackaddr_set;
+  void *stackaddr;
+  size_t stacksize;
 } pthread_attr_t;
 
 enum
@@ -272,6 +276,49 @@ extern int pthread_attr_setscope __P ((pthread_attr_t *__attr, int __scope));
 extern int pthread_attr_getscope __P ((__const pthread_attr_t *__attr,
 				       int *__scope));
 
+#ifdef __USE_UNIX98
+/* Set the size of the guard area at the bottom of the thread.  */
+extern int __pthread_attr_setguardsize __P ((pthread_attr_t *__attr,
+					     size_t __guardsize));
+extern int pthread_attr_setguardsize __P ((pthread_attr_t *__attr,
+					   size_t __guardsize));
+
+/* Get the size of the guard area at the bottom of the thread.  */
+extern int __pthread_attr_getguardsize __P ((__const pthread_attr_t *__attr,
+					     size_t *__guardsize));
+extern int pthread_attr_getguardsize __P ((__const pthread_attr_t *__attr,
+					   size_t *__guardsize));
+
+/* Set the starting address of the stack of the thread to be created.
+   Depending on whether the stack grows up or doen the value must either
+   be higher or lower than all the address in the memory block.  The
+   minimal size of the block must be PTHREAD_STACK_SIZE.  */
+extern int __pthread_attr_setstackaddr __P ((pthread_attr_t *__attr,
+					     void *__stackaddr));
+extern int pthread_attr_setstackaddr __P ((pthread_attr_t *__attr,
+					   void *__stackaddr));
+
+/* Return the previously set address for the stack.  */
+extern int __pthread_attr_getstackaddr __P ((__const pthread_attr_t *__attr,
+					     void **__stackaddr));
+extern int pthread_attr_getstackaddr __P ((__const pthread_attr_t *__attr,
+					   void **__stackaddr));
+
+/* Add information about the minimum stack size needed for the thread
+   to be started.  This size must never be less than PTHREAD_STACK_SIZE
+   and must also not exceed the system limits.  */
+extern int __pthread_attr_setstacksize __P ((pthread_attr_t *__attr,
+					     size_t __stacksize));
+extern int pthread_attr_setstacksize __P ((pthread_attr_t *__attr,
+					   size_t __stacksize));
+
+/* Return the currently used minimal stack size.  */
+extern int __pthread_attr_getstacksize __P ((__const pthread_attr_t *__attr,
+					     size_t *__stacksize));
+extern int pthread_attr_getstacksize __P ((__const pthread_attr_t *__attr,
+					   size_t *__stacksize));
+#endif
+
 /* Functions for scheduling control. */
 
 /* Set the scheduling parameters for TARGET_THREAD according to POLICY
@@ -284,6 +331,15 @@ extern int pthread_getschedparam __P ((pthread_t __target_thread,
 				       int *__policy,
 				       struct sched_param *__param));
 
+#ifdef __USE_UNIX98
+/* Determine  level of concurrency.  */
+extern int __pthread_getconcurrency __P ((void));
+extern int pthread_getconcurrency __P ((void));
+
+/* Set new concurrency level to LEVEL.  */
+extern int __pthread_setconcurrency __P ((int __level));
+extern int pthread_setconcurrency __P ((int __level));
+#endif
 
 /* Functions for mutex handling. */
 
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h b/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h
index 68635ba36f..f884863ffa 100644
--- a/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h
+++ b/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h
@@ -1,5 +1,5 @@
 /* Minimum guaranteed maximum values for system limits.  Linux version.
-   Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+   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
@@ -38,3 +38,6 @@
 /* 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