-/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+/* 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.
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
+ 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 "libioP.h"
#include <shlib-compat.h>
-
-/* Local function to start thread and handle cleanup. */
-static int start_thread (void *arg);
-/* Similar version used when debugging. */
-static int start_thread_debug (void *arg);
+#include <stap-probe.h>
/* Nozero if debugging mode is enabled. */
int __pthread_debug;
/* Globally enabled events. */
-static td_thr_events_t __nptl_threads_events;
+static td_thr_events_t __nptl_threads_events __attribute_used__;
/* Pointer to descriptor with the last event. */
-static struct pthread *__nptl_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. */
-#define DEFINE_DEALLOC
#include "allocatestack.c"
-/* Code to create the thread. */
-#include "createthread.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>
-/* Table of the key information. */
-struct pthread_key_struct __pthread_keys[PTHREAD_KEYS_MAX]
- __attribute__ ((section (".bss")));
-hidden_def (__pthread_keys)
-
-/* This is for libthread_db only. */
-const int __pthread_pthread_sizeof_descr = sizeof (struct pthread);
struct pthread *
-__find_in_stack_list (pd)
- struct pthread *pd;
+internal_function
+__find_in_stack_list (struct pthread *pd)
{
list_t *entry;
struct pthread *result = NULL;
- lll_lock (stack_cache_lock);
+ lll_lock (stack_cache_lock, LLL_PRIVATE);
list_for_each (entry, &stack_used)
{
struct pthread *curp;
- curp = list_entry (entry, struct pthread, header.data.list);
+ curp = list_entry (entry, struct pthread, list);
if (curp == pd)
{
result = curp;
{
struct pthread *curp;
- curp = list_entry (entry, struct pthread, header.data.list);
+ curp = list_entry (entry, struct pthread, list);
if (curp == pd)
{
result = curp;
}
}
- lll_unlock (stack_cache_lock);
+ lll_unlock (stack_cache_lock, LLL_PRIVATE);
return result;
}
/* Deallocate POSIX thread-local-storage. */
-static void
-deallocate_tsd (struct pthread *pd)
+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 (pd->specific_used)
+ if (THREAD_GETMEM (self, specific_used))
{
size_t round;
- bool found_nonzero;
+ size_t cnt;
- for (round = 0, found_nonzero = true;
- found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
- ++round)
+ round = 0;
+ do
{
- size_t cnt;
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)
- if (pd->specific[cnt] != NULL)
- {
- size_t inner;
-
- for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE;
- ++inner, ++idx)
- {
- void *data = pd->specific[cnt][inner].data;
-
- if (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. */
- && (pd->specific[cnt][inner].seq
- == __pthread_keys[idx].seq)
- /* It is not necessary to register a destructor
- function. */
- && __pthread_keys[idx].destr != NULL)
- {
- pd->specific[cnt][inner].data = NULL;
- __pthread_keys[idx].destr (data);
- found_nonzero = true;
- }
- }
-
- if (cnt != 0)
- {
- /* The first block is allocated as part of the thread
- descriptor. */
- free (pd->specific[cnt]);
- pd->specific[cnt] = NULL;
- }
- else
- /* Clear the memory of the first block for reuse. */
- memset (pd->specific[0], '\0',
- sizeof (struct pthread_key_data));
- }
- else
- idx += PTHREAD_KEY_1STLEVEL_SIZE;
+ {
+ 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);
+ }
}
- pd->specific_used = false;
+ 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 (atomic_bit_test_set (&pd->cancelhandling, TERMINATED_BIT) == 0)
+ 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)
running thread is gone. */
abort ();
- /* Run the destructor for the thread-local data. */
- deallocate_tsd (pd);
+ /* 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
}
-static int
-start_thread (void *arg)
+/* 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
{
- /* One more thread. */
- atomic_increment (&__nptl_nthreads);
-
- struct pthread *pd = (struct pthread *) arg;
+ struct pthread *pd = START_THREAD_SELF;
#if HP_TIMING_AVAIL
/* Remember the time when the thread was started. */
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. */
- if (setjmp (pd->cancelbuf) == 0)
+ 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. */
- pd->result = pd->start_routine (pd->arg);
+ 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 (atomic_decrement_and_test (&__nptl_nthreads))
+ 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 (__builtin_expect (pd->report_events, 0))
+ if (__glibc_unlikely (pd->report_events))
{
/* See whether TD_DEATH is in any of the mask. */
const int idx = __td_eventword (TD_DEATH);
do
pd->nextevent = __nptl_last_event;
- while (atomic_compare_and_exchange_acq (&__nptl_last_event, pd,
- pd->nextevent) != 0);
+ while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event,
+ pd, pd->nextevent));
}
- /* Now call the function to signal the event. */
+ /* 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. */
+ /* 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_CLEARTID
+ 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_inline (0);
+ __exit_thread ();
/* NOTREACHED */
- return 0;
}
-/* Just list start_thread but we do some more things needed for a run
- with a debugger attached. */
-static int
-start_thread_debug (void *arg)
+/* Return true iff obliged to report TD_CREATE events. */
+static bool
+report_thread_creation (struct pthread *pd)
{
- struct pthread *pd = (struct pthread *) arg;
-
- /* Get the lock the parent locked to force synchronization. */
- lll_lock (pd->lock);
- /* And give it up right away. */
- lll_unlock (pd->lock);
+ 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);
- /* Now do the actual startup. */
- return start_thread (arg);
+ return ((mask & (__nptl_threads_events.event_bits[idx]
+ | pd->eventbuf.eventmask.event_bits[idx])) != 0);
+ }
+ return false;
}
-/* Default thread attributes for the case when the user does not
- provide any. */
-static const struct pthread_attr default_attr =
- {
- /* Just some value > 0 which gets rounded to the nearest page size. */
- .guardsize = 1,
- };
-
-
int
-__pthread_create_2_1 (newthread, attr, start_routine, arg)
- pthread_t *newthread;
- const pthread_attr_t *attr;
- void *(*start_routine) (void *);
- void *arg;
+__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 *pd;
- int err;
- iattr = (struct pthread_attr *) attr;
+ const struct pthread_attr *iattr = (struct pthread_attr *) attr;
+ struct pthread_attr default_attr;
+ bool free_cpuset = false;
if (iattr == NULL)
- /* Is this the best idea? On NUMA machines this could mean
- accessing far-away memory. */
- iattr = &default_attr;
+ {
+ 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;
+ }
- err = ALLOCATE_STACK (iattr, &pd);
- if (err != 0)
+ 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. */
- return err;
+ 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.data.self = pd;
+ pd->header.self = pd;
-#ifdef TLS_TCB_AT_TP
- /* Self-reference. */
- pd->header.data.tcb = pd;
+ /* Self-reference for TLS. */
+ pd->header.tcb = pd;
#endif
/* Store the address of the start routine and the parameter. Since
pd->arg = arg;
/* Copy the thread attribute flags. */
- pd->flags = iattr->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
pd->joinid = iattr->flags & ATTR_FLAG_DETACHSTATE ? pd : NULL;
/* The debug events are inherited from the parent. */
- pd->eventbuf = THREAD_SELF->eventbuf;
+ 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.
- XXX How to determine whether scheduling handling is needed? */
- if (0 && attr != NULL)
+ /* 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)
{
- if (iattr->flags & ATTR_FLAG_NOTINHERITSCHED)
- {
- /* Use the scheduling parameters the user provided. */
- pd->schedpolicy = iattr->schedpolicy;
- memcpy (&pd->schedparam, &iattr->schedparam,
- sizeof (struct sched_param));
- }
- else
- {
- /* Just store the scheduling attributes of the parent. */
- pd->schedpolicy = __sched_getscheduler (0);
- __sched_getparam (0, &pd->schedparam);
- }
+ /* 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);
}
+ if (__glibc_unlikely (__nptl_nthreads == 1))
+ _IO_enable_locks ();
+
/* 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. */
- err = create_thread (pd, STACK_VARIABLES_ARGS);
- if (err != 0)
+ if (__glibc_unlikely (report_thread_creation (pd)))
{
- /* Something went wrong. Free the resources. */
- __deallocate_stack (pd);
- return err;
+ 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 0;
+ 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 (newthread, attr, start_routine, arg)
- pthread_t *newthread;
- const pthread_attr_t *attr;
- void *(*start_routine) (void *);
- void *arg;
+__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.
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;
compat_symbol (libpthread, __pthread_create_2_0, pthread_create,
GLIBC_2_0);
#endif
+\f
+/* Information for libthread_db. */
+
+#include "../nptl_db/db_info.c"
+\f
+/* 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)