int _pthread_cancel(pthread_t pthread) { struct pthread *curthread = tls_get_curthread(); int oldval, newval = 0; int oldtype; int ret; /* * POSIX says _pthread_cancel should be async cancellation safe, * so we temporarily disable async cancellation. */ _pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); if ((ret = _thr_ref_add(curthread, pthread, 0)) != 0) { _pthread_setcanceltype(oldtype, NULL); return (ret); } do { oldval = pthread->cancelflags; if (oldval & THR_CANCEL_NEEDED) break; newval = oldval | THR_CANCEL_NEEDED; } while (!atomic_cmpset_acq_int(&pthread->cancelflags, oldval, newval)); if (!(oldval & THR_CANCEL_NEEDED) && SHOULD_ASYNC_CANCEL(newval)) _thr_send_sig(pthread, SIGCANCEL); _thr_ref_delete(curthread, pthread); _pthread_setcanceltype(oldtype, NULL); return (0); }
int _pthread_setcanceltype(int type, int *oldtype) { struct pthread *curthread = tls_get_curthread(); int oldval; oldval = curthread->cancelflags; if (oldtype != NULL) *oldtype = ((oldval & THR_CANCEL_AT_POINT) ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED); switch (type) { case PTHREAD_CANCEL_ASYNCHRONOUS: atomic_set_int(&curthread->cancelflags, THR_CANCEL_AT_POINT); testcancel(curthread); break; case PTHREAD_CANCEL_DEFERRED: atomic_clear_int(&curthread->cancelflags, THR_CANCEL_AT_POINT); break; default: return (EINVAL); } return (0); }
int _pthread_key_create(pthread_key_t *key, void (*destructor) (void *)) { struct pthread *curthread = tls_get_curthread(); int i; /* Lock the key table: */ THR_LOCK_ACQUIRE(curthread, &_keytable_lock); for (i = 0; i < PTHREAD_KEYS_MAX; i++) { if (_thread_keytable[i].allocated == 0) { _thread_keytable[i].allocated = 1; _thread_keytable[i].destructor = destructor; _thread_keytable[i].seqno++; /* Unlock the key table: */ THR_LOCK_RELEASE(curthread, &_keytable_lock); *key = i; return (0); } } /* Unlock the key table: */ THR_LOCK_RELEASE(curthread, &_keytable_lock); return (EAGAIN); }
void * _pthread_getspecific(pthread_key_t key) { struct pthread *pthread; const void *data; /* Point to the running thread: */ pthread = tls_get_curthread(); /* Check if there is specific data: */ if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) { /* Check if this key has been used before: */ if (_thread_keytable[key].allocated && (pthread->specific[key].seqno == _thread_keytable[key].seqno)) { /* Return the value: */ data = pthread->specific[key].data; } else { /* * This key has not been used before, so return NULL * instead: */ data = NULL; } } else /* No specific data has been created, so just return NULL: */ data = NULL; return __DECONST(void *, data); }
int _pthread_setspecific(pthread_key_t key, const void *value) { struct pthread *pthread; int ret = 0; /* Point to the running thread: */ pthread = tls_get_curthread(); if ((pthread->specific) || (pthread->specific = pthread_key_allocate_data())) { if ((unsigned int)key < PTHREAD_KEYS_MAX) { if (_thread_keytable[key].allocated) { if (pthread->specific[key].data == NULL) { if (value != NULL) pthread->specific_data_count++; } else if (value == NULL) pthread->specific_data_count--; pthread->specific[key].data = value; pthread->specific[key].seqno = _thread_keytable[key].seqno; ret = 0; } else ret = EINVAL; } else ret = EINVAL; } else ret = ENOMEM; return (ret); }
int _pthread_detach(pthread_t pthread) { struct pthread *curthread = tls_get_curthread(); int rval; if (pthread == NULL) return (EINVAL); THREAD_LIST_LOCK(curthread); if ((rval = _thr_find_thread(curthread, pthread, /*include dead*/1)) != 0) { THREAD_LIST_UNLOCK(curthread); return (rval); } /* Check if the thread is already detached or has a joiner. */ if ((pthread->tlflags & TLFLAGS_DETACHED) != 0 || (pthread->joiner != NULL)) { THREAD_LIST_UNLOCK(curthread); return (EINVAL); } /* Flag the thread as detached. */ pthread->tlflags |= TLFLAGS_DETACHED; if (pthread->state == PS_DEAD) THR_GCLIST_ADD(pthread); THREAD_LIST_UNLOCK(curthread); return (0); }
static void backout_join(void *arg) { struct pthread *curthread = tls_get_curthread(); struct pthread *pthread = (struct pthread *)arg; THREAD_LIST_LOCK(curthread); pthread->joiner = NULL; THREAD_LIST_UNLOCK(curthread); }
void _thread_cleanupspecific(void) { struct pthread *curthread = tls_get_curthread(); void (*destructor)( void *); const void *data = NULL; int key; int i; if (curthread->specific == NULL) return; /* Lock the key table: */ THR_LOCK_ACQUIRE(curthread, &_keytable_lock); for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) && (curthread->specific_data_count > 0); i++) { for (key = 0; (key < PTHREAD_KEYS_MAX) && (curthread->specific_data_count > 0); key++) { destructor = NULL; if (_thread_keytable[key].allocated && (curthread->specific[key].data != NULL)) { if (curthread->specific[key].seqno == _thread_keytable[key].seqno) { data = curthread->specific[key].data; destructor = _thread_keytable[key].destructor; } curthread->specific[key].data = NULL; curthread->specific_data_count--; } /* * If there is a destructore, call it * with the key table entry unlocked: */ if (destructor != NULL) { /* * Don't hold the lock while calling the * destructor: */ THR_LOCK_RELEASE(curthread, &_keytable_lock); destructor(__DECONST(void *, data)); THR_LOCK_ACQUIRE(curthread, &_keytable_lock); } } } THR_LOCK_RELEASE(curthread, &_keytable_lock); free(curthread->specific); curthread->specific = NULL; if (curthread->specific_data_count > 0) stderr_debug("Thread %p has exited with leftover " "thread-specific data after %d destructor iterations\n", curthread, PTHREAD_DESTRUCTOR_ITERATIONS); }
/* Set the thread name for debug. */ void _pthread_set_name_np(pthread_t thread, const char *name) { struct pthread *curthread = tls_get_curthread(); if (curthread == thread) { lwp_setname(thread->tid, name); } else { if (_thr_ref_add(curthread, thread, 0) == 0) { THR_THREAD_LOCK(curthread, thread); if (thread->state != PS_DEAD) lwp_setname(thread->tid, name); THR_THREAD_UNLOCK(curthread, thread); _thr_ref_delete(curthread, thread); } } }
int _pthread_attr_get_np(pthread_t pid, pthread_attr_t *dst) { struct pthread *curthread; struct pthread_attr attr; int ret; if (pid == NULL || dst == NULL || *dst == NULL) return (EINVAL); curthread = tls_get_curthread(); if ((ret = _thr_ref_add(curthread, pid, /*include dead*/0)) != 0) return (ret); attr = pid->attr; if (pid->tlflags & TLFLAGS_DETACHED) attr.flags |= PTHREAD_DETACHED; _thr_ref_delete(curthread, pid); memcpy(*dst, &attr, sizeof(struct pthread_attr)); return (0); }
int _pthread_key_delete(pthread_key_t key) { struct pthread *curthread = tls_get_curthread(); int ret = 0; if ((unsigned int)key < PTHREAD_KEYS_MAX) { /* Lock the key table: */ THR_LOCK_ACQUIRE(curthread, &_keytable_lock); if (_thread_keytable[key].allocated) _thread_keytable[key].allocated = 0; else ret = EINVAL; /* Unlock the key table: */ THR_LOCK_RELEASE(curthread, &_keytable_lock); } else ret = EINVAL; return (ret); }
int _pthread_setcancelstate(int state, int *oldstate) { struct pthread *curthread = tls_get_curthread(); int oldval; oldval = curthread->cancelflags; if (oldstate != NULL) *oldstate = ((oldval & THR_CANCEL_DISABLE) ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE); switch (state) { case PTHREAD_CANCEL_DISABLE: atomic_set_int(&curthread->cancelflags, THR_CANCEL_DISABLE); break; case PTHREAD_CANCEL_ENABLE: atomic_clear_int(&curthread->cancelflags, THR_CANCEL_DISABLE); testcancel(curthread); break; default: return (EINVAL); } return (0); }
void _pthread_testcancel(void) { testcancel(tls_get_curthread()); }
static int join_common(pthread_t pthread, void **thread_return, const struct timespec *abstime) { struct pthread *curthread = tls_get_curthread(); struct timespec ts, ts2, *tsp; void *tmp; long state; int oldcancel; int ret = 0; if (pthread == NULL) return (EINVAL); if (pthread == curthread) return (EDEADLK); THREAD_LIST_LOCK(curthread); if ((ret = _thr_find_thread(curthread, pthread, 1)) != 0) { ret = ESRCH; } else if ((pthread->tlflags & TLFLAGS_DETACHED) != 0) { ret = ESRCH; } else if (pthread->joiner != NULL) { /* Multiple joiners are not supported. */ ret = ENOTSUP; } if (ret) { THREAD_LIST_UNLOCK(curthread); return (ret); } /* Set the running thread to be the joiner: */ pthread->joiner = curthread; THREAD_LIST_UNLOCK(curthread); THR_CLEANUP_PUSH(curthread, backout_join, pthread); oldcancel = _thr_cancel_enter(curthread); while ((state = pthread->state) != PS_DEAD) { if (abstime != NULL) { clock_gettime(CLOCK_REALTIME, &ts); TIMESPEC_SUB(&ts2, abstime, &ts); if (ts2.tv_sec < 0) { ret = ETIMEDOUT; break; } tsp = &ts2; } else tsp = NULL; ret = _thr_umtx_wait(&pthread->state, state, tsp, CLOCK_REALTIME); if (ret == ETIMEDOUT) break; } _thr_cancel_leave(curthread, oldcancel); THR_CLEANUP_POP(curthread, 0); if (ret == ETIMEDOUT) { THREAD_LIST_LOCK(curthread); pthread->joiner = NULL; THREAD_LIST_UNLOCK(curthread); } else { ret = 0; tmp = pthread->ret; THREAD_LIST_LOCK(curthread); pthread->tlflags |= TLFLAGS_DETACHED; pthread->joiner = NULL; THR_GCLIST_ADD(pthread); THREAD_LIST_UNLOCK(curthread); if (thread_return != NULL) *thread_return = tmp; } return (ret); }
pthread_t _pthread_self(void) { /* Return the running thread pointer: */ return (tls_get_curthread()); }
int _pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg) { struct lwp_params create_params; void *stack; sigset_t sigmask, oldsigmask; struct pthread *curthread, *new_thread; int ret = 0, locked; _thr_check_init(); /* * Tell libc and others now they need lock to protect their data. */ if (_thr_isthreaded() == 0 && _thr_setthreaded(1)) return (EAGAIN); curthread = tls_get_curthread(); if ((new_thread = _thr_alloc(curthread)) == NULL) return (EAGAIN); if (attr == NULL || *attr == NULL) { /* Use the default thread attributes: */ new_thread->attr = _pthread_attr_default; } else { new_thread->attr = *(*attr); } if (new_thread->attr.sched_inherit == PTHREAD_INHERIT_SCHED) { /* inherit scheduling contention scope */ 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 (create_stack(&new_thread->attr) != 0) { /* Insufficient memory to create a stack: */ new_thread->terminated = 1; _thr_free(curthread, new_thread); return (EAGAIN); } /* * Write a magic value to the thread structure * to help identify valid ones: */ new_thread->magic = THR_MAGIC; new_thread->start_routine = start_routine; new_thread->arg = arg; new_thread->cancelflags = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; /* * 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_LOCK(curthread); new_thread->base_priority = curthread->base_priority; new_thread->attr.prio = curthread->attr.prio; new_thread->attr.sched_policy = curthread->attr.sched_policy; THR_UNLOCK(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; /* Initialize the mutex queue: */ TAILQ_INIT(&new_thread->mutexq); /* Initialise hooks in the thread structure: */ if (new_thread->attr.suspend == THR_CREATE_SUSPENDED) new_thread->flags = THR_FLAGS_NEED_SUSPEND; new_thread->state = PS_RUNNING; if (new_thread->attr.flags & PTHREAD_CREATE_DETACHED) new_thread->tlflags |= TLFLAGS_DETACHED; /* Add the new thread. */ new_thread->refcount = 1; _thr_link(curthread, new_thread); /* Return thread pointer eariler so that new thread can use it. */ (*thread) = new_thread; if (SHOULD_REPORT_EVENT(curthread, TD_CREATE)) { THR_THREAD_LOCK(curthread, new_thread); locked = 1; } else locked = 0; /* Schedule the new thread. */ stack = (char *)new_thread->attr.stackaddr_attr + new_thread->attr.stacksize_attr; bzero(&create_params, sizeof(create_params)); create_params.lwp_func = thread_start; create_params.lwp_arg = new_thread; create_params.lwp_stack = stack; create_params.lwp_tid1 = &new_thread->tid; /* * Thread created by thr_create() inherits currrent thread * sigmask, however, before new thread setup itself correctly, * it can not handle signal, so we should mask all signals here. * We do this at the very last moment, so that we don't run * into problems while we have all signals disabled. */ SIGFILLSET(sigmask); __sys_sigprocmask(SIG_SETMASK, &sigmask, &oldsigmask); new_thread->sigmask = oldsigmask; ret = lwp_create(&create_params); __sys_sigprocmask(SIG_SETMASK, &oldsigmask, NULL); if (ret != 0) { if (!locked) THR_THREAD_LOCK(curthread, new_thread); new_thread->state = PS_DEAD; new_thread->terminated = 1; if (new_thread->flags & THR_FLAGS_NEED_SUSPEND) { new_thread->cycle++; _thr_umtx_wake(&new_thread->cycle, INT_MAX); } THR_THREAD_UNLOCK(curthread, new_thread); THREAD_LIST_LOCK(curthread); _thread_active_threads--; new_thread->tlflags |= TLFLAGS_DETACHED; _thr_ref_delete_unlocked(curthread, new_thread); THREAD_LIST_UNLOCK(curthread); (*thread) = NULL; ret = EAGAIN; } else if (locked) { _thr_report_creation(curthread, new_thread); THR_THREAD_UNLOCK(curthread, new_thread); } return (ret); }
#ifdef DEBUG_SIGNAL #define DBG_MSG stdout_debug #else #define DBG_MSG(x...) #endif int __sigwait(const sigset_t *set, int *sig); int __sigwaitinfo(const sigset_t *set, siginfo_t *info); int __sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); static void sigcancel_handler(int sig __unused, siginfo_t *info __unused, ucontext_t *ucp __unused) { struct pthread *curthread = tls_get_curthread(); if (curthread->cancelflags & THR_CANCEL_AT_POINT) _pthread_testcancel(); _thr_ast(curthread); } void _thr_ast(struct pthread *curthread) { if (!THR_IN_CRITICAL(curthread)) { if (__predict_false((curthread->flags & (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND)) _thr_suspend_check(curthread); }