int pth_rwlock_acquire(pth_rwlock_t *rwlock, int op, int tryonly, pth_event_t ev_extra) { /* consistency checks */ if (rwlock == NULL) return pth_error(FALSE, EINVAL); if (!(rwlock->rw_state & PTH_RWLOCK_INITIALIZED)) return pth_error(FALSE, EDEADLK); /* acquire lock */ if (op == PTH_RWLOCK_RW) { /* read-write lock is simple */ if (!pth_mutex_acquire(&(rwlock->rw_mutex_rw), tryonly, ev_extra)) return FALSE; rwlock->rw_mode = PTH_RWLOCK_RW; } else { /* read-only lock is more complicated to get right */ if (!pth_mutex_acquire(&(rwlock->rw_mutex_rd), tryonly, ev_extra)) return FALSE; rwlock->rw_readers++; if (rwlock->rw_readers == 1) { if (!pth_mutex_acquire(&(rwlock->rw_mutex_rw), tryonly, ev_extra)) { rwlock->rw_readers--; pth_shield { pth_mutex_release(&(rwlock->rw_mutex_rd)); } return FALSE; } } rwlock->rw_mode = PTH_RWLOCK_RD; pth_mutex_release(&(rwlock->rw_mutex_rd)); }
/* create user-space context structure */ int pth_uctx_create( pth_uctx_t *puctx) { pth_uctx_t uctx; /* argument sanity checking */ if (puctx == NULL) return pth_error(FALSE, EINVAL); /* allocate the context structure */ if ((uctx = (pth_uctx_t)malloc(sizeof(struct pth_uctx_st))) == NULL) return pth_error(FALSE, errno); /* initialize the context structure */ uctx->uc_stack_own = FALSE; uctx->uc_stack_ptr = NULL; uctx->uc_stack_len = 0; uctx->uc_mctx_set = FALSE; memset((void *)&uctx->uc_mctx, 0, sizeof(pth_mctx_t)); /* pass result to caller */ *puctx = uctx; return TRUE; }
/* initialize the scheduler ingredients */ intern int pth_scheduler_init(void) { /* create the internal signal pipe */ if (pipe(pth_sigpipe) == -1) return pth_error(FALSE, errno); if (pth_fdmode(pth_sigpipe[0], PTH_FDMODE_NONBLOCK) == PTH_FDMODE_ERROR) return pth_error(FALSE, errno); if (pth_fdmode(pth_sigpipe[1], PTH_FDMODE_NONBLOCK) == PTH_FDMODE_ERROR) return pth_error(FALSE, errno); /* initialize the essential threads */ pth_sched = NULL; pth_current = NULL; /* initalize the thread queues */ pth_pqueue_init(&pth_NQ); pth_pqueue_init(&pth_RQ); pth_pqueue_init(&pth_WQ); pth_pqueue_init(&pth_SQ); pth_pqueue_init(&pth_DQ); /* initialize scheduling hints */ pth_favournew = 1; /* the default is the original behaviour */ /* initialize load support */ pth_loadval = 1.0; pth_time_set(&pth_loadticknext, PTH_TIME_NOW); return TRUE; }
int pth_mutex_release(pth_mutex_t *mutex) { /* consistency checks */ if (mutex == NULL) return pth_error(FALSE, EINVAL); if (!(mutex->mx_state & PTH_MUTEX_INITIALIZED)) return pth_error(FALSE, EDEADLK); if (!(mutex->mx_state & PTH_MUTEX_LOCKED)) return pth_error(FALSE, EDEADLK); #define DISABLE_SECURITY_CHECK #ifndef DISABLE_SECURITY_CHECK if (mutex->mx_owner != pth_current) return pth_error(FALSE, EACCES); #endif /* decrement recursion counter and release mutex */ mutex->mx_count--; if (mutex->mx_count <= 0) { mutex->mx_state &= ~(PTH_MUTEX_LOCKED); mutex->mx_owner = NULL; mutex->mx_count = 0; pth_ring_delete(&(pth_current->mutexring), &(mutex->mx_node)); } return TRUE; }
int pth_mutex_acquire(pth_mutex_t *mutex, int tryonly, pth_event_t ev_extra) { static pth_key_t ev_key = PTH_KEY_INIT; pth_event_t ev; pth_debug2("pth_mutex_acquire: called from thread \"%s\"", pth_current->name); /* consistency checks */ if (mutex == NULL) return pth_error(FALSE, EINVAL); if (!(mutex->mx_state & PTH_MUTEX_INITIALIZED)) return pth_error(FALSE, EDEADLK); /* still not locked, so simply acquire mutex? */ if (!(mutex->mx_state & PTH_MUTEX_LOCKED)) { mutex->mx_state |= PTH_MUTEX_LOCKED; mutex->mx_owner = pth_current; mutex->mx_count = 1; pth_ring_append(&(pth_current->mutexring), &(mutex->mx_node)); pth_debug1("pth_mutex_acquire: immediately locking mutex"); return TRUE; } /* already locked by caller? */ if (mutex->mx_count >= 1 && mutex->mx_owner == pth_current) { /* recursive lock */ mutex->mx_count++; pth_debug1("pth_mutex_acquire: recursive locking"); return TRUE; } /* should we just tryonly? */ if (tryonly) return pth_error(FALSE, EBUSY); /* else wait for mutex to become unlocked.. */ pth_debug1("pth_mutex_acquire: wait until mutex is unlocked"); for (;;) { ev = pth_event(PTH_EVENT_MUTEX|PTH_MODE_STATIC, &ev_key, mutex); if (ev_extra != NULL) pth_event_concat(ev, ev_extra, NULL); pth_wait(ev); if (ev_extra != NULL) { pth_event_isolate(ev); if (pth_event_status(ev) == PTH_STATUS_PENDING) return pth_error(FALSE, EINTR); } if (!(mutex->mx_state & PTH_MUTEX_LOCKED)) break; } /* now it's again unlocked, so acquire mutex */ pth_debug1("pth_mutex_acquire: locking mutex"); mutex->mx_state |= PTH_MUTEX_LOCKED; mutex->mx_owner = pth_current; mutex->mx_count = 1; pth_ring_append(&(pth_current->mutexring), &(mutex->mx_node)); return TRUE; }
/* make setup of user-space context structure */ int pth_uctx_make( pth_uctx_t uctx, char *sk_addr, size_t sk_size, const sigset_t *sigmask, void (*start_func)(void *), void *start_arg, pth_uctx_t uctx_after) { pth_mctx_t mctx_parent; sigset_t ss; /* argument sanity checking */ if (uctx == NULL || start_func == NULL || sk_size < 16*1024) return pth_error(FALSE, EINVAL); /* configure run-time stack */ if (sk_addr == NULL) { if ((sk_addr = (char *)malloc(sk_size)) == NULL) return pth_error(FALSE, errno); uctx->uc_stack_own = TRUE; } else uctx->uc_stack_own = FALSE; uctx->uc_stack_ptr = sk_addr; uctx->uc_stack_len = sk_size; /* configure the underlying machine context */ if (!pth_mctx_set(&uctx->uc_mctx, pth_uctx_trampoline, uctx->uc_stack_ptr, uctx->uc_stack_ptr+uctx->uc_stack_len)) return pth_error(FALSE, errno); /* move context information into global storage for the trampoline jump */ pth_gctx_get()->pth_uctx_trampoline_ctx.mctx_parent = &mctx_parent; pth_gctx_get()->pth_uctx_trampoline_ctx.uctx_this = uctx; pth_gctx_get()->pth_uctx_trampoline_ctx.uctx_after = uctx_after; pth_gctx_get()->pth_uctx_trampoline_ctx.start_func = start_func; pth_gctx_get()->pth_uctx_trampoline_ctx.start_arg = start_arg; /* optionally establish temporary signal mask */ if (sigmask != NULL) sigprocmask(SIG_SETMASK, sigmask, &ss); /* perform the trampoline step */ pth_mctx_switch(&mctx_parent, &(uctx->uc_mctx)); /* optionally restore original signal mask */ if (sigmask != NULL) sigprocmask(SIG_SETMASK, &ss, NULL); /* finally flag that the context is now configured */ uctx->uc_mctx_set = TRUE; return TRUE; }
/* cancel a thread (the friendly way) */ int pth_cancel(pth_t thread) { pth_pqueue_t *q; if (thread == NULL) return pth_error(FALSE, EINVAL); /* the current thread cannot be cancelled */ if (thread == pth_gctx_get()->pth_current) return pth_error(FALSE, EINVAL); /* the thread has to be at least still alive */ if (thread->state == PTH_STATE_DEAD) return pth_error(FALSE, EPERM); /* now mark the thread as cancelled */ thread->cancelreq = TRUE; /* when cancellation is enabled in async mode we cancel the thread immediately */ if ( thread->cancelstate & PTH_CANCEL_ENABLE && thread->cancelstate & PTH_CANCEL_ASYNCHRONOUS) { /* remove thread from its queue */ switch (thread->state) { case PTH_STATE_NEW: q = &pth_gctx_get()->pth_NQ; break; case PTH_STATE_READY: q = &pth_gctx_get()->pth_RQ; break; case PTH_STATE_WAITING: q = &pth_gctx_get()->pth_WQ; break; default: q = NULL; } if (q == NULL) return pth_error(FALSE, ESRCH); if (!pth_pqueue_contains(q, thread)) return pth_error(FALSE, ESRCH); pth_pqueue_delete(q, thread); /* execute cleanups */ pth_thread_cleanup(thread); /* and now either kick it out or move it to dead queue */ if (!thread->joinable) { pth_debug2("pth_cancel: kicking out cancelled thread \"%s\" immediately", thread->name); pth_tcb_free(thread); } else { pth_debug2("pth_cancel: moving cancelled thread \"%s\" to dead queue", thread->name); thread->join_arg = PTH_CANCELED; thread->state = PTH_STATE_DEAD; pth_pqueue_insert(&pth_gctx_get()->pth_DQ, PTH_PRIO_STD, thread); } } return TRUE; }
int pth_cleanup_push(void (*func)(void *), void *arg) { pth_cleanup_t *cleanup; if (func == NULL) return pth_error(FALSE, EINVAL); if ((cleanup = (pth_cleanup_t *)malloc(sizeof(pth_cleanup_t))) == NULL) return pth_error(FALSE, ENOMEM); cleanup->func = func; cleanup->arg = arg; cleanup->next = pth_current->cleanups; pth_current->cleanups = cleanup; return TRUE; }
/* switch from current to other user-space context */ int pth_uctx_switch( pth_uctx_t uctx_from, pth_uctx_t uctx_to) { /* argument sanity checking */ if (uctx_from == NULL || uctx_to == NULL) return pth_error(FALSE, EINVAL); if (!(uctx_to->uc_mctx_set)) return pth_error(FALSE, EPERM); /* switch underlying machine context */ uctx_from->uc_mctx_set = TRUE; pth_mctx_switch(&(uctx_from->uc_mctx), &(uctx_to->uc_mctx)); return TRUE; }
int pth_mutex_init(pth_mutex_t *mutex) { if (mutex == NULL) return pth_error(FALSE, EINVAL); mutex->mx_state = PTH_MUTEX_INITIALIZED; mutex->mx_owner = NULL; mutex->mx_count = 0; return TRUE; }
int pth_rwlock_init(pth_rwlock_t *rwlock) { if (rwlock == NULL) return pth_error(FALSE, EINVAL); rwlock->rw_state = PTH_RWLOCK_INITIALIZED; rwlock->rw_readers = 0; pth_mutex_init(&(rwlock->rw_mutex_rd)); pth_mutex_init(&(rwlock->rw_mutex_rw)); return TRUE; }
int pth_atfork_push(void (*prepare)(void *), void (*parent)(void *), void (*child)(void *), void *arg) { if (pth_atfork_idx > PTH_ATFORK_MAX-1) return pth_error(FALSE, ENOMEM); pth_atfork_list[pth_atfork_idx].prepare = prepare; pth_atfork_list[pth_atfork_idx].parent = parent; pth_atfork_list[pth_atfork_idx].child = child; pth_atfork_list[pth_atfork_idx].arg = arg; pth_atfork_idx++; return TRUE; }
/* initialize the package */ int pth_init(void) { pth_attr_t t_attr; /* support for implicit initialization calls and to prevent multiple explict initialization, too */ if (pth_initialized) return pth_error(FALSE, EPERM); else pth_initialized = TRUE; pth_debug1("pth_init: enter"); /* initialize syscall wrapping */ pth_syscall_init(); /* initialize the scheduler */ if (!pth_scheduler_init()) { pth_shield { pth_syscall_kill(); } return pth_error(FALSE, EAGAIN); }
/* abort a thread (the cruel way) */ int pth_abort(pth_t thread) { if (thread == NULL) return pth_error(FALSE, EINVAL); /* the current thread cannot be aborted */ if (thread == pth_gctx_get()->pth_current) return pth_error(FALSE, EINVAL); if (thread->state == PTH_STATE_DEAD && thread->joinable) { /* if thread is already terminated, just join it */ if (!pth_join(thread, NULL)) return FALSE; } else { /* else force it to be detached and cancel it asynchronously */ thread->joinable = FALSE; thread->cancelstate = (PTH_CANCEL_ENABLE|PTH_CANCEL_ASYNCHRONOUS); if (!pth_cancel(thread)) return FALSE; } return TRUE; }
/* destroy user-space context structure */ int pth_uctx_destroy( pth_uctx_t uctx) { /* argument sanity checking */ if (uctx == NULL) return pth_error(FALSE, EINVAL); /* deallocate dynamically allocated stack */ if (uctx->uc_stack_own && uctx->uc_stack_ptr != NULL) free(uctx->uc_stack_ptr); /* deallocate context structure */ free(uctx); return TRUE; }