/* * Threaded process initialization. * * This is only called under two conditions: * * 1) Some thread routines have detected that the library hasn't yet * been initialized (_thr_initial == NULL && curthread == NULL), or * * 2) An explicit call to reinitialize after a fork (indicated * by curthread != NULL) */ void _libpthread_init(struct pthread *curthread) { int fd; /* Check if this function has already been called: */ if ((_thr_initial != NULL) && (curthread == NULL)) /* Only initialize the threaded application once. */ return; /* * Make gcc quiescent about {,libgcc_}references not being * referenced: */ if ((references[0] == NULL) || (libgcc_references[0] == NULL)) PANIC("Failed loading mandatory references in _thread_init"); /* Pull debug symbols in for static binary */ _thread_state_running = PS_RUNNING; /* * Check the size of the jump table to make sure it is preset * with the correct number of entries. */ if (sizeof(jmp_table) != (sizeof(pthread_func_t) * PJT_MAX * 2)) PANIC("Thread jump table not properly initialized"); memcpy(__thr_jtable, jmp_table, sizeof(jmp_table)); /* * Check for the special case of this process running as * or in place of init as pid = 1: */ if ((_thr_pid = getpid()) == 1) { /* * Setup a new session for this process which is * assumed to be running as root. */ if (setsid() == -1) PANIC("Can't set session ID"); if (revoke(_PATH_CONSOLE) != 0) PANIC("Can't revoke console"); if ((fd = __sys_open(_PATH_CONSOLE, O_RDWR)) < 0) PANIC("Can't open console"); if (setlogin("root") == -1) PANIC("Can't set login to root"); if (__sys_ioctl(fd, TIOCSCTTY, (char *) NULL) == -1) PANIC("Can't set controlling terminal"); } /* Initialize pthread private data. */ init_private(); _kse_init(); /* Initialize the initial kse and kseg. */ _kse_initial = _kse_alloc(NULL, _thread_scope_system > 0); if (_kse_initial == NULL) PANIC("Can't allocate initial kse."); _kse_initial->k_kseg = _kseg_alloc(NULL); if (_kse_initial->k_kseg == NULL) PANIC("Can't allocate initial kseg."); _kse_initial->k_kseg->kg_flags |= KGF_SINGLE_THREAD; _kse_initial->k_schedq = &_kse_initial->k_kseg->kg_schedq; TAILQ_INSERT_TAIL(&_kse_initial->k_kseg->kg_kseq, _kse_initial, k_kgqe); _kse_initial->k_kseg->kg_ksecount = 1; /* Set the initial thread. */ if (curthread == NULL) { /* Create and initialize the initial thread. */ curthread = _thr_alloc(NULL); if (curthread == NULL) PANIC("Can't allocate initial thread"); _thr_initial = curthread; init_main_thread(curthread); } else { /* * The initial thread is the current thread. It is * assumed that the current thread is already initialized * because it is left over from a fork(). */ _thr_initial = curthread; } _kse_initial->k_kseg->kg_threadcount = 0; _thr_initial->kse = _kse_initial; _thr_initial->kseg = _kse_initial->k_kseg; _thr_initial->active = 1; /* * Add the thread to the thread list and to the KSEG's thread * queue. */ THR_LIST_ADD(_thr_initial); KSEG_THRQ_ADD(_kse_initial->k_kseg, _thr_initial); /* Setup the KSE/thread specific data for the current KSE/thread. */ _thr_initial->kse->k_curthread = _thr_initial; _kcb_set(_thr_initial->kse->k_kcb); _tcb_set(_thr_initial->kse->k_kcb, _thr_initial->tcb); _thr_initial->kse->k_flags |= KF_INITIALIZED; _thr_signal_init(); _kse_critical_leave(&_thr_initial->tcb->tcb_tmbx); /* * activate threaded mode as soon as possible if we are * being debugged */ if (_libkse_debug) _kse_setthreaded(1); }
/* * Some notes on new thread creation and first time initializion * to enable multi-threading. * * There are basically two things that need to be done. * * 1) The internal library variables must be initialized. * 2) Upcalls need to be enabled to allow multiple threads * to be run. * * The first may be done as a result of other pthread functions * being called. When _thr_initial is null, _libpthread_init is * called to initialize the internal variables; this also creates * or sets the initial thread. It'd be nice to automatically * have _libpthread_init called on program execution so we don't * have to have checks throughout the library. * * The second part is only triggered by the creation of the first * thread (other than the initial/main thread). If the thread * being created is a scope system thread, then a new KSE/KSEG * pair needs to be allocated. Also, if upcalls haven't been * enabled on the initial thread's KSE, they must be now that * there is more than one thread; this could be delayed until * the initial KSEG has more than one thread. */ int _pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg) { struct pthread *curthread, *new_thread; struct kse *kse = NULL; struct kse_group *kseg = NULL; kse_critical_t crit; int ret = 0; if (_thr_initial == NULL) _libpthread_init(NULL); /* * Turn on threaded mode, if failed, it is unnecessary to * do further work. */ if (_kse_isthreaded() == 0 && _kse_setthreaded(1)) { return (EAGAIN); } curthread = _get_curthread(); /* * Allocate memory for the thread structure. * Some functions use malloc, so don't put it * in a critical region. */ if ((new_thread = _thr_alloc(curthread)) == NULL) { /* Insufficient memory to create a thread: */ ret = EAGAIN; } else { /* Check if default thread attributes are required: */ if (attr == NULL || *attr == NULL) /* Use the default thread attributes: */ new_thread->attr = _pthread_attr_default; else { new_thread->attr = *(*attr); if ((*attr)->sched_inherit == PTHREAD_INHERIT_SCHED) { /* inherit scheduling contention scop */ if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; else new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; /* * scheduling policy and scheduling parameters will be * inherited in following code. */ } } if (_thread_scope_system > 0) new_thread->attr.flags |= PTHREAD_SCOPE_SYSTEM; else if ((_thread_scope_system < 0) && (thread != &_thr_sig_daemon)) new_thread->attr.flags &= ~PTHREAD_SCOPE_SYSTEM; if (create_stack(&new_thread->attr) != 0) { /* Insufficient memory to create a stack: */ ret = EAGAIN; _thr_free(curthread, new_thread); } else if (((new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) != 0) && (((kse = _kse_alloc(curthread, 1)) == NULL) || ((kseg = _kseg_alloc(curthread)) == NULL))) { /* Insufficient memory to create a new KSE/KSEG: */ ret = EAGAIN; if (kse != NULL) { kse->k_kcb->kcb_kmbx.km_flags |= KMF_DONE; _kse_free(curthread, kse); } free_stack(&new_thread->attr); _thr_free(curthread, new_thread); } else { if (kseg != NULL) { /* Add the KSE to the KSEG's list of KSEs. */ TAILQ_INSERT_HEAD(&kseg->kg_kseq, kse, k_kgqe); kseg->kg_ksecount = 1; kse->k_kseg = kseg; kse->k_schedq = &kseg->kg_schedq; } /* * Write a magic value to the thread structure * to help identify valid ones: */ new_thread->magic = THR_MAGIC; new_thread->slice_usec = -1; new_thread->start_routine = start_routine; new_thread->arg = arg; new_thread->cancelflags = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; /* No thread is wanting to join to this one: */ new_thread->joiner = NULL; /* * Initialize the machine context. * Enter a critical region to get consistent context. */ crit = _kse_critical_enter(); THR_GETCONTEXT(&new_thread->tcb->tcb_tmbx.tm_context); /* Initialize the thread for signals: */ new_thread->sigmask = curthread->sigmask; _kse_critical_leave(crit); new_thread->tcb->tcb_tmbx.tm_udata = new_thread; new_thread->tcb->tcb_tmbx.tm_context.uc_sigmask = new_thread->sigmask; new_thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_size = new_thread->attr.stacksize_attr; new_thread->tcb->tcb_tmbx.tm_context.uc_stack.ss_sp = new_thread->attr.stackaddr_attr; makecontext(&new_thread->tcb->tcb_tmbx.tm_context, (void (*)(void))thread_start, 3, new_thread, start_routine, arg); /* * Check if this thread is to inherit the scheduling * attributes from its parent: */ if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) { /* * Copy the scheduling attributes. * Lock the scheduling lock to get consistent * scheduling parameters. */ THR_SCHED_LOCK(curthread, curthread); new_thread->base_priority = curthread->base_priority & ~THR_SIGNAL_PRIORITY; new_thread->attr.prio = curthread->base_priority & ~THR_SIGNAL_PRIORITY; new_thread->attr.sched_policy = curthread->attr.sched_policy; THR_SCHED_UNLOCK(curthread, curthread); } else { /* * Use just the thread priority, leaving the * other scheduling attributes as their * default values: */ new_thread->base_priority = new_thread->attr.prio; } new_thread->active_priority = new_thread->base_priority; new_thread->inherited_priority = 0; /* Initialize the mutex queue: */ TAILQ_INIT(&new_thread->mutexq); /* Initialise hooks in the thread structure: */ new_thread->specific = NULL; new_thread->specific_data_count = 0; new_thread->cleanup = NULL; new_thread->flags = 0; new_thread->tlflags = 0; new_thread->sigbackout = NULL; new_thread->continuation = NULL; new_thread->wakeup_time.tv_sec = -1; new_thread->lock_switch = 0; sigemptyset(&new_thread->sigpend); new_thread->check_pending = 0; new_thread->locklevel = 0; new_thread->rdlock_count = 0; new_thread->sigstk.ss_sp = 0; new_thread->sigstk.ss_size = 0; new_thread->sigstk.ss_flags = SS_DISABLE; new_thread->oldsigmask = NULL; if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) { new_thread->state = PS_SUSPENDED; new_thread->flags = THR_FLAGS_SUSPENDED; } else new_thread->state = PS_RUNNING; /* * System scope threads have their own kse and * kseg. Process scope threads are all hung * off the main process kseg. */ if ((new_thread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) { new_thread->kseg = _kse_initial->k_kseg; new_thread->kse = _kse_initial; } else { kse->k_curthread = NULL; kse->k_kseg->kg_flags |= KGF_SINGLE_THREAD; new_thread->kse = kse; new_thread->kseg = kse->k_kseg; kse->k_kcb->kcb_kmbx.km_udata = kse; kse->k_kcb->kcb_kmbx.km_curthread = NULL; } /* * Schedule the new thread starting a new KSEG/KSE * pair if necessary. */ ret = _thr_schedule_add(curthread, new_thread); if (ret != 0) free_thread(curthread, new_thread); else { /* Return a pointer to the thread structure: */ (*thread) = new_thread; } } } /* Return the status: */ return (ret); }