diff options
Diffstat (limited to 'REORG.TODO/nptl/pthread_create.c')
-rw-r--r-- | REORG.TODO/nptl/pthread_create.c | 933 |
1 files changed, 933 insertions, 0 deletions
diff --git a/REORG.TODO/nptl/pthread_create.c b/REORG.TODO/nptl/pthread_create.c new file mode 100644 index 0000000000..c7d1b8f413 --- /dev/null +++ b/REORG.TODO/nptl/pthread_create.c @@ -0,0 +1,933 @@ +/* Copyright (C) 2002-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include "pthreadP.h" +#include <hp-timing.h> +#include <ldsodefs.h> +#include <atomic.h> +#include <libc-internal.h> +#include <resolv.h> +#include <kernel-features.h> +#include <exit-thread.h> +#include <default-sched.h> +#include <futex-internal.h> + +#include <shlib-compat.h> + +#include <stap-probe.h> + + +/* Nozero if debugging mode is enabled. */ +int __pthread_debug; + +/* Globally enabled events. */ +static td_thr_events_t __nptl_threads_events __attribute_used__; + +/* Pointer to descriptor with the last event. */ +static struct pthread *__nptl_last_event __attribute_used__; + +/* Number of threads running. */ +unsigned int __nptl_nthreads = 1; + + +/* Code to allocate and deallocate a stack. */ +#include "allocatestack.c" + +/* CONCURRENCY NOTES: + + Understanding who is the owner of the 'struct pthread' or 'PD' + (refers to the value of the 'struct pthread *pd' function argument) + is critically important in determining exactly which operations are + allowed and which are not and when, particularly when it comes to the + implementation of pthread_create, pthread_join, pthread_detach, and + other functions which all operate on PD. + + The owner of PD is responsible for freeing the final resources + associated with PD, and may examine the memory underlying PD at any + point in time until it frees it back to the OS or to reuse by the + runtime. + + The thread which calls pthread_create is called the creating thread. + The creating thread begins as the owner of PD. + + During startup the new thread may examine PD in coordination with the + owner thread (which may be itself). + + The four cases of ownership transfer are: + + (1) Ownership of PD is released to the process (all threads may use it) + after the new thread starts in a joinable state + i.e. pthread_create returns a usable pthread_t. + + (2) Ownership of PD is released to the new thread starting in a detached + state. + + (3) Ownership of PD is dynamically released to a running thread via + pthread_detach. + + (4) Ownership of PD is acquired by the thread which calls pthread_join. + + Implementation notes: + + The PD->stopped_start and thread_ran variables are used to determine + exactly which of the four ownership states we are in and therefore + what actions can be taken. For example after (2) we cannot read or + write from PD anymore since the thread may no longer exist and the + memory may be unmapped. + + It is important to point out that PD->lock is being used both + similar to a one-shot semaphore and subsequently as a mutex. The + lock is taken in the parent to force the child to wait, and then the + child releases the lock. However, this semaphore-like effect is used + only for synchronizing the parent and child. After startup the lock + is used like a mutex to create a critical section during which a + single owner modifies the thread parameters. + + The most complicated cases happen during thread startup: + + (a) If the created thread is in a detached (PTHREAD_CREATE_DETACHED), + or joinable (default PTHREAD_CREATE_JOINABLE) state and + STOPPED_START is true, then the creating thread has ownership of + PD until the PD->lock is released by pthread_create. If any + errors occur we are in states (c), (d), or (e) below. + + (b) If the created thread is in a detached state + (PTHREAD_CREATED_DETACHED), and STOPPED_START is false, then the + creating thread has ownership of PD until it invokes the OS + kernel's thread creation routine. If this routine returns + without error, then the created thread owns PD; otherwise, see + (c) and (e) below. + + (c) If the detached thread setup failed and THREAD_RAN is true, then + the creating thread releases ownership to the new thread by + sending a cancellation signal. All threads set THREAD_RAN to + true as quickly as possible after returning from the OS kernel's + thread creation routine. + + (d) If the joinable thread setup failed and THREAD_RAN is true, then + then the creating thread retains ownership of PD and must cleanup + state. Ownership cannot be released to the process via the + return of pthread_create since a non-zero result entails PD is + undefined and therefore cannot be joined to free the resources. + We privately call pthread_join on the thread to finish handling + the resource shutdown (Or at least we should, see bug 19511). + + (e) If the thread creation failed and THREAD_RAN is false, then the + creating thread retains ownership of PD and must cleanup state. + No waiting for the new thread is required because it never + started. + + The nptl_db interface: + + The interface with nptl_db requires that we enqueue PD into a linked + list and then call a function which the debugger will trap. The PD + will then be dequeued and control returned to the thread. The caller + at the time must have ownership of PD and such ownership remains + after control returns to thread. The enqueued PD is removed from the + linked list by the nptl_db callback td_thr_event_getmsg. The debugger + must ensure that the thread does not resume execution, otherwise + ownership of PD may be lost and examining PD will not be possible. + + Note that the GNU Debugger as of (December 10th 2015) commit + c2c2a31fdb228d41ce3db62b268efea04bd39c18 no longer uses + td_thr_event_getmsg and several other related nptl_db interfaces. The + principal reason for this is that nptl_db does not support non-stop + mode where other threads can run concurrently and modify runtime + structures currently in use by the debugger and the nptl_db + interface. + + Axioms: + + * The create_thread function can never set stopped_start to false. + * The created thread can read stopped_start but never write to it. + * The variable thread_ran is set some time after the OS thread + creation routine returns, how much time after the thread is created + is unspecified, but it should be as quickly as possible. + +*/ + +/* CREATE THREAD NOTES: + + createthread.c defines the create_thread function, and two macros: + START_THREAD_DEFN and START_THREAD_SELF (see below). + + create_thread must initialize PD->stopped_start. It should be true + if the STOPPED_START parameter is true, or if create_thread needs the + new thread to synchronize at startup for some other implementation + reason. If STOPPED_START will be true, then create_thread is obliged + to lock PD->lock before starting the thread. Then pthread_create + unlocks PD->lock which synchronizes-with START_THREAD_DEFN in the + child thread which does an acquire/release of PD->lock as the last + action before calling the user entry point. The goal of all of this + is to ensure that the required initial thread attributes are applied + (by the creating thread) before the new thread runs user code. Note + that the the functions pthread_getschedparam, pthread_setschedparam, + pthread_setschedprio, __pthread_tpp_change_priority, and + __pthread_current_priority reuse the same lock, PD->lock, for a + similar purpose e.g. synchronizing the setting of similar thread + attributes. These functions are never called before the thread is + created, so don't participate in startup syncronization, but given + that the lock is present already and in the unlocked state, reusing + it saves space. + + The return value is zero for success or an errno code for failure. + If the return value is ENOMEM, that will be translated to EAGAIN, + so create_thread need not do that. On failure, *THREAD_RAN should + be set to true iff the thread actually started up and then got + canceled before calling user code (*PD->start_routine). */ +static int create_thread (struct pthread *pd, const struct pthread_attr *attr, + bool *stopped_start, STACK_VARIABLES_PARMS, + bool *thread_ran); + +#include <createthread.c> + + +struct pthread * +internal_function +__find_in_stack_list (struct pthread *pd) +{ + list_t *entry; + struct pthread *result = NULL; + + lll_lock (stack_cache_lock, LLL_PRIVATE); + + list_for_each (entry, &stack_used) + { + struct pthread *curp; + + curp = list_entry (entry, struct pthread, list); + if (curp == pd) + { + result = curp; + break; + } + } + + if (result == NULL) + list_for_each (entry, &__stack_user) + { + struct pthread *curp; + + curp = list_entry (entry, struct pthread, list); + if (curp == pd) + { + result = curp; + break; + } + } + + lll_unlock (stack_cache_lock, LLL_PRIVATE); + + return result; +} + + +/* Deallocate POSIX thread-local-storage. */ +void +attribute_hidden +__nptl_deallocate_tsd (void) +{ + struct pthread *self = THREAD_SELF; + + /* Maybe no data was ever allocated. This happens often so we have + a flag for this. */ + if (THREAD_GETMEM (self, specific_used)) + { + size_t round; + size_t cnt; + + round = 0; + do + { + size_t idx; + + /* So far no new nonzero data entry. */ + THREAD_SETMEM (self, specific_used, false); + + for (cnt = idx = 0; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt) + { + struct pthread_key_data *level2; + + level2 = THREAD_GETMEM_NC (self, specific, cnt); + + if (level2 != NULL) + { + size_t inner; + + for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE; + ++inner, ++idx) + { + void *data = level2[inner].data; + + if (data != NULL) + { + /* Always clear the data. */ + level2[inner].data = NULL; + + /* Make sure the data corresponds to a valid + key. This test fails if the key was + deallocated and also if it was + re-allocated. It is the user's + responsibility to free the memory in this + case. */ + if (level2[inner].seq + == __pthread_keys[idx].seq + /* It is not necessary to register a destructor + function. */ + && __pthread_keys[idx].destr != NULL) + /* Call the user-provided destructor. */ + __pthread_keys[idx].destr (data); + } + } + } + else + idx += PTHREAD_KEY_1STLEVEL_SIZE; + } + + if (THREAD_GETMEM (self, specific_used) == 0) + /* No data has been modified. */ + goto just_free; + } + /* We only repeat the process a fixed number of times. */ + while (__builtin_expect (++round < PTHREAD_DESTRUCTOR_ITERATIONS, 0)); + + /* Just clear the memory of the first block for reuse. */ + memset (&THREAD_SELF->specific_1stblock, '\0', + sizeof (self->specific_1stblock)); + + just_free: + /* Free the memory for the other blocks. */ + for (cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt) + { + struct pthread_key_data *level2; + + level2 = THREAD_GETMEM_NC (self, specific, cnt); + if (level2 != NULL) + { + /* The first block is allocated as part of the thread + descriptor. */ + free (level2); + THREAD_SETMEM_NC (self, specific, cnt, NULL); + } + } + + THREAD_SETMEM (self, specific_used, false); + } +} + + +/* Deallocate a thread's stack after optionally making sure the thread + descriptor is still valid. */ +void +internal_function +__free_tcb (struct pthread *pd) +{ + /* The thread is exiting now. */ + if (__builtin_expect (atomic_bit_test_set (&pd->cancelhandling, + TERMINATED_BIT) == 0, 1)) + { + /* Remove the descriptor from the list. */ + if (DEBUGGING_P && __find_in_stack_list (pd) == NULL) + /* Something is really wrong. The descriptor for a still + running thread is gone. */ + abort (); + + /* Free TPP data. */ + if (__glibc_unlikely (pd->tpp != NULL)) + { + struct priority_protection_data *tpp = pd->tpp; + + pd->tpp = NULL; + free (tpp); + } + + /* Queue the stack memory block for reuse and exit the process. The + kernel will signal via writing to the address returned by + QUEUE-STACK when the stack is available. */ + __deallocate_stack (pd); + } +} + + +/* Local function to start thread and handle cleanup. + createthread.c defines the macro START_THREAD_DEFN to the + declaration that its create_thread function will refer to, and + START_THREAD_SELF to the expression to optimally deliver the new + thread's THREAD_SELF value. */ +START_THREAD_DEFN +{ + struct pthread *pd = START_THREAD_SELF; + +#if HP_TIMING_AVAIL + /* Remember the time when the thread was started. */ + hp_timing_t now; + HP_TIMING_NOW (now); + THREAD_SETMEM (pd, cpuclock_offset, now); +#endif + + /* Initialize resolver state pointer. */ + __resp = &pd->res; + + /* Initialize pointers to locale data. */ + __ctype_init (); + + /* Allow setxid from now onwards. */ + if (__glibc_unlikely (atomic_exchange_acq (&pd->setxid_futex, 0) == -2)) + futex_wake (&pd->setxid_futex, 1, FUTEX_PRIVATE); + +#ifdef __NR_set_robust_list +# ifndef __ASSUME_SET_ROBUST_LIST + if (__set_robust_list_avail >= 0) +# endif + { + INTERNAL_SYSCALL_DECL (err); + /* This call should never fail because the initial call in init.c + succeeded. */ + INTERNAL_SYSCALL (set_robust_list, err, 2, &pd->robust_head, + sizeof (struct robust_list_head)); + } +#endif + +#ifdef SIGCANCEL + /* If the parent was running cancellation handlers while creating + the thread the new thread inherited the signal mask. Reset the + cancellation signal mask. */ + if (__glibc_unlikely (pd->parent_cancelhandling & CANCELING_BITMASK)) + { + INTERNAL_SYSCALL_DECL (err); + sigset_t mask; + __sigemptyset (&mask); + __sigaddset (&mask, SIGCANCEL); + (void) INTERNAL_SYSCALL (rt_sigprocmask, err, 4, SIG_UNBLOCK, &mask, + NULL, _NSIG / 8); + } +#endif + + /* This is where the try/finally block should be created. For + compilers without that support we do use setjmp. */ + struct pthread_unwind_buf unwind_buf; + + /* No previous handlers. */ + unwind_buf.priv.data.prev = NULL; + unwind_buf.priv.data.cleanup = NULL; + + int not_first_call; + not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); + if (__glibc_likely (! not_first_call)) + { + /* Store the new cleanup handler info. */ + THREAD_SETMEM (pd, cleanup_jmp_buf, &unwind_buf); + + /* We are either in (a) or (b), and in either case we either own + PD already (2) or are about to own PD (1), and so our only + restriction would be that we can't free PD until we know we + have ownership (see CONCURRENCY NOTES above). */ + if (__glibc_unlikely (pd->stopped_start)) + { + int oldtype = CANCEL_ASYNC (); + + /* Get the lock the parent locked to force synchronization. */ + lll_lock (pd->lock, LLL_PRIVATE); + + /* We have ownership of PD now. */ + + /* And give it up right away. */ + lll_unlock (pd->lock, LLL_PRIVATE); + + CANCEL_RESET (oldtype); + } + + LIBC_PROBE (pthread_start, 3, (pthread_t) pd, pd->start_routine, pd->arg); + + /* Run the code the user provided. */ + THREAD_SETMEM (pd, result, pd->start_routine (pd->arg)); + } + + /* Call destructors for the thread_local TLS variables. */ +#ifndef SHARED + if (&__call_tls_dtors != NULL) +#endif + __call_tls_dtors (); + + /* Run the destructor for the thread-local data. */ + __nptl_deallocate_tsd (); + + /* Clean up any state libc stored in thread-local variables. */ + __libc_thread_freeres (); + + /* If this is the last thread we terminate the process now. We + do not notify the debugger, it might just irritate it if there + is no thread left. */ + if (__glibc_unlikely (atomic_decrement_and_test (&__nptl_nthreads))) + /* This was the last thread. */ + exit (0); + + /* Report the death of the thread if this is wanted. */ + if (__glibc_unlikely (pd->report_events)) + { + /* See whether TD_DEATH is in any of the mask. */ + const int idx = __td_eventword (TD_DEATH); + const uint32_t mask = __td_eventmask (TD_DEATH); + + if ((mask & (__nptl_threads_events.event_bits[idx] + | pd->eventbuf.eventmask.event_bits[idx])) != 0) + { + /* Yep, we have to signal the death. Add the descriptor to + the list but only if it is not already on it. */ + if (pd->nextevent == NULL) + { + pd->eventbuf.eventnum = TD_DEATH; + pd->eventbuf.eventdata = pd; + + do + pd->nextevent = __nptl_last_event; + while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event, + pd, pd->nextevent)); + } + + /* Now call the function which signals the event. See + CONCURRENCY NOTES for the nptl_db interface comments. */ + __nptl_death_event (); + } + } + + /* The thread is exiting now. Don't set this bit until after we've hit + the event-reporting breakpoint, so that td_thr_get_info on us while at + the breakpoint reports TD_THR_RUN state rather than TD_THR_ZOMBIE. */ + atomic_bit_set (&pd->cancelhandling, EXITING_BIT); + +#ifndef __ASSUME_SET_ROBUST_LIST + /* If this thread has any robust mutexes locked, handle them now. */ +# ifdef __PTHREAD_MUTEX_HAVE_PREV + void *robust = pd->robust_head.list; +# else + __pthread_slist_t *robust = pd->robust_list.__next; +# endif + /* We let the kernel do the notification if it is able to do so. + If we have to do it here there for sure are no PI mutexes involved + since the kernel support for them is even more recent. */ + if (__set_robust_list_avail < 0 + && __builtin_expect (robust != (void *) &pd->robust_head, 0)) + { + do + { + struct __pthread_mutex_s *this = (struct __pthread_mutex_s *) + ((char *) robust - offsetof (struct __pthread_mutex_s, + __list.__next)); + robust = *((void **) robust); + +# ifdef __PTHREAD_MUTEX_HAVE_PREV + this->__list.__prev = NULL; +# endif + this->__list.__next = NULL; + + atomic_or (&this->__lock, FUTEX_OWNER_DIED); + futex_wake ((unsigned int *) &this->__lock, 1, + /* XYZ */ FUTEX_SHARED); + } + while (robust != (void *) &pd->robust_head); + } +#endif + + /* Mark the memory of the stack as usable to the kernel. We free + everything except for the space used for the TCB itself. */ + size_t pagesize_m1 = __getpagesize () - 1; +#ifdef _STACK_GROWS_DOWN + char *sp = CURRENT_STACK_FRAME; + size_t freesize = (sp - (char *) pd->stackblock) & ~pagesize_m1; + assert (freesize < pd->stackblock_size); + if (freesize > PTHREAD_STACK_MIN) + __madvise (pd->stackblock, freesize - PTHREAD_STACK_MIN, MADV_DONTNEED); +#else + /* Page aligned start of memory to free (higher than or equal + to current sp plus the minimum stack size). */ + void *freeblock = (void*)((size_t)(CURRENT_STACK_FRAME + + PTHREAD_STACK_MIN + + pagesize_m1) + & ~pagesize_m1); + char *free_end = (char *) (((uintptr_t) pd - pd->guardsize) & ~pagesize_m1); + /* Is there any space to free? */ + if (free_end > (char *)freeblock) + { + size_t freesize = (size_t)(free_end - (char *)freeblock); + assert (freesize < pd->stackblock_size); + __madvise (freeblock, freesize, MADV_DONTNEED); + } +#endif + + /* If the thread is detached free the TCB. */ + if (IS_DETACHED (pd)) + /* Free the TCB. */ + __free_tcb (pd); + else if (__glibc_unlikely (pd->cancelhandling & SETXID_BITMASK)) + { + /* Some other thread might call any of the setXid functions and expect + us to reply. In this case wait until we did that. */ + do + /* XXX This differs from the typical futex_wait_simple pattern in that + the futex_wait condition (setxid_futex) is different from the + condition used in the surrounding loop (cancelhandling). We need + to check and document why this is correct. */ + futex_wait_simple (&pd->setxid_futex, 0, FUTEX_PRIVATE); + while (pd->cancelhandling & SETXID_BITMASK); + + /* Reset the value so that the stack can be reused. */ + pd->setxid_futex = 0; + } + + /* We cannot call '_exit' here. '_exit' will terminate the process. + + The 'exit' implementation in the kernel will signal when the + process is really dead since 'clone' got passed the CLONE_CHILD_CLEARTID + flag. The 'tid' field in the TCB will be set to zero. + + The exit code is zero since in case all threads exit by calling + 'pthread_exit' the exit status must be 0 (zero). */ + __exit_thread (); + + /* NOTREACHED */ +} + + +/* Return true iff obliged to report TD_CREATE events. */ +static bool +report_thread_creation (struct pthread *pd) +{ + if (__glibc_unlikely (THREAD_GETMEM (THREAD_SELF, report_events))) + { + /* The parent thread is supposed to report events. + Check whether the TD_CREATE event is needed, too. */ + const size_t idx = __td_eventword (TD_CREATE); + const uint32_t mask = __td_eventmask (TD_CREATE); + + return ((mask & (__nptl_threads_events.event_bits[idx] + | pd->eventbuf.eventmask.event_bits[idx])) != 0); + } + return false; +} + + +int +__pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + STACK_VARIABLES; + + const struct pthread_attr *iattr = (struct pthread_attr *) attr; + struct pthread_attr default_attr; + bool free_cpuset = false; + if (iattr == NULL) + { + lll_lock (__default_pthread_attr_lock, LLL_PRIVATE); + default_attr = __default_pthread_attr; + size_t cpusetsize = default_attr.cpusetsize; + if (cpusetsize > 0) + { + cpu_set_t *cpuset; + if (__glibc_likely (__libc_use_alloca (cpusetsize))) + cpuset = __alloca (cpusetsize); + else + { + cpuset = malloc (cpusetsize); + if (cpuset == NULL) + { + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + return ENOMEM; + } + free_cpuset = true; + } + memcpy (cpuset, default_attr.cpuset, cpusetsize); + default_attr.cpuset = cpuset; + } + lll_unlock (__default_pthread_attr_lock, LLL_PRIVATE); + iattr = &default_attr; + } + + struct pthread *pd = NULL; + int err = ALLOCATE_STACK (iattr, &pd); + int retval = 0; + + if (__glibc_unlikely (err != 0)) + /* Something went wrong. Maybe a parameter of the attributes is + invalid or we could not allocate memory. Note we have to + translate error codes. */ + { + retval = err == ENOMEM ? EAGAIN : err; + goto out; + } + + + /* Initialize the TCB. All initializations with zero should be + performed in 'get_cached_stack'. This way we avoid doing this if + the stack freshly allocated with 'mmap'. */ + +#if TLS_TCB_AT_TP + /* Reference to the TCB itself. */ + pd->header.self = pd; + + /* Self-reference for TLS. */ + pd->header.tcb = pd; +#endif + + /* Store the address of the start routine and the parameter. Since + we do not start the function directly the stillborn thread will + get the information from its thread descriptor. */ + pd->start_routine = start_routine; + pd->arg = arg; + + /* Copy the thread attribute flags. */ + struct pthread *self = THREAD_SELF; + pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) + | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))); + + /* Initialize the field for the ID of the thread which is waiting + for us. This is a self-reference in case the thread is created + detached. */ + pd->joinid = iattr->flags & ATTR_FLAG_DETACHSTATE ? pd : NULL; + + /* The debug events are inherited from the parent. */ + pd->eventbuf = self->eventbuf; + + + /* Copy the parent's scheduling parameters. The flags will say what + is valid and what is not. */ + pd->schedpolicy = self->schedpolicy; + pd->schedparam = self->schedparam; + + /* Copy the stack guard canary. */ +#ifdef THREAD_COPY_STACK_GUARD + THREAD_COPY_STACK_GUARD (pd); +#endif + + /* Copy the pointer guard value. */ +#ifdef THREAD_COPY_POINTER_GUARD + THREAD_COPY_POINTER_GUARD (pd); +#endif + + /* Verify the sysinfo bits were copied in allocate_stack if needed. */ +#ifdef NEED_DL_SYSINFO + CHECK_THREAD_SYSINFO (pd); +#endif + + /* Inform start_thread (above) about cancellation state that might + translate into inherited signal state. */ + pd->parent_cancelhandling = THREAD_GETMEM (THREAD_SELF, cancelhandling); + + /* Determine scheduling parameters for the thread. */ + if (__builtin_expect ((iattr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0, 0) + && (iattr->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) != 0) + { + /* Use the scheduling parameters the user provided. */ + if (iattr->flags & ATTR_FLAG_POLICY_SET) + { + pd->schedpolicy = iattr->schedpolicy; + pd->flags |= ATTR_FLAG_POLICY_SET; + } + if (iattr->flags & ATTR_FLAG_SCHED_SET) + { + /* The values were validated in pthread_attr_setschedparam. */ + pd->schedparam = iattr->schedparam; + pd->flags |= ATTR_FLAG_SCHED_SET; + } + + if ((pd->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) + != (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) + collect_default_sched (pd); + } + + /* Pass the descriptor to the caller. */ + *newthread = (pthread_t) pd; + + LIBC_PROBE (pthread_create, 4, newthread, attr, start_routine, arg); + + /* One more thread. We cannot have the thread do this itself, since it + might exist but not have been scheduled yet by the time we've returned + and need to check the value to behave correctly. We must do it before + creating the thread, in case it does get scheduled first and then + might mistakenly think it was the only thread. In the failure case, + we momentarily store a false value; this doesn't matter because there + is no kosher thing a signal handler interrupting us right here can do + that cares whether the thread count is correct. */ + atomic_increment (&__nptl_nthreads); + + /* Our local value of stopped_start and thread_ran can be accessed at + any time. The PD->stopped_start may only be accessed if we have + ownership of PD (see CONCURRENCY NOTES above). */ + bool stopped_start = false; bool thread_ran = false; + + /* Start the thread. */ + if (__glibc_unlikely (report_thread_creation (pd))) + { + stopped_start = true; + + /* We always create the thread stopped at startup so we can + notify the debugger. */ + retval = create_thread (pd, iattr, &stopped_start, + STACK_VARIABLES_ARGS, &thread_ran); + if (retval == 0) + { + /* We retain ownership of PD until (a) (see CONCURRENCY NOTES + above). */ + + /* Assert stopped_start is true in both our local copy and the + PD copy. */ + assert (stopped_start); + assert (pd->stopped_start); + + /* 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. */ + pd->eventbuf.eventnum = TD_CREATE; + pd->eventbuf.eventdata = pd; + + /* Enqueue the descriptor. */ + do + pd->nextevent = __nptl_last_event; + while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event, + pd, pd->nextevent) + != 0); + + /* Now call the function which signals the event. See + CONCURRENCY NOTES for the nptl_db interface comments. */ + __nptl_create_event (); + } + } + else + retval = create_thread (pd, iattr, &stopped_start, + STACK_VARIABLES_ARGS, &thread_ran); + + if (__glibc_unlikely (retval != 0)) + { + if (thread_ran) + /* State (c) or (d) and we may not have PD ownership (see + CONCURRENCY NOTES above). We can assert that STOPPED_START + must have been true because thread creation didn't fail, but + thread attribute setting did. */ + /* See bug 19511 which explains why doing nothing here is a + resource leak for a joinable thread. */ + assert (stopped_start); + else + { + /* State (e) and we have ownership of PD (see CONCURRENCY + NOTES above). */ + + /* Oops, we lied for a second. */ + atomic_decrement (&__nptl_nthreads); + + /* Perhaps a thread wants to change the IDs and is waiting for this + stillborn thread. */ + if (__glibc_unlikely (atomic_exchange_acq (&pd->setxid_futex, 0) + == -2)) + futex_wake (&pd->setxid_futex, 1, FUTEX_PRIVATE); + + /* Free the resources. */ + __deallocate_stack (pd); + } + + /* We have to translate error codes. */ + if (retval == ENOMEM) + retval = EAGAIN; + } + else + { + /* We don't know if we have PD ownership. Once we check the local + stopped_start we'll know if we're in state (a) or (b) (see + CONCURRENCY NOTES above). */ + if (stopped_start) + /* State (a), we own PD. The thread blocked on this lock either + because we're doing TD_CREATE event reporting, or for some + other reason that create_thread chose. Now let it run + free. */ + lll_unlock (pd->lock, LLL_PRIVATE); + + /* We now have for sure more than one thread. The main thread might + not yet have the flag set. No need to set the global variable + again if this is what we use. */ + THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); + } + + out: + if (__glibc_unlikely (free_cpuset)) + free (default_attr.cpuset); + + return retval; +} +versioned_symbol (libpthread, __pthread_create_2_1, pthread_create, GLIBC_2_1); + + +#if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_1) +int +__pthread_create_2_0 (pthread_t *newthread, 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. */ + struct pthread_attr new_attr; + + if (attr != NULL) + { + struct pthread_attr *iattr = (struct pthread_attr *) attr; + size_t ps = __getpagesize (); + + /* Copy values from the user-provided attributes. */ + new_attr.schedparam = iattr->schedparam; + new_attr.schedpolicy = iattr->schedpolicy; + new_attr.flags = iattr->flags; + + /* Fill in default values for the fields not present in the old + implementation. */ + new_attr.guardsize = ps; + new_attr.stackaddr = NULL; + new_attr.stacksize = 0; + new_attr.cpuset = NULL; + + /* We will pass this value on to the real implementation. */ + attr = (pthread_attr_t *) &new_attr; + } + + return __pthread_create_2_1 (newthread, attr, start_routine, arg); +} +compat_symbol (libpthread, __pthread_create_2_0, pthread_create, + GLIBC_2_0); +#endif + +/* Information for libthread_db. */ + +#include "../nptl_db/db_info.c" + +/* If pthread_create is present, libgcc_eh.a and libsupc++.a expects some other POSIX thread + functions to be present as well. */ +PTHREAD_STATIC_FN_REQUIRE (pthread_mutex_lock) +PTHREAD_STATIC_FN_REQUIRE (pthread_mutex_trylock) +PTHREAD_STATIC_FN_REQUIRE (pthread_mutex_unlock) + +PTHREAD_STATIC_FN_REQUIRE (pthread_once) +PTHREAD_STATIC_FN_REQUIRE (pthread_cancel) + +PTHREAD_STATIC_FN_REQUIRE (pthread_key_create) +PTHREAD_STATIC_FN_REQUIRE (pthread_key_delete) +PTHREAD_STATIC_FN_REQUIRE (pthread_setspecific) +PTHREAD_STATIC_FN_REQUIRE (pthread_getspecific) |