#define _WANT_P_OSREL
#include "namespace.h"
#include <sys/types.h>
#include <sys/rtprio.h>
#include <sys/signalvar.h>
#include <sys/exterrvar.h>
#include <errno.h>
#include <link.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <pthread.h>
#include <pthread_np.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "thr_private.h"
int __getosreldate(void);
static int create_stack(struct pthread_attr *pattr);
static void thread_start(struct pthread *curthread);
int __thr_new_flags = THR_C_RUNTIME;
__weak_reference(_pthread_create, pthread_create);
int
_pthread_create(pthread_t * __restrict thread,
const pthread_attr_t * __restrict attr, void *(*start_routine) (void *),
void * __restrict arg)
{
struct pthread *curthread, *new_thread;
struct thr_param param;
struct sched_param sched_param;
struct rtprio rtp;
sigset_t set, oset;
cpuset_t *cpusetp;
int i, cpusetsize, create_suspended, locked, old_stack_prot, ret;
cpusetp = NULL;
ret = cpusetsize = 0;
_thr_check_init();
if (_thr_isthreaded() == 0) {
_malloc_first_thread();
_thr_setthreaded(1);
}
curthread = _get_curthread();
if ((new_thread = _thr_alloc(curthread)) == NULL)
return (EAGAIN);
memset(¶m, 0, sizeof(param));
if (attr == NULL || *attr == NULL)
new_thread->attr = _pthread_attr_default;
else {
new_thread->attr = *(*attr);
cpusetp = new_thread->attr.cpuset;
cpusetsize = new_thread->attr.cpusetsize;
new_thread->attr.cpuset = NULL;
new_thread->attr.cpusetsize = 0;
}
if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) {
if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM)
new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM;
else
new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM;
new_thread->attr.prio = curthread->attr.prio;
new_thread->attr.sched_policy = curthread->attr.sched_policy;
}
new_thread->tid = TID_TERMINATED;
old_stack_prot = _rtld_get_stack_prot();
if (create_stack(&new_thread->attr) != 0) {
_thr_free(curthread, new_thread);
return (EAGAIN);
}
new_thread->magic = THR_MAGIC;
new_thread->start_routine = start_routine;
new_thread->arg = arg;
new_thread->cancel_enable = 1;
new_thread->cancel_async = 0;
for (i = 0; i < TMQ_NITEMS; i++)
TAILQ_INIT(&new_thread->mq[i]);
if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) {
new_thread->flags = THR_FLAGS_NEED_SUSPEND;
create_suspended = 1;
} else {
create_suspended = 0;
}
new_thread->state = PS_RUNNING;
if (new_thread->attr.flags & PTHREAD_CREATE_DETACHED)
new_thread->flags |= THR_FLAGS_DETACHED;
new_thread->refcount = 1;
_thr_link(curthread, new_thread);
if (old_stack_prot != _rtld_get_stack_prot())
_thr_stack_fix_protection(new_thread);
*thread = new_thread;
if (SHOULD_REPORT_EVENT(curthread, TD_CREATE) || cpusetp != NULL) {
THR_THREAD_LOCK(curthread, new_thread);
locked = 1;
} else
locked = 0;
param.start_func = (void (*)(void *)) thread_start;
param.arg = new_thread;
param.stack_base = new_thread->attr.stackaddr_attr;
param.stack_size = new_thread->attr.stacksize_attr;
param.tls_base = (char *)new_thread->tcb;
param.tls_size = sizeof(struct tcb);
param.child_tid = &new_thread->tid;
param.parent_tid = &new_thread->tid;
param.flags = __thr_new_flags;
if (new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM)
param.flags |= THR_SYSTEM_SCOPE;
if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED)
param.rtp = NULL;
else {
sched_param.sched_priority = new_thread->attr.prio;
_schedparam_to_rtp(new_thread->attr.sched_policy,
&sched_param, &rtp);
param.rtp = &rtp;
}
if (create_suspended) {
SIGFILLSET(set);
SIGDELSET(set, SIGTRAP);
__sys_sigprocmask(SIG_SETMASK, &set, &oset);
new_thread->sigmask = oset;
SIGDELSET(new_thread->sigmask, SIGCANCEL);
}
ret = thr_new(¶m, sizeof(param));
if (ret != 0) {
ret = errno;
if (ret == EPROCLIM)
ret = EAGAIN;
}
if (create_suspended)
__sys_sigprocmask(SIG_SETMASK, &oset, NULL);
if (ret != 0) {
if (!locked)
THR_THREAD_LOCK(curthread, new_thread);
new_thread->state = PS_DEAD;
new_thread->tid = TID_TERMINATED;
new_thread->flags |= THR_FLAGS_DETACHED;
new_thread->refcount--;
if (new_thread->flags & THR_FLAGS_NEED_SUSPEND) {
new_thread->cycle++;
_thr_umtx_wake(&new_thread->cycle, INT_MAX, 0);
}
_thr_try_gc(curthread, new_thread);
atomic_add_int(&_thread_active_threads, -1);
} else if (locked) {
if (cpusetp != NULL) {
if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
TID(new_thread), cpusetsize, cpusetp)) {
ret = errno;
new_thread->force_exit = 1;
new_thread->flags |= THR_FLAGS_DETACHED;
_thr_try_gc(curthread, new_thread);
goto out;
}
}
_thr_report_creation(curthread, new_thread);
THR_THREAD_UNLOCK(curthread, new_thread);
}
out:
if (ret != 0)
*thread = NULL;
return (ret);
}
static int
create_stack(struct pthread_attr *pattr)
{
int ret;
if ((pattr->stackaddr_attr) != NULL) {
pattr->guardsize_attr = 0;
pattr->flags |= THR_STACK_USER;
ret = 0;
}
else
ret = _thr_stack_alloc(pattr);
return (ret);
}
static void
thread_start(struct pthread *curthread)
{
sigset_t set;
if (curthread->attr.suspend == THR_CREATE_SUSPENDED)
set = curthread->sigmask;
_thr_signal_block_setup(curthread);
THR_LOCK(curthread);
THR_UNLOCK(curthread);
if (curthread->force_exit)
_pthread_exit(PTHREAD_CANCELED);
if (curthread->attr.suspend == THR_CREATE_SUSPENDED) {
#if 0
_thr_ast(curthread);
#endif
__sys_sigprocmask(SIG_SETMASK, &set, NULL);
}
#ifdef _PTHREAD_FORCED_UNWIND
curthread->unwind_stackend = (char *)curthread->attr.stackaddr_attr +
curthread->attr.stacksize_attr;
#endif
curthread->uexterr.ver = UEXTERROR_VER;
if (__getosreldate() >= P_OSREL_EXTERRCTL)
exterrctl(EXTERRCTL_ENABLE, 0, &curthread->uexterr);
_pthread_exit(curthread->start_routine(curthread->arg));
PANIC("Thread has resumed after exit");
}