#include "pth_p.h"
long pth_version(void)
{
return PTH_VERSION;
}
intern int pth_initialized = FALSE;
#if cpp
#define pth_implicit_init() \
if (!pth_initialized) \
pth_init();
#endif
int pth_init(void)
{
pth_attr_t t_attr;
if (pth_initialized)
return_errno(FALSE, EPERM);
else
pth_initialized = TRUE;
pth_debug1("pth_init: enter");
pth_scheduler_init();
t_attr = pth_attr_new();
pth_attr_set(t_attr, PTH_ATTR_PRIO, PTH_PRIO_MAX);
pth_attr_set(t_attr, PTH_ATTR_NAME, "**SCHEDULER**");
pth_attr_set(t_attr, PTH_ATTR_JOINABLE, FALSE);
pth_attr_set(t_attr, PTH_ATTR_CANCEL_STATE, PTH_CANCEL_DISABLE);
pth_attr_set(t_attr, PTH_ATTR_STACK_SIZE, 64*1024);
pth_attr_set(t_attr, PTH_ATTR_STACK_ADDR, NULL);
pth_sched = pth_spawn(t_attr, pth_scheduler, NULL);
if (pth_sched == NULL) {
errno_shield {
pth_attr_destroy(t_attr);
pth_scheduler_kill();
}
return FALSE;
}
pth_attr_set(t_attr, PTH_ATTR_PRIO, PTH_PRIO_STD);
pth_attr_set(t_attr, PTH_ATTR_NAME, "main");
pth_attr_set(t_attr, PTH_ATTR_JOINABLE, TRUE);
pth_attr_set(t_attr, PTH_ATTR_CANCEL_STATE, PTH_CANCEL_ENABLE|PTH_CANCEL_DEFERRED);
pth_attr_set(t_attr, PTH_ATTR_STACK_SIZE, 0 );
pth_attr_set(t_attr, PTH_ATTR_STACK_ADDR, NULL);
pth_main = pth_spawn(t_attr, (void *(*)(void *))(-1), NULL);
if (pth_main == NULL) {
errno_shield {
pth_attr_destroy(t_attr);
pth_scheduler_kill();
}
return FALSE;
}
pth_attr_destroy(t_attr);
pth_current = pth_sched;
pth_mctx_switch(&pth_main->mctx, &pth_sched->mctx);
pth_debug1("pth_init: leave");
return TRUE;
}
int pth_kill(void)
{
if (pth_current != pth_main)
return_errno(FALSE, EPERM);
pth_debug1("pth_kill: enter");
pth_thread_cleanup(pth_main);
pth_scheduler_kill();
pth_initialized = FALSE;
pth_tcb_free(pth_sched);
pth_tcb_free(pth_main);
pth_debug1("pth_kill: leave");
return TRUE;
}
long pth_ctrl(unsigned long query, ...)
{
long rc;
va_list ap;
rc = 0;
va_start(ap, query);
if (query & PTH_CTRL_GETTHREADS) {
if (query & PTH_CTRL_GETTHREADS_NEW)
rc += pth_pqueue_elements(&pth_NQ);
if (query & PTH_CTRL_GETTHREADS_READY)
rc += pth_pqueue_elements(&pth_RQ);
if (query & PTH_CTRL_GETTHREADS_RUNNING)
rc += 1;
if (query & PTH_CTRL_GETTHREADS_WAITING)
rc += pth_pqueue_elements(&pth_WQ);
if (query & PTH_CTRL_GETTHREADS_SUSPENDED)
rc += pth_pqueue_elements(&pth_SQ);
if (query & PTH_CTRL_GETTHREADS_DEAD)
rc += pth_pqueue_elements(&pth_DQ);
}
else if (query & PTH_CTRL_GETAVLOAD) {
float *pload = va_arg(ap, float *);
*pload = pth_loadval;
}
else if (query & PTH_CTRL_GETPRIO) {
pth_t t = va_arg(ap, pth_t);
rc = t->prio;
}
else if (query & PTH_CTRL_GETNAME) {
pth_t t = va_arg(ap, pth_t);
rc = (long)t->name;
}
else if (query & PTH_CTRL_DUMPSTATE) {
FILE *fp = va_arg(ap, FILE *);
pth_dumpstate(fp);
}
else
rc = -1;
va_end(ap);
if (rc == -1)
return_errno(-1, EINVAL);
return rc;
}
static void pth_spawn_trampoline(void)
{
void *data;
data = (*pth_current->start_func)(pth_current->start_arg);
pth_exit(data);
abort();
}
pth_t pth_spawn(pth_attr_t attr, void *(*func)(void *), void *arg)
{
pth_t t;
unsigned int stacksize;
void *stackaddr;
pth_time_t ts;
pth_debug1("pth_spawn: enter");
if (func == NULL)
return_errno(NULL, EINVAL);
if (func == (void *(*)(void *))(-1))
func = NULL;
stacksize = (attr == PTH_ATTR_DEFAULT ? 64*1024 : attr->a_stacksize);
stackaddr = (attr == PTH_ATTR_DEFAULT ? NULL : attr->a_stackaddr);
if ((t = pth_tcb_alloc(stacksize, stackaddr)) == NULL)
return NULL;
if (attr != PTH_ATTR_DEFAULT) {
t->prio = attr->a_prio;
t->joinable = attr->a_joinable;
t->cancelstate = attr->a_cancelstate;
pth_util_cpystrn(t->name, attr->a_name, PTH_TCB_NAMELEN);
}
else if (pth_current != NULL) {
t->prio = pth_current->prio;
t->joinable = pth_current->joinable;
t->cancelstate = pth_current->cancelstate;
pth_snprintf(t->name, PTH_TCB_NAMELEN, "%s.child@%d=0x%lx",
pth_current->name, (unsigned int)time(NULL),
(unsigned long)pth_current);
}
else {
t->prio = PTH_PRIO_STD;
t->joinable = TRUE;
t->cancelstate = PTH_CANCEL_DEFAULT;
pth_snprintf(t->name, PTH_TCB_NAMELEN,
"user/%x", (unsigned int)time(NULL));
}
pth_time_set(&ts, PTH_TIME_NOW);
pth_time_set(&t->spawned, &ts);
pth_time_set(&t->lastran, &ts);
pth_time_set(&t->running, PTH_TIME_ZERO);
t->events = NULL;
sigemptyset(&t->sigpending);
t->sigpendcnt = 0;
t->start_func = func;
t->start_arg = arg;
t->join_arg = NULL;
t->data_value = NULL;
t->data_count = 0;
t->cancelreq = FALSE;
t->cleanups = NULL;
pth_ring_init(&t->mutexring);
if (t->stacksize > 0) {
if (!pth_mctx_set(&t->mctx, pth_spawn_trampoline,
t->stack, ((char *)t->stack+t->stacksize))) {
errno_shield { pth_tcb_free(t); }
return NULL;
}
}
if (func != pth_scheduler) {
t->state = PTH_STATE_NEW;
pth_pqueue_insert(&pth_NQ, t->prio, t);
}
pth_debug1("pth_spawn: leave");
return t;
}
pth_t pth_self(void)
{
return pth_current;
}
int pth_raise(pth_t t, int sig)
{
struct sigaction sa;
if (t == NULL || t == pth_current || (sig < 0 || sig > PTH_NSIG))
return_errno(FALSE, EINVAL);
if (sig == 0)
return pth_thread_exists(t);
else {
if (sigaction(sig, NULL, &sa) != 0)
return FALSE;
if (sa.sa_handler == SIG_IGN)
return TRUE;
if (!sigismember(&t->sigpending, sig)) {
sigaddset(&t->sigpending, sig);
t->sigpendcnt++;
}
pth_yield(t);
return TRUE;
}
}
intern int pth_thread_exists(pth_t t)
{
if (!pth_pqueue_contains(&pth_NQ, t))
if (!pth_pqueue_contains(&pth_RQ, t))
if (!pth_pqueue_contains(&pth_WQ, t))
if (!pth_pqueue_contains(&pth_SQ, t))
if (!pth_pqueue_contains(&pth_DQ, t))
return_errno(FALSE, ESRCH);
return TRUE;
}
intern void pth_thread_cleanup(pth_t thread)
{
if (thread->cleanups != NULL)
pth_cleanup_popall(thread, TRUE);
if (thread->data_value != NULL)
pth_key_destroydata(thread);
pth_mutex_releaseall(thread);
return;
}
static int pth_exit_cb(void *arg)
{
int rc;
rc = 0;
rc += pth_pqueue_elements(&pth_NQ);
rc += pth_pqueue_elements(&pth_RQ);
rc += pth_pqueue_elements(&pth_WQ);
rc += pth_pqueue_elements(&pth_SQ);
rc += pth_pqueue_elements(&pth_DQ);
if (rc == 1 )
return TRUE;
else
return FALSE;
}
void pth_exit(void *value)
{
pth_event_t ev;
pth_debug2("pth_exit: marking thread \"%s\" as dead", pth_current->name);
if (pth_current == pth_main) {
ev = pth_event(PTH_EVENT_FUNC, pth_exit_cb);
pth_wait(ev);
pth_event_free(ev, PTH_FREE_THIS);
}
pth_thread_cleanup(pth_current);
pth_current->join_arg = value;
pth_current->state = PTH_STATE_DEAD;
if (pth_current != pth_main) {
pth_debug2("pth_exit: switching from thread \"%s\" to scheduler", pth_current->name);
pth_mctx_switch(&pth_current->mctx, &pth_sched->mctx);
abort();
}
else {
pth_kill();
exit((int)((long)value));
abort();
}
}
int pth_join(pth_t tid, void **value)
{
pth_event_t ev;
static pth_key_t ev_key = PTH_KEY_INIT;
pth_debug2("pth_join: joining thread \"%s\"", tid == NULL ? "-ANY-" : tid->name);
if (tid == pth_current)
return_errno(FALSE, EDEADLK);
if (tid != NULL && !tid->joinable)
return_errno(FALSE, EINVAL);
if (pth_ctrl(PTH_CTRL_GETTHREADS) == 1)
return_errno(FALSE, EDEADLK);
if (tid == NULL)
tid = pth_pqueue_head(&pth_DQ);
if (tid == NULL || (tid != NULL && tid->state != PTH_STATE_DEAD)) {
ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD|PTH_MODE_STATIC, &ev_key, tid);
pth_wait(ev);
}
if (tid == NULL)
tid = pth_pqueue_head(&pth_DQ);
if (tid == NULL || (tid != NULL && tid->state != PTH_STATE_DEAD))
return_errno(FALSE, EIO);
if (value != NULL)
*value = tid->join_arg;
pth_pqueue_delete(&pth_DQ, tid);
pth_tcb_free(tid);
return TRUE;
}
int pth_yield(pth_t to)
{
pth_pqueue_t *q = NULL;
pth_debug2("pth_yield: enter from thread \"%s\"", pth_current->name);
if (to != NULL) {
switch (to->state) {
case PTH_STATE_NEW: q = &pth_NQ; break;
case PTH_STATE_READY: q = &pth_RQ; break;
default: q = NULL;
}
if (q == NULL || !pth_pqueue_contains(q, to))
return_errno(FALSE, EINVAL);
}
if (to != NULL && q != NULL)
pth_pqueue_favorite(q, to);
if (to != NULL)
pth_debug2("pth_yield: give up control to scheduler "
"in favour of thread \"%s\"", to->name);
else
pth_debug1("pth_yield: give up control to scheduler");
pth_mctx_switch(&pth_current->mctx, &pth_sched->mctx);
pth_debug1("pth_yield: got back control from scheduler");
pth_debug2("pth_yield: leave to thread \"%s\"", pth_current->name);
return TRUE;
}
int pth_suspend(pth_t t)
{
pth_pqueue_t *q;
if (t == NULL)
return_errno(FALSE, EINVAL);
if (t == pth_sched || t == pth_current)
return_errno(FALSE, EPERM);
switch (t->state) {
case PTH_STATE_NEW: q = &pth_NQ; break;
case PTH_STATE_READY: q = &pth_RQ; break;
case PTH_STATE_WAITING: q = &pth_WQ; break;
default: q = NULL;
}
if (q == NULL)
return_errno(FALSE, EPERM);
if (!pth_pqueue_contains(q, t))
return_errno(FALSE, ESRCH);
pth_pqueue_delete(q, t);
pth_pqueue_insert(&pth_SQ, PTH_PRIO_STD, t);
pth_debug2("pth_suspend: suspend thread \"%s\"\n", t->name);
return TRUE;
}
int pth_resume(pth_t t)
{
pth_pqueue_t *q;
if (t == NULL)
return_errno(FALSE, EINVAL);
if (t == pth_sched || t == pth_current)
return_errno(FALSE, EPERM);
if (!pth_pqueue_contains(&pth_SQ, t))
return_errno(FALSE, EPERM);
pth_pqueue_delete(&pth_SQ, t);
switch (t->state) {
case PTH_STATE_NEW: q = &pth_NQ; break;
case PTH_STATE_READY: q = &pth_RQ; break;
case PTH_STATE_WAITING: q = &pth_WQ; break;
default: q = NULL;
}
pth_pqueue_insert(q, PTH_PRIO_STD, t);
pth_debug2("pth_resume: resume thread \"%s\"\n", t->name);
return TRUE;
}
int pth_fdmode(int fd, int newmode)
{
int fdmode;
int oldmode;
if ((fdmode = fcntl(fd, F_GETFL, NULL)) == -1)
oldmode = PTH_FDMODE_ERROR;
else if (fdmode & O_NONBLOCKING)
oldmode = PTH_FDMODE_NONBLOCK;
else
oldmode = PTH_FDMODE_BLOCK;
if (oldmode == PTH_FDMODE_BLOCK && newmode == PTH_FDMODE_NONBLOCK)
fcntl(fd, F_SETFL, (fdmode | O_NONBLOCKING));
if (oldmode == PTH_FDMODE_NONBLOCK && newmode == PTH_FDMODE_BLOCK)
fcntl(fd, F_SETFL, (fdmode & ~(O_NONBLOCKING)));
return oldmode;
}
int pth_nap(pth_time_t naptime)
{
pth_time_t until;
pth_event_t ev;
static pth_key_t ev_key = PTH_KEY_INIT;
if (pth_time_cmp(&naptime, PTH_TIME_ZERO) == 0)
return_errno(FALSE, EINVAL);
pth_time_set(&until, PTH_TIME_NOW);
pth_time_add(&until, &naptime);
ev = pth_event(PTH_EVENT_TIME|PTH_MODE_STATIC, &ev_key, until);
pth_wait(ev);
return TRUE;
}
int pth_once(pth_once_t *oncectrl, void (*constructor)(void *), void *arg)
{
if (oncectrl == NULL || constructor == NULL)
return_errno(FALSE, EINVAL);
if (*oncectrl != TRUE)
constructor(arg);
*oncectrl = TRUE;
return TRUE;
}