/* Do some minimal initialization which has to be done during the startup of the C library. */ void __pthread_initialize_minimal(void) { #ifdef USE_TLS pthread_descr self; /* First of all init __pthread_handles[0] and [1] if needed. */ # if __LT_SPINLOCK_INIT != 0 __pthread_handles[0].h_lock = __LOCK_INITIALIZER; __pthread_handles[1].h_lock = __LOCK_INITIALIZER; # endif # ifndef SHARED /* Unlike in the dynamically linked case the dynamic linker has not taken care of initializing the TLS data structures. */ __libc_setup_tls (TLS_TCB_SIZE, TLS_TCB_ALIGN); # elif !USE___THREAD if (__builtin_expect (GL(dl_tls_dtv_slotinfo_list) == NULL, 0)) { tcbhead_t *tcbp; /* There is no actual TLS being used, so the thread register was not initialized in the dynamic linker. */ /* We need to install special hooks so that the malloc and memalign calls in _dl_tls_setup and _dl_allocate_tls won't cause full malloc initialization that will try to set up its thread state. */ extern void __libc_malloc_pthread_startup (bool first_time); __libc_malloc_pthread_startup (true); if (__builtin_expect (_dl_tls_setup (), 0) || __builtin_expect ((tcbp = _dl_allocate_tls (NULL)) == NULL, 0)) { static const char msg[] = "\ cannot allocate TLS data structures for initial thread\n"; TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, msg, sizeof msg - 1)); abort (); } const char *lossage = TLS_INIT_TP (tcbp, 0); if (__builtin_expect (lossage != NULL, 0)) { static const char msg[] = "cannot set up thread-local storage: "; const char nl = '\n'; TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, msg, sizeof msg - 1)); TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, lossage, strlen (lossage))); TEMP_FAILURE_RETRY (write_not_cancel (STDERR_FILENO, &nl, 1)); } /* Though it was allocated with libc's malloc, that was done without the user's __malloc_hook installed. A later realloc that uses the hooks might not work with that block from the plain malloc. So we record this block as unfreeable just as the dynamic linker does when it allocates the DTV before the libc malloc exists. */ GL(dl_initial_dtv) = GET_DTV (tcbp); __libc_malloc_pthread_startup (false); }
void *__nacl_tls_data_bss_initialize_from_template(void *combined_area, size_t tdb_size) { if (__nacl_tp_tdb_offset(tdb_size) != 0) { /* * This needs more work for ARM. * For now abort via null pointer dereference. */ while (1) *(volatile int *) 0; } else { void *tdb = aligned_addr(((char *) combined_area) + tls_size + SAFETY_PADDING , tls_align); return _dl_allocate_tls(tdb); } }
/* Get a TLS, returns 0 on failure. Vcores have their own TLS, and any thread * created by a user-level scheduler needs to create a TLS as well. */ void *allocate_tls(void) { void *tcb = _dl_allocate_tls(NULL); if (!tcb) return 0; #ifdef TLS_TCB_AT_TP /* Make sure the TLS is set up properly - its tcb pointer points to itself. * Keep this in sync with sysdeps/akaros/XXX/tls.h. For whatever reason, * dynamically linked programs do not need this to be redone, but statics * do. */ tcbhead_t *head = (tcbhead_t*)tcb; head->tcb = tcb; head->self = tcb; #endif return tcb; }
static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg, sigset_t * mask, int father_pid, int report_events, td_thr_events_t *event_maskp) { size_t sseg; int pid; pthread_descr new_thread; char *stack_addr; char * new_thread_bottom; pthread_t new_thread_id; char *guardaddr = NULL; size_t guardsize = 0, stksize = 0; int pagesize = __getpagesize(); int saved_errno = 0; #ifdef USE_TLS new_thread = _dl_allocate_tls (NULL); if (new_thread == NULL) return EAGAIN; # if 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 but this is hard to implement. FIXME */ if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0) return EPERM; /* Find a free segment for the thread, and allocate a stack if needed */ for (sseg = 2; ; sseg++) { if (sseg >= PTHREAD_THREADS_MAX) { #ifdef USE_TLS # if 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, &stack_addr, &new_thread_bottom, &guardaddr, &guardsize, &stksize) == 0) { #ifdef USE_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; #endif } } __pthread_handles_num++; /* Allocate new thread identifier */ pthread_threads_counter += PTHREAD_THREADS_MAX; 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 USE_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 USE_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; #if !(USE_TLS && HAVE___THREAD) new_thread->p_errnop = &new_thread->p_errno; new_thread->p_h_errnop = &new_thread->p_h_errno; new_thread->p_resp = &new_thread->p_res; #endif new_thread->p_guardaddr = guardaddr; new_thread->p_guardsize = guardsize; 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; __pthread_handles[sseg].h_bottom = new_thread_bottom; /* Determine scheduling parameters for the thread */ new_thread->p_start_args.schedpolicy = -1; if (attr != NULL) { new_thread->p_detached = attr->__detachstate; new_thread->p_userstack = attr->__stackaddr_set; switch(attr->__inheritsched) { case PTHREAD_EXPLICIT_SCHED: new_thread->p_start_args.schedpolicy = attr->__schedpolicy; memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam, 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); break; } new_thread->p_priority = new_thread->p_start_args.schedparam.sched_priority; } /* Finish setting up arguments to pthread_start_thread */ 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. */ if (report_events) { /* See whether the TD_CREATE event bit is set in any of the masks. */ int idx = __td_eventword (TD_CREATE); uint32_t mask = __td_eventmask (TD_CREATE); 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 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 _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, 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. */ new_thread->p_eventbuf.eventdata = new_thread; new_thread->p_eventbuf.eventnum = TD_CREATE; __pthread_last_event = new_thread; /* We have to set the PID here since the callback function in the debug library will need it and we cannot guarantee the child got scheduled before the debugger. */ new_thread->p_pid = pid; /* Now call the function which signals the event. */ __linuxthreads_create_event (); /* Now restart the thread. */ __pthread_unlock(new_thread->p_lock); } } } if (pid == 0) { #ifdef NEED_SEPARATE_REGISTER_STACK pid = __clone2(pthread_start_thread, (void **)new_thread_bottom, (char *)stack_addr - new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #elif _STACK_GROWS_UP pid = __clone(pthread_start_thread, (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, stack_addr, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | __pthread_sig_cancel, new_thread); #endif /* !NEED_SEPARATE_REGISTER_STACK */ saved_errno = errno; } /* Check if cloning succeeded */ if (pid == -1) { /* Free the stack if we allocated it */ if (attr == NULL || !attr->__stackaddr_set) { #ifdef NEED_SEPARATE_REGISTER_STACK size_t stacksize = ((char *)(new_thread->p_guardaddr) - new_thread_bottom); munmap((caddr_t)new_thread_bottom, 2 * stacksize + new_thread->p_guardsize); #elif _STACK_GROWS_UP # ifdef USE_TLS size_t stacksize = guardaddr - stack_addr; munmap(stack_addr, stacksize + guardsize); # else size_t stacksize = guardaddr - (char *)new_thread; munmap(new_thread, stacksize + guardsize); # endif #else # ifdef USE_TLS size_t stacksize = stack_addr - new_thread_bottom; # else size_t stacksize = (char *)(new_thread+1) - new_thread_bottom; # endif munmap(new_thread_bottom - guardsize, guardsize + stacksize); #endif } #ifdef USE_TLS # if TLS_DTV_AT_TP new_thread = (pthread_descr) ((char *) new_thread + TLS_PRE_TCB_SIZE); # endif _dl_deallocate_tls (new_thread, true); #endif __pthread_handles[sseg].h_descr = NULL; __pthread_handles[sseg].h_bottom = NULL; __pthread_handles_num--; return saved_errno; } /* 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; /* Set pid field of the new thread, in case we get there before the child starts. */ new_thread->p_pid = pid; return 0; }
/* Internal version of pthread_create. See comment in pt-internal.h. */ int __pthread_create_internal (struct __pthread **thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { int err; struct __pthread *pthread; const struct __pthread_attr *setup; sigset_t sigset; /* Allocate a new thread structure. */ err = __pthread_alloc (&pthread); if (err) goto failed; /* Use the default attributes if ATTR is NULL. */ setup = attr ? attr : &__pthread_default_attr; /* Initialize the thread state. */ pthread->state = (setup->detachstate == PTHREAD_CREATE_DETACHED ? PTHREAD_DETACHED : PTHREAD_JOINABLE); /* If the user supplied a stack, it is not our responsibility to setup a stack guard. */ if (setup->stackaddr) pthread->guardsize = 0; else pthread->guardsize = (setup->guardsize <= setup->stacksize ? setup->guardsize : setup->stacksize); /* Find a stack. There are several scenarios: if a detached thread kills itself, it has no way to deallocate its stack, thus it leaves PTHREAD->stack set to true. We try to reuse it here, however, if the user supplied a stack, we cannot use the old one. Right now, we simply deallocate it. */ if (pthread->stack) { if (setup->stackaddr != __pthread_default_attr.stackaddr) { __pthread_stack_dealloc (pthread->stackaddr, pthread->stacksize); pthread->stackaddr = setup->stackaddr; pthread->stacksize = setup->stacksize; } } else { err = __pthread_stack_alloc (&pthread->stackaddr, setup->stacksize); if (err) goto failed_stack_alloc; pthread->stacksize = setup->stacksize; pthread->stack = 1; } /* Allocate the kernel thread and other required resources. */ err = __pthread_thread_alloc (pthread); if (err) goto failed_thread_alloc; #ifdef ENABLE_TLS pthread->tcb = _dl_allocate_tls (NULL); if (!pthread->tcb) goto failed_thread_tls_alloc; pthread->tcb->tcb = pthread->tcb; #endif /* ENABLE_TLS */ /* And initialize the rest of the machine context. This may include additional machine- and system-specific initializations that prove convenient. */ err = __pthread_setup (pthread, entry_point, start_routine, arg); if (err) goto failed_setup; /* Initialize the system-specific signal state for the new thread. */ err = __pthread_sigstate_init (pthread); if (err) goto failed_sigstate; /* Set the new thread's signal mask and set the pending signals to empty. POSIX says: "The signal mask shall be inherited from the creating thread. The set of signals pending for the new thread shall be empty." If the currnet thread is not a pthread then we just inherit the process' sigmask. */ if (__pthread_num_threads == 1) err = sigprocmask (0, 0, &sigset); else err = __pthread_sigstate (_pthread_self (), 0, 0, &sigset, 0); assert_perror (err); err = __pthread_sigstate (pthread, SIG_SETMASK, &sigset, 0, 1); assert_perror (err); /* Increase the total number of threads. We do this before actually starting the new thread, since the new thread might immediately call `pthread_exit' which decreases the number of threads and calls `exit' if the number of threads reaches zero. Increasing the number of threads from within the new thread isn't an option since this thread might return and call `pthread_exit' before the new thread runs. */ __atomic_inc (&__pthread_total); /* Store a pointer to this thread in the thread ID lookup table. We could use __thread_setid, however, we only lock for reading as no other thread should be using this entry (we also assume that the store is atomic). */ pthread_rwlock_rdlock (&__pthread_threads_lock); __pthread_threads[pthread->thread - 1] = pthread; pthread_rwlock_unlock (&__pthread_threads_lock); /* At this point it is possible to guess our pthread ID. We have to make sure that all functions taking a pthread_t argument can handle the fact that this thread isn't really running yet. */ /* Schedule the new thread. */ err = __pthread_thread_start (pthread); if (err) goto failed_starting; /* At this point the new thread is up and running. */ *thread = pthread; return 0; failed_starting: __pthread_setid (pthread->thread, NULL); __atomic_dec (&__pthread_total); failed_sigstate: __pthread_sigstate_destroy (pthread); failed_setup: #ifdef ENABLE_TLS _dl_deallocate_tls (pthread->tcb, 1); failed_thread_tls_alloc: #endif /* ENABLE_TLS */ __pthread_thread_dealloc (pthread); __pthread_thread_halt (pthread); failed_thread_alloc: __pthread_stack_dealloc (pthread->stackaddr, pthread->stacksize); pthread->stack = 0; failed_stack_alloc: __pthread_dealloc (pthread); failed: return err; }