summaryrefslogtreecommitdiff
path: root/libpthread/linuxthreads/manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpthread/linuxthreads/manager.c')
-rw-r--r--libpthread/linuxthreads/manager.c829
1 files changed, 320 insertions, 509 deletions
diff --git a/libpthread/linuxthreads/manager.c b/libpthread/linuxthreads/manager.c
index 3c5bee876..e4022f8ea 100644
--- a/libpthread/linuxthreads/manager.c
+++ b/libpthread/linuxthreads/manager.c
@@ -14,7 +14,7 @@
/* The "thread manager" thread: manages creation and termination of threads */
-#include <assert.h>
+#include <features.h>
#include <errno.h>
#include <sched.h>
#include <stddef.h>
@@ -27,72 +27,77 @@
#include <sys/param.h>
#include <sys/time.h>
#include <sys/wait.h> /* for waitpid macros */
-#include <locale.h> /* for __uselocale */
-#include <resolv.h> /* for __resp */
#include "pthread.h"
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
#include "semaphore.h"
-#include <not-cancel.h>
+#include "debug.h" /* PDEBUG, added by StS */
+
+#ifndef THREAD_STACK_OFFSET
+#define THREAD_STACK_OFFSET 0
+#endif
+
+/* poll() is not supported in kernel <= 2.0, therefore is __NR_poll is
+ * not available, we assume an old Linux kernel is in use and we will
+ * use select() instead. */
+#include <sys/syscall.h>
+#ifndef __NR_poll
+# define USE_SELECT
+#endif
+
+libpthread_hidden_proto(waitpid)
+libpthread_hidden_proto(raise)
+
+/* 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;
-#ifndef THREAD_SELF
/* 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;
-#endif
/* Number of active entries in __pthread_handles (used by gdb) */
-__volatile__ int __pthread_handles_num = 2;
+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;
+volatile int __pthread_threads_debug;
/* Globally enabled events. */
-__volatile__ td_thr_events_t __pthread_threads_events;
+volatile td_thr_events_t __pthread_threads_events;
/* Pointer to thread descriptor with last event. */
-__volatile__ pthread_descr __pthread_last_event;
-
-static pthread_descr manager_thread;
+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. */
-#if FLOATING_STACKS
-# define thread_segment(seq) NULL
-#else
static __inline__ pthread_descr thread_segment(int seg)
{
-# ifdef _STACK_GROWS_UP
- return (pthread_descr)(THREAD_STACK_START_ADDRESS + (seg - 1) * STACK_SIZE)
- + 1;
-# else
return (pthread_descr)(THREAD_STACK_START_ADDRESS - (seg - 1) * STACK_SIZE)
- 1;
-# endif
}
-#endif
/* Flag set in signal handler to record child termination */
-static __volatile__ int terminated_children;
+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;
+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;
+static pthread_t pthread_threads_counter = 0;
/* Forward declarations */
@@ -102,54 +107,67 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
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)
- __attribute__ ((noreturn));
+static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode) attribute_noreturn;
static void pthread_reap_children(void);
static void pthread_kill_all_threads(int sig, int main_thread_also);
-static void pthread_for_each_thread(void *arg,
- void (*fn)(void *, pthread_descr));
/* The server thread managing requests for thread creation and termination */
-int
-__attribute__ ((noreturn))
-__pthread_manager(void *arg)
+int attribute_noreturn __pthread_manager(void *arg)
{
- pthread_descr self = manager_thread = arg;
- int reqfd = __pthread_manager_reader;
+ int reqfd = (int) (long int) arg;
+#ifdef USE_SELECT
+ struct timeval tv;
+ fd_set fd;
+#else
struct pollfd ufd;
+#endif
sigset_t manager_mask;
int n;
struct pthread_request request;
/* If we have special thread_self processing, initialize it. */
#ifdef INIT_THREAD_SELF
- INIT_THREAD_SELF(self, 1);
+ INIT_THREAD_SELF(&__pthread_manager_thread, 1);
#endif
-#ifndef __UCLIBC_HAS_TLS__
/* Set the error variable. */
- self->p_errnop = &self->p_errno;
- self->p_h_errnop = &self->p_h_errno;
-#endif
+ __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno;
+ __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno;
+
+#ifdef __UCLIBC_HAS_XLOCALE__
+ /* Initialize thread's locale to the global locale. */
+ __pthread_manager_thread.locale = __global_locale;
+#endif /* __UCLIBC_HAS_XLOCALE__ */
+
/* Block all signals except __pthread_sig_cancel and SIGTRAP */
__sigfillset(&manager_mask);
sigdelset(&manager_mask, __pthread_sig_cancel); /* for thread termination */
sigdelset(&manager_mask, SIGTRAP); /* for debugging purposes */
if (__pthread_threads_debug && __pthread_sig_debug > 0)
- sigdelset(&manager_mask, __pthread_sig_debug);
+ sigdelset(&manager_mask, __pthread_sig_debug);
sigprocmask(SIG_SETMASK, &manager_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 = TEMP_FAILURE_RETRY(read_not_cancel(reqfd, (char *)&request,
- sizeof(request)));
- ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG);
+ n = TEMP_FAILURE_RETRY(read(reqfd, (char *)&request,
+ sizeof(request)));
+#ifndef USE_SELECT
ufd.fd = reqfd;
ufd.events = POLLIN;
+#endif
/* Enter server loop */
while(1) {
- n = __poll(&ufd, 1, 2000);
-
+#ifdef USE_SELECT
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ FD_ZERO (&fd);
+ FD_SET (reqfd, &fd);
+ n = select (reqfd + 1, &fd, NULL, NULL, &tv);
+#else
+ PDEBUG("before poll\n");
+ n = poll(&ufd, 1, 2000);
+ PDEBUG("after poll\n");
+#endif
/* Check for termination of the main thread */
if (getppid() == 1) {
pthread_kill_all_threads(SIGKILL, 0);
@@ -161,20 +179,19 @@ __pthread_manager(void *arg)
pthread_reap_children();
}
/* Read and execute request */
- if (n == 1 && (ufd.revents & POLLIN)) {
- n = TEMP_FAILURE_RETRY(read_not_cancel(reqfd, (char *)&request,
- sizeof(request)));
-#ifdef DEBUG
- if (n < 0) {
- char d[64];
- write(STDERR_FILENO, d, snprintf(d, sizeof(d), "*** read err %m\n"));
- } else if (n != sizeof(request)) {
- write(STDERR_FILENO, "*** short read in manager\n", 26);
- }
+#ifdef USE_SELECT
+ if (n == 1)
+#else
+ if (n == 1 && (ufd.revents & POLLIN))
#endif
+ {
+ PDEBUG("before read\n");
+ n = read(reqfd, (char *)&request, sizeof(request));
+ PDEBUG("after read, n=%d\n", n);
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,
@@ -182,19 +199,23 @@ __pthread_manager(void *arg)
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);
+ request.req_thread->p_report_events,
+ &request.req_thread->p_eventbuf.eventmask);
+ PDEBUG("restarting %p\n", request.req_thread);
restart(request.req_thread);
break;
case REQ_FREE:
- pthread_handle_free(request.req_args.free.thread_id);
+ 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 %p, exit code = %d\n",
+ request.req_thread, request.req_args.exit.code);
pthread_handle_exit(request.req_thread,
request.req_args.exit.code);
- /* NOTREACHED */
break;
case REQ_MAIN_THREAD_EXIT:
+ PDEBUG("got REQ_MAIN_THREAD_EXIT\n");
main_thread_exiting = 1;
/* Reap children in case all other threads died and the signal handler
went off before we set main_thread_exiting to 1, and therefore did
@@ -210,99 +231,81 @@ __pthread_manager(void *arg)
}
break;
case REQ_POST:
+ PDEBUG("got REQ_POST\n");
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)
+ if (__pthread_threads_debug && __pthread_sig_debug > 0) {
+ PDEBUG("about to call raise(__pthread_sig_debug)\n");
raise(__pthread_sig_debug);
- break;
+ }
case REQ_KICK:
/* This is just a prod to get the manager to reap some
threads right away, avoiding a potential delay at shutdown. */
break;
- case REQ_FOR_EACH_THREAD:
- pthread_for_each_thread(request.req_args.for_each.arg,
- request.req_args.for_each.fn);
- restart(request.req_thread);
- break;
}
}
}
}
-int __pthread_manager_event(void *arg)
+int attribute_noreturn __pthread_manager_event(void *arg)
{
- pthread_descr self = arg;
/* If we have special thread_self processing, initialize it. */
#ifdef INIT_THREAD_SELF
- INIT_THREAD_SELF(self, 1);
+ 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(self, p_lock), NULL);
+ __pthread_lock (THREAD_GETMEM((&__pthread_manager_thread), p_lock), NULL);
/* Free it immediately. */
- __pthread_unlock (THREAD_GETMEM(self, p_lock));
+ __pthread_unlock (THREAD_GETMEM((&__pthread_manager_thread), p_lock));
- return __pthread_manager(arg);
+ __pthread_manager(arg);
}
/* Process creation */
-
static int
-__attribute__ ((noreturn))
+attribute_noreturn
pthread_start_thread(void *arg)
{
pthread_descr self = (pthread_descr) arg;
struct pthread_request request;
void * outcome;
-#if HP_TIMING_AVAIL
- hp_timing_t tmpclock;
-#endif
/* Initialize special thread_self processing, if any. */
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(self, self->p_nr);
#endif
-#if HP_TIMING_AVAIL
- HP_TIMING_NOW (tmpclock);
- THREAD_SETMEM (self, p_cpuclock_offset, tmpclock);
-#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());
+ 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),
+ sched_setscheduler(THREAD_GETMEM(self, p_pid),
THREAD_GETMEM(self, p_start_args.schedpolicy),
&self->p_start_args.schedparam);
- else if (manager_thread->p_priority > 0)
+ 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_setscheduler(THREAD_GETMEM(self, p_pid),
SCHED_OTHER, &default_params);
}
-#ifndef __UCLIBC_HAS_TLS__
- /* Initialize thread-locale current locale to point to the global one.
- With __thread support, the variable's initializer takes care of this. */
- __uselocale (LC_GLOBAL_LOCALE);
-#elif defined __UCLIBC_HAS_RESOLVER_SUPPORT__
- /* Initialize __resp. */
- __resp = &self->p_res;
-#endif
/* Make gdb aware of new thread */
if (__pthread_threads_debug && __pthread_sig_debug > 0) {
request.req_thread = self;
request.req_kind = REQ_DEBUG;
- TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
- (char *) &request, sizeof(request)));
+ TEMP_FAILURE_RETRY(write(__pthread_manager_request,
+ (char *) &request, sizeof(request)));
suspend(self);
}
/* Run the thread code */
@@ -313,7 +316,7 @@ pthread_start_thread(void *arg)
}
static int
-__attribute__ ((noreturn))
+attribute_noreturn
pthread_start_thread_event(void *arg)
{
pthread_descr self = (pthread_descr) arg;
@@ -323,7 +326,7 @@ pthread_start_thread_event(void *arg)
#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());
+ 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. */
@@ -333,250 +336,136 @@ pthread_start_thread_event(void *arg)
pthread_start_thread (arg);
}
-#if defined __UCLIBC_HAS_TLS__ && !FLOATING_STACKS
-# error "TLS can only work with floating stacks"
-#endif
-
static int pthread_allocate_stack(const pthread_attr_t *attr,
pthread_descr default_new_thread,
int pagesize,
- char ** out_new_thread,
+ pthread_descr * out_new_thread,
char ** out_new_thread_bottom,
char ** out_guardaddr,
- size_t * out_guardsize,
- size_t * out_stacksize)
+ size_t * out_guardsize)
{
pthread_descr new_thread;
char * new_thread_bottom;
char * guardaddr;
size_t stacksize, guardsize;
-#ifdef __UCLIBC_HAS_TLS__
- /* TLS cannot work with fixed thread descriptor addresses. */
- assert (default_new_thread == NULL);
-#endif
-
if (attr != NULL && attr->__stackaddr_set)
{
-#ifdef _STACK_GROWS_UP
/* The user provided a stack. */
-# ifdef __UCLIBC_HAS_TLS__
- /* This value is not needed. */
- new_thread = (pthread_descr) attr->__stackaddr;
- new_thread_bottom = (char *) new_thread;
-# else
- new_thread = (pthread_descr) attr->__stackaddr;
- new_thread_bottom = (char *) (new_thread + 1);
-# endif
- guardaddr = attr->__stackaddr + attr->__stacksize;
- guardsize = 0;
-#else
- /* The user provided a stack. For now we interpret the supplied
- address as 1 + the highest addr. in the stack segment. If a
- separate register stack is needed, we place it at the low end
- of the segment, relying on the associated stacksize to
- determine the low end of the segment. This differs from many
- (but not all) other pthreads implementations. The intent is
- that on machines with a single stack growing toward higher
- addresses, stackaddr would be the lowest address in the stack
- segment, so that it is consistently close to the initial sp
- value. */
-# ifdef __UCLIBC_HAS_TLS__
- new_thread = (pthread_descr) attr->__stackaddr;
-# else
- new_thread =
- (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1;
-# endif
+ new_thread = (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1;
new_thread_bottom = (char *) attr->__stackaddr - attr->__stacksize;
- guardaddr = new_thread_bottom;
+ guardaddr = NULL;
guardsize = 0;
-#endif
-#ifndef THREAD_SELF
__pthread_nonstandard_stacks = 1;
+#ifndef __ARCH_USE_MMU__
+ /* check the initial thread stack boundaries so they don't overlap */
+ NOMMU_INITIAL_THREAD_BOUNDS((char *) new_thread, (char *) new_thread_bottom);
+
+ PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos,
+ __pthread_initial_thread_tos);
#endif
-#ifndef __UCLIBC_HAS_TLS__
- /* Clear the thread data structure. */
- memset (new_thread, '\0', sizeof (*new_thread));
-#endif
- stacksize = attr->__stacksize;
}
else
{
-#ifdef NEED_SEPARATE_REGISTER_STACK
- const size_t granularity = 2 * pagesize;
- /* Try to make stacksize/2 a multiple of pagesize */
+#ifdef __ARCH_USE_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 == (size_t) (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
- const size_t granularity = pagesize;
-#endif
- void *map_addr;
+ /* 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 */
- /* Allocate space for stack and thread descriptor at default address */
-#if FLOATING_STACKS
+ stacksize = INITIAL_STACK_SIZE;
+ /* The user may want to use a non-default stacksize */
if (attr != NULL)
{
- guardsize = page_roundup (attr->__guardsize, granularity);
- stacksize = __pthread_max_stacksize - guardsize;
- stacksize = MIN (stacksize,
- page_roundup (attr->__stacksize, granularity));
+ stacksize = attr->__stacksize;
}
- else
+
+ /* malloc a stack - memory from the bottom up */
+ if ((new_thread_bottom = malloc(stacksize)) == NULL)
{
- guardsize = granularity;
- stacksize = __pthread_max_stacksize - guardsize;
+ /* bad luck, we cannot malloc any more */
+ return -1 ;
}
+ PDEBUG("malloced chunk: base=%p, size=0x%04x\n", new_thread_bottom, stacksize);
+
+ /* 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 = ((pthread_descr) ((int)(new_thread_bottom + stacksize) & -sizeof(void*))) - 1;
+ guardaddr = NULL;
+ guardsize = 0;
- map_addr = mmap(NULL, stacksize + guardsize,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (map_addr == MAP_FAILED)
- /* No more memory available. */
- return -1;
+ PDEBUG("thread stack: bos=%p, tos=%p\n", new_thread_bottom, new_thread);
-# ifdef NEED_SEPARATE_REGISTER_STACK
- guardaddr = map_addr + stacksize / 2;
- if (guardsize > 0)
- mprotect (guardaddr, guardsize, PROT_NONE);
-
- new_thread_bottom = (char *) map_addr;
-# ifdef __UCLIBC_HAS_TLS__
- new_thread = ((pthread_descr) (new_thread_bottom + stacksize
- + guardsize));
-# else
- new_thread = ((pthread_descr) (new_thread_bottom + stacksize
- + guardsize)) - 1;
-# endif
-# elif defined _STACK_GROWS_DOWN
- guardaddr = map_addr;
- if (guardsize > 0)
- mprotect (guardaddr, guardsize, PROT_NONE);
-
- new_thread_bottom = (char *) map_addr + guardsize;
-# ifdef __UCLIBC_HAS_TLS__
- new_thread = ((pthread_descr) (new_thread_bottom + stacksize));
-# else
- new_thread = ((pthread_descr) (new_thread_bottom + stacksize)) - 1;
-# endif
-# elif defined _STACK_GROWS_UP
- guardaddr = map_addr + stacksize;
- if (guardsize > 0)
- mprotect (guardaddr, guardsize, PROT_NONE);
-
- new_thread = (pthread_descr) map_addr;
-# ifdef __UCLIBC_HAS_TLS__
- new_thread_bottom = (char *) new_thread;
-# else
- new_thread_bottom = (char *) (new_thread + 1);
-# endif
-# else
-# error You must define a stack direction
-# endif /* Stack direction */
-#else /* !FLOATING_STACKS */
-# if !defined NEED_SEPARATE_REGISTER_STACK && defined _STACK_GROWS_DOWN
- void *res_addr;
-# endif
+ /* check the initial thread stack boundaries so they don't overlap */
+ NOMMU_INITIAL_THREAD_BOUNDS((char *) new_thread, (char *) new_thread_bottom);
- if (attr != NULL)
- {
- guardsize = page_roundup (attr->__guardsize, granularity);
- stacksize = STACK_SIZE - guardsize;
- stacksize = MIN (stacksize,
- page_roundup (attr->__stacksize, granularity));
- }
- else
- {
- guardsize = granularity;
- stacksize = STACK_SIZE - granularity;
- }
+ PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos,
+ __pthread_initial_thread_tos);
-# ifdef NEED_SEPARATE_REGISTER_STACK
- new_thread = default_new_thread;
- new_thread_bottom = (char *) (new_thread + 1) - stacksize - guardsize;
- /* Includes guard area, unlike the normal case. Use the bottom
- end of the segment as backing store for the register stack.
- Needed on IA64. In this case, we also map the entire stack at
- once. According to David Mosberger, that's cheaper. It also
- avoids the risk of intermittent failures due to other mappings
- in the same region. The cost is that we might be able to map
- slightly fewer stacks. */
-
- /* First the main stack: */
- map_addr = (caddr_t)((char *)(new_thread + 1) - stacksize / 2);
- res_addr = mmap(map_addr, stacksize / 2,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (res_addr != map_addr)
- {
- /* Bad luck, this segment is already mapped. */
- if (res_addr != MAP_FAILED)
- munmap(res_addr, stacksize / 2);
- return -1;
- }
- /* Then the register stack: */
- map_addr = (caddr_t)new_thread_bottom;
- res_addr = mmap(map_addr, stacksize/2,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (res_addr != map_addr)
- {
- if (res_addr != MAP_FAILED)
- munmap(res_addr, stacksize / 2);
- munmap((caddr_t)((char *)(new_thread + 1) - stacksize/2),
- stacksize/2);
- return -1;
- }
-
- guardaddr = new_thread_bottom + stacksize/2;
- /* We leave the guard area in the middle unmapped. */
-# else /* !NEED_SEPARATE_REGISTER_STACK */
-# ifdef _STACK_GROWS_DOWN
- new_thread = default_new_thread;
- new_thread_bottom = (char *) (new_thread + 1) - stacksize;
- map_addr = new_thread_bottom - guardsize;
- res_addr = mmap(map_addr, stacksize + guardsize,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (res_addr != map_addr)
- {
- /* Bad luck, this segment is already mapped. */
- if (res_addr != MAP_FAILED)
- munmap (res_addr, stacksize + guardsize);
- return -1;
- }
+ /* on non-MMU systems we always have non-standard stack frames */
+ __pthread_nonstandard_stacks = 1;
- /* We manage to get a stack. Protect the guard area pages if
- necessary. */
- guardaddr = map_addr;
- if (guardsize > 0)
- mprotect (guardaddr, guardsize, PROT_NONE);
-# else
- /* The thread description goes at the bottom of this area, and
- * the stack starts directly above it.
- */
- new_thread = (pthread_descr)((unsigned long)default_new_thread &~ (STACK_SIZE - 1));
- map_addr = mmap(new_thread, stacksize + guardsize,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (map_addr == MAP_FAILED)
- return -1;
-
- new_thread_bottom = map_addr + sizeof(*new_thread);
- guardaddr = map_addr + stacksize;
- if (guardsize > 0)
- mprotect (guardaddr, guardsize, PROT_NONE);
-
-# endif /* stack direction */
-# endif /* !NEED_SEPARATE_REGISTER_STACK */
-#endif /* !FLOATING_STACKS */
+#endif /* __ARCH_USE_MMU__ */
}
- *out_new_thread = (char *) new_thread;
+
+ /* 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;
-#ifdef NEED_SEPARATE_REGISTER_STACK
- *out_stacksize = stacksize / 2;
-#else
- *out_stacksize = stacksize;
-#endif
return 0;
}
@@ -589,30 +478,17 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
size_t sseg;
int pid;
pthread_descr new_thread;
- char *stack_addr;
char * new_thread_bottom;
+ char * new_thread_top;
pthread_t new_thread_id;
char *guardaddr = NULL;
- size_t guardsize = 0, stksize = 0;
- int pagesize = __getpagesize();
+ size_t guardsize = 0;
+ int pagesize = getpagesize();
int saved_errno = 0;
-#ifdef __UCLIBC_HAS_TLS__
- new_thread = _dl_allocate_tls (NULL);
- if (new_thread == NULL)
- return EAGAIN;
-# if defined(TLS_DTV_AT_TP)
- /* pthread_descr is below TP. */
- new_thread = (pthread_descr) ((char *) new_thread - TLS_PRE_TCB_SIZE);
-# endif
-#else
- /* Prevent warnings. */
- new_thread = NULL;
-#endif
-
/* 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
+ 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;
@@ -620,36 +496,21 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
for (sseg = 2; ; sseg++)
{
if (sseg >= PTHREAD_THREADS_MAX)
- {
-#ifdef __UCLIBC_HAS_TLS__
-# if defined(TLS_DTV_AT_TP)
- new_thread = (pthread_descr) ((char *) new_thread + TLS_PRE_TCB_SIZE);
-# endif
- _dl_deallocate_tls (new_thread, true);
-#endif
- return EAGAIN;
- }
+ return EAGAIN;
if (__pthread_handles[sseg].h_descr != NULL)
continue;
- if (pthread_allocate_stack(attr, thread_segment(sseg),
- pagesize, &stack_addr, &new_thread_bottom,
- &guardaddr, &guardsize, &stksize) == 0)
- {
-#ifdef __UCLIBC_HAS_TLS__
- new_thread->p_stackaddr = stack_addr;
-#else
- new_thread = (pthread_descr) stack_addr;
-#endif
- break;
+ if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize,
+ &new_thread, &new_thread_bottom,
+ &guardaddr, &guardsize) == 0)
+ break;
#ifndef __ARCH_USE_MMU__
- } else {
- /* When there is MMU, mmap () is used to allocate the stack. If one
- * segment is already mapped, we should continue to see if we can
- * use the next one. However, when there is no MMU, malloc () is used.
- * It's waste of CPU cycles to continue to try if it fails. */
- return EAGAIN;
+ else
+ /* When there is MMU, mmap () is used to allocate the stack. If one
+ * segment is already mapped, we should continue to see if we can
+ * use the next one. However, when there is no MMU, malloc () is used.
+ * It's waste of CPU cycles to continue to try if it fails. */
+ return EAGAIN;
#endif
- }
}
__pthread_handles_num++;
/* Allocate new thread identifier */
@@ -657,28 +518,20 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
new_thread_id = sseg + pthread_threads_counter;
/* Initialize the thread descriptor. Elements which have to be
initialized to zero already have this value. */
-#if !defined __UCLIBC_HAS_TLS__ || !TLS_DTV_AT_TP
- new_thread->p_header.data.tcb = new_thread;
- new_thread->p_header.data.self = new_thread;
-#endif
-#if TLS_MULTIPLE_THREADS_IN_TCB || !defined __UCLIBC_HAS_TLS__ || !TLS_DTV_AT_TP
- new_thread->p_multiple_threads = 1;
-#endif
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;
-#ifndef __UCLIBC_HAS_TLS__
new_thread->p_errnop = &new_thread->p_errno;
new_thread->p_h_errnop = &new_thread->p_h_errno;
- new_thread->p_resp = &new_thread->p_res;
-#endif
+#ifdef __UCLIBC_HAS_XLOCALE__
+ /* Initialize thread's locale to the global locale. */
+ new_thread->locale = __global_locale;
+#endif /* __UCLIBC_HAS_XLOCALE__ */
new_thread->p_guardaddr = guardaddr;
new_thread->p_guardsize = guardsize;
+ new_thread->p_self = new_thread;
new_thread->p_nr = sseg;
- new_thread->p_inheritsched = attr ? attr->__inheritsched : 0;
- new_thread->p_alloca_cutoff = stksize / 4 > __MAX_ALLOCA_CUTOFF
- ? __MAX_ALLOCA_CUTOFF : stksize / 4;
/* Initialize the thread handle */
__pthread_init_lock(&__pthread_handles[sseg].h_lock);
__pthread_handles[sseg].h_descr = new_thread;
@@ -696,8 +549,8 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
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);
+ 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 =
@@ -707,59 +560,55 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
new_thread->p_start_args.start_routine = start_routine;
new_thread->p_start_args.arg = arg;
new_thread->p_start_args.mask = *mask;
- /* Make the new thread ID available already now. If any of the later
- functions fail we return an error value and the caller must not use
- the stored thread ID. */
- *thread = new_thread_id;
/* 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. */
+ pid = 0; /* Note that the thread never can have PID zero. */
+ new_thread_top = ((char *)new_thread - THREAD_STACK_OFFSET);
+
+ /* ******************************************************** */
+ /* This code was moved from below to cope with running threads
+ * on uClinux systems. See comment below...
+ * 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;
+ /* ********************************************************* */
+
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);
+ uint32_t m = __td_eventmask (TD_CREATE);
- if ((mask & (__pthread_threads_events.event_bits[idx]
+ if ((m & (__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. */
-#ifdef NEED_SEPARATE_REGISTER_STACK
- /* Perhaps this version should be used on all platforms. But
- this requires that __clone2 be uniformly supported
- everywhere.
-
- And there is some argument for changing the __clone2
- interface to pass sp and bsp instead, making it more IA64
- specific, but allowing stacks to grow outward from each
- other, to get less paging and fewer mmaps. */
- pid = __clone2(pthread_start_thread_event,
- (void **)new_thread_bottom,
- (char *)stack_addr - new_thread_bottom,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM |
- __pthread_sig_cancel, new_thread);
-#elif defined _STACK_GROWS_UP
- pid = __clone(pthread_start_thread_event, (void *) new_thread_bottom,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM |
+#ifdef __ia64__
+ pid = __clone2(pthread_start_thread_event, new_thread_top,
+ new_thread_top - new_thread_bottom,
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
__pthread_sig_cancel, new_thread);
#else
- pid = __clone(pthread_start_thread_event, stack_addr,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM |
+ pid = clone(pthread_start_thread_event, new_thread_top,
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
__pthread_sig_cancel, new_thread);
#endif
+
saved_errno = errno;
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. */
+ 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;
@@ -779,69 +628,71 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr,
}
if (pid == 0)
{
-#ifdef NEED_SEPARATE_REGISTER_STACK
- pid = __clone2(pthread_start_thread,
- (void **)new_thread_bottom,
- (char *)stack_addr - new_thread_bottom,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM |
- __pthread_sig_cancel, new_thread);
-#elif defined _STACK_GROWS_UP
- pid = __clone(pthread_start_thread, (void *) new_thread_bottom,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM |
+ PDEBUG("cloning new_thread = %p\n", new_thread);
+#ifdef __ia64__
+ pid = __clone2(pthread_start_thread, new_thread_top,
+ new_thread_top - new_thread_bottom,
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
__pthread_sig_cancel, new_thread);
#else
- pid = __clone(pthread_start_thread, stack_addr,
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM |
+ pid = clone(pthread_start_thread, new_thread_top,
+ CLONE_VM | CLONE_FS | CLON