diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2015-02-14 23:04:02 -0600 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2015-02-14 23:04:02 -0600 |
commit | e1e46622ac0fce73d802fa4a8a2e83cc25cd9e7a (patch) | |
tree | 2c8d14e4dcc733f4227f0984ed05da5826b13acd /libpthread/linuxthreads/manager.c | |
parent | 6b6ede3d15f04fe825cfa9f697507457e3640344 (diff) |
Revert "resolve merge"
This reverts commit 6b6ede3d15f04fe825cfa9f697507457e3640344.
Diffstat (limited to 'libpthread/linuxthreads/manager.c')
-rw-r--r-- | libpthread/linuxthreads/manager.c | 829 |
1 files changed, 509 insertions, 320 deletions
diff --git a/libpthread/linuxthreads/manager.c b/libpthread/linuxthreads/manager.c index e4022f8ea..3c5bee876 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 <features.h> +#include <assert.h> #include <errno.h> #include <sched.h> #include <stddef.h> @@ -27,77 +27,72 @@ #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 "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 */ }; +#include <not-cancel.h> /* 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; +__volatile__ pthread_descr __pthread_last_event; + +static pthread_descr manager_thread; /* 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 = 0; +static __volatile__ int terminated_children; /* Flag set when the initial thread is blocked on pthread_exit waiting for all other threads to terminate */ -static int main_thread_exiting = 0; +static int main_thread_exiting; /* Counter used to generate unique thread identifier. Thread identifier is pthread_threads_counter + segment. */ -static pthread_t pthread_threads_counter = 0; +static pthread_t pthread_threads_counter; /* Forward declarations */ @@ -107,67 +102,54 @@ 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) { - int reqfd = (int) (long int) arg; -#ifdef USE_SELECT - struct timeval tv; - fd_set fd; -#else + pthread_descr self = manager_thread = arg; + int reqfd = __pthread_manager_reader; 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(&__pthread_manager_thread, 1); + INIT_THREAD_SELF(self, 1); #endif +#ifndef __UCLIBC_HAS_TLS__ /* 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; - -#ifdef __UCLIBC_HAS_XLOCALE__ - /* Initialize thread's locale to the global locale. */ - __pthread_manager_thread.locale = __global_locale; -#endif /* __UCLIBC_HAS_XLOCALE__ */ - + self->p_errnop = &self->p_errno; + self->p_h_errnop = &self->p_h_errno; +#endif /* 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(reqfd, (char *)&request, - sizeof(request))); -#ifndef USE_SELECT + n = TEMP_FAILURE_RETRY(read_not_cancel(reqfd, (char *)&request, + sizeof(request))); + ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG); ufd.fd = reqfd; ufd.events = POLLIN; -#endif /* Enter server loop */ while(1) { -#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 + n = __poll(&ufd, 1, 2000); + /* Check for termination of the main thread */ if (getppid() == 1) { pthread_kill_all_threads(SIGKILL, 0); @@ -179,19 +161,20 @@ int attribute_noreturn __pthread_manager(void *arg) pthread_reap_children(); } /* Read and execute request */ -#ifdef USE_SELECT - if (n == 1) -#else - if (n == 1 && (ufd.revents & POLLIN)) + 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); + } #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, @@ -199,23 +182,19 @@ int attribute_noreturn __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); - PDEBUG("restarting %p\n", request.req_thread); + request.req_thread->p_report_events, + &request.req_thread->p_eventbuf.eventmask); restart(request.req_thread); break; case REQ_FREE: - PDEBUG("got REQ_FREE\n"); - pthread_handle_free(request.req_args.free.thread_id); + 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 @@ -231,81 +210,99 @@ int attribute_noreturn __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) { - PDEBUG("about to call raise(__pthread_sig_debug)\n"); + if (__pthread_threads_debug && __pthread_sig_debug > 0) 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 attribute_noreturn __pthread_manager_event(void *arg) +int __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(&__pthread_manager_thread, 1); + INIT_THREAD_SELF(self, 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); + __pthread_lock (THREAD_GETMEM(self, p_lock), NULL); /* Free it immediately. */ - __pthread_unlock (THREAD_GETMEM((&__pthread_manager_thread), p_lock)); + __pthread_unlock (THREAD_GETMEM(self, p_lock)); - __pthread_manager(arg); + return __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 - PDEBUG("\n"); +#if HP_TIMING_AVAIL + HP_TIMING_NOW (tmpclock); + THREAD_SETMEM (self, p_cpuclock_offset, tmpclock); +#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()); /* 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 (__pthread_manager_thread.p_priority > 0) + else if (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(__pthread_manager_request, - (char *) &request, sizeof(request))); + TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, + (char *) &request, sizeof(request))); suspend(self); } /* Run the thread code */ @@ -316,7 +313,7 @@ pthread_start_thread(void *arg) } static int -attribute_noreturn +__attribute__ ((noreturn)) pthread_start_thread_event(void *arg) { pthread_descr self = (pthread_descr) arg; @@ -326,7 +323,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. */ @@ -336,136 +333,250 @@ 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, - pthread_descr * out_new_thread, + char ** out_new_thread, char ** out_new_thread_bottom, char ** out_guardaddr, - size_t * out_guardsize) + size_t * out_guardsize, + size_t * out_stacksize) { 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. */ - new_thread = (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1; +# 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_bottom = (char *) attr->__stackaddr - attr->__stacksize; - guardaddr = NULL; + guardaddr = new_thread_bottom; 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 __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; - } - } +#ifdef NEED_SEPARATE_REGISTER_STACK + const size_t granularity = 2 * pagesize; + /* Try to make stacksize/2 a multiple of pagesize */ #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 */ + const size_t granularity = pagesize; +#endif + void *map_addr; - stacksize = INITIAL_STACK_SIZE; - /* The user may want to use a non-default stacksize */ + /* Allocate space for stack and thread descriptor at default address */ +#if FLOATING_STACKS if (attr != NULL) { - stacksize = attr->__stacksize; + guardsize = page_roundup (attr->__guardsize, granularity); + stacksize = __pthread_max_stacksize - guardsize; + stacksize = MIN (stacksize, + page_roundup (attr->__stacksize, granularity)); } - - /* malloc a stack - memory from the bottom up */ - if ((new_thread_bottom = malloc(stacksize)) == NULL) + else { - /* bad luck, we cannot malloc any more */ - return -1 ; + guardsize = granularity; + stacksize = __pthread_max_stacksize - guardsize; } - 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; - PDEBUG("thread stack: bos=%p, tos=%p\n", new_thread_bottom, new_thread); + 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; - /* check the initial thread stack boundaries so they don't overlap */ - NOMMU_INITIAL_THREAD_BOUNDS((char *) new_thread, (char *) new_thread_bottom); +# 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 - PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos, - __pthread_initial_thread_tos); + 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; + } - /* on non-MMU systems we always have non-standard stack frames */ - __pthread_nonstandard_stacks = 1; +# 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; + } -#endif /* __ARCH_USE_MMU__ */ - } + 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; + } - /* Clear the thread data structure. */ - memset (new_thread, '\0', sizeof (*new_thread)); - *out_new_thread = new_thread; + /* 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 */ + } + *out_new_thread = (char *) 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; } @@ -478,17 +589,30 @@ 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; - int pagesize = getpagesize(); + size_t guardsize = 0, stksize = 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; @@ -496,21 +620,36 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, for (sseg = 2; ; sseg++) { if (sseg >= PTHREAD_THREADS_MAX) - return EAGAIN; + { +#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; + } 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; + 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; #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 */ @@ -518,20 +657,28 @@ 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; -#ifdef __UCLIBC_HAS_XLOCALE__ - /* Initialize thread's locale to the global locale. */ - new_thread->locale = __global_locale; -#endif /* __UCLIBC_HAS_XLOCALE__ */ + new_thread->p_resp = &new_thread->p_res; +#endif 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; @@ -549,8 +696,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 = @@ -560,55 +707,59 @@ 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. */ - 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; - /* ********************************************************* */ - + 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 m = __td_eventmask (TD_CREATE); + uint32_t mask = __td_eventmask (TD_CREATE); - if ((m & (__pthread_threads_events.event_bits[idx] + 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. */ -#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 | +#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 | __pthread_sig_cancel, new_thread); #else - pid = clone(pthread_start_thread_event, new_thread_top, - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + pid = __clone(pthread_start_thread_event, stack_addr, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __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; |