void _thr_free(struct pthread *curthread, struct pthread *thread) { DBG_MSG("Freeing thread %p\n", thread); if (thread->name) { free(thread->name); thread->name = NULL; } /* * Always free tcb, as we only know it is part of RTLD TLS * block, but don't know its detail and can not assume how * it works, so better to avoid caching it here. */ if (curthread != NULL) { THR_LOCK_ACQUIRE(curthread, &tcb_lock); _tcb_dtor(thread->tcb); THR_LOCK_RELEASE(curthread, &tcb_lock); } else { _tcb_dtor(thread->tcb); } thread->tcb = NULL; if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) { thr_destroy(curthread, thread); } else { /* * Add the thread to the free thread list, this also avoids * pthread id is reused too quickly, may help some buggy apps. */ THR_LOCK_ACQUIRE(curthread, &free_thread_lock); TAILQ_INSERT_TAIL(&free_threadq, thread, tle); free_thread_count++; THR_LOCK_RELEASE(curthread, &free_thread_lock); } }
int _pthread_key_create(pthread_key_t *key, void (*destructor) (void *)) { struct pthread *curthread; int i; if (_thr_initial == NULL) _libpthread_init(NULL); curthread = _get_curthread(); /* 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 _thread_cleanupspecific(void) { struct pthread *curthread = _get_curthread(); const_key_destructor_t destructor; 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 = (const_key_destructor_t) _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(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); }
static void cond_wait_backout(void *arg) { struct pthread *curthread = (struct pthread *)arg; pthread_cond_t cond; cond = curthread->data.cond; if (cond != NULL) { /* Lock the condition variable structure: */ THR_LOCK_ACQUIRE(curthread, &cond->c_lock); /* Process according to condition variable type: */ switch (cond->c_type) { /* Fast condition variable: */ case COND_TYPE_FAST: cond_queue_remove(cond, curthread); /* Check for no more waiters: */ if (TAILQ_EMPTY(&cond->c_queue)) cond->c_mutex = NULL; break; default: break; } /* Unlock the condition variable structure: */ THR_LOCK_RELEASE(curthread, &cond->c_lock); } /* No need to call this again. */ curthread->sigbackout = NULL; }
/* * Allocate wake-address, the memory area is never freed after * allocated, this becauses threads may be referencing it. */ struct wake_addr * _thr_alloc_wake_addr(void) { struct pthread *curthread; struct wake_addr *p; if (_thr_initial == NULL) { return &default_wake_addr; } curthread = _get_curthread(); THR_LOCK_ACQUIRE(curthread, &addr_lock); if (wake_addr_head == NULL) { unsigned i; unsigned pagesize = getpagesize(); struct wake_addr *pp = (struct wake_addr *) mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); for (i = 1; i < pagesize/sizeof(struct wake_addr); ++i) pp[i].link = &pp[i+1]; pp[i-1].link = NULL; wake_addr_head = &pp[1]; p = &pp[0]; } else { p = wake_addr_head; wake_addr_head = p->link; } THR_LOCK_RELEASE(curthread, &addr_lock); p->value = 0; return (p); }
int _pthread_mutex_destroy(pthread_mutex_t *mutex) { struct pthread *curthread = _get_curthread(); pthread_mutex_t m; int ret = 0; if (mutex == NULL || *mutex == NULL) ret = EINVAL; else { /* Lock the mutex structure: */ THR_LOCK_ACQUIRE(curthread, &(*mutex)->m_lock); /* * Check to see if this mutex is in use: */ if (((*mutex)->m_owner != NULL) || (!TAILQ_EMPTY(&(*mutex)->m_queue)) || ((*mutex)->m_refcount != 0)) { ret = EBUSY; /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &(*mutex)->m_lock); } else { /* * Save a pointer to the mutex so it can be free'd * and set the caller's pointer to NULL: */ m = *mutex; *mutex = NULL; /* Unlock the mutex structure: */ THR_LOCK_RELEASE(curthread, &m->m_lock); /* * Free the memory allocated for the mutex * structure: */ MUTEX_ASSERT_NOT_OWNED(m); MUTEX_DESTROY(m); } } /* Return the completion status: */ return (ret); }
void _thr_release_wake_addr(struct wake_addr *wa) { struct pthread *curthread = _get_curthread(); if (wa == &default_wake_addr) return; THR_LOCK_ACQUIRE(curthread, &addr_lock); wa->link = wake_addr_head; wake_addr_head = wa; THR_LOCK_RELEASE(curthread, &addr_lock); }
struct pthread * _thr_alloc(struct pthread *curthread) { struct pthread *thread = NULL; struct tls_tcb *tcb; if (curthread != NULL) { if (GC_NEEDED()) _thr_gc(curthread); if (free_thread_count > 0) { THR_LOCK_ACQUIRE(curthread, &free_thread_lock); if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) { TAILQ_REMOVE(&free_threadq, thread, tle); free_thread_count--; } THR_LOCK_RELEASE(curthread, &free_thread_lock); } } if (thread == NULL) { thread = malloc(sizeof(struct pthread)); if (thread == NULL) return (NULL); } if (curthread != NULL) { THR_LOCK_ACQUIRE(curthread, &tcb_lock); tcb = _tcb_ctor(thread, 0 /* not initial tls */); THR_LOCK_RELEASE(curthread, &tcb_lock); } else { tcb = _tcb_ctor(thread, 1 /* initial tls */); } if (tcb != NULL) { memset(thread, 0, sizeof(*thread)); thread->tcb = tcb; } else { thr_destroy(curthread, thread); thread = NULL; } return (thread); }
static int init_static_private(struct pthread *thread, pthread_mutex_t *mutex) { int ret; THR_LOCK_ACQUIRE(thread, &_mutex_static_lock); if (*mutex == NULL) ret = _pthread_mutex_init(mutex, &static_mattr); else ret = 0; THR_LOCK_RELEASE(thread, &_mutex_static_lock); return (ret); }
static int init_static(pthread_rwlock_t *rwlock) { struct pthread *thread = _get_curthread(); int ret; THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock); if (*rwlock == NULL) ret = _pthread_rwlock_init(rwlock, NULL); else ret = 0; THR_LOCK_RELEASE(thread, &_rwlock_static_lock); return (ret); }
static int init_static(struct pthread *thread, pthread_rwlock_t *rwlock) { int ret; THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock); if (*rwlock == THR_RWLOCK_INITIALIZER) ret = rwlock_init(rwlock, NULL); else ret = 0; THR_LOCK_RELEASE(thread, &_rwlock_static_lock); return (ret); }
static int init_static(struct pthread *thread, pthread_cond_t *cond) { int ret; THR_LOCK_ACQUIRE(thread, &_cond_static_lock); if (*cond == NULL) ret = cond_init(cond, NULL); else ret = 0; THR_LOCK_RELEASE(thread, &_cond_static_lock); return (ret); }
void _thr_tsd_unload(struct dl_phdr_info *phdr_info) { struct pthread *curthread = _get_curthread(); void (*destructor)(void *); int key; THR_LOCK_ACQUIRE(curthread, &_keytable_lock); for (key = 0; key < PTHREAD_KEYS_MAX; key++) { if (_thread_keytable[key].allocated) { destructor = _thread_keytable[key].destructor; if (destructor != NULL) { if (__elf_phdr_match_addr(phdr_info, destructor)) _thread_keytable[key].destructor = NULL; } } } THR_LOCK_RELEASE(curthread, &_keytable_lock); }
static inline void check_continuation(struct pthread *curthread, struct pthread_cond *cond, pthread_mutex_t *mutex) { if ((curthread->interrupted != 0) && (curthread->continuation != NULL)) { if (cond != NULL) /* Unlock the condition variable structure: */ THR_LOCK_RELEASE(curthread, &cond->c_lock); /* * Note that even though this thread may have been * canceled, POSIX requires that the mutex be * reaquired prior to cancellation. */ if (mutex != NULL) _mutex_cv_lock(mutex); curthread->continuation((void *) curthread); PANIC("continuation returned in pthread_cond_wait.\n"); } }
int _pthread_key_delete(pthread_key_t key) { struct pthread *curthread = _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_cond_destroy(pthread_cond_t *cond) { struct pthread_cond *cv; struct pthread *curthread = _get_curthread(); int rval = 0; if (cond == NULL || *cond == NULL) rval = EINVAL; else { /* Lock the condition variable structure: */ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); /* * NULL the caller's pointer now that the condition * variable has been destroyed: */ cv = *cond; *cond = NULL; /* Unlock the condition variable structure: */ THR_LOCK_RELEASE(curthread, &cv->c_lock); /* Free the cond lock structure: */ _lock_destroy(&cv->c_lock); /* * Free the memory allocated for the condition * variable structure: */ free(cv); } /* Return the completion status: */ return (rval); }
int _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime) { struct pthread *curthread = _get_curthread(); int rval = 0; int done = 0; int mutex_locked = 1; int seqno; THR_ASSERT(curthread->locklevel == 0, "cv_timedwait: locklevel is not zero!"); if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return (EINVAL); /* * If the condition variable is statically initialized, perform dynamic * initialization. */ if (*cond == NULL && (rval = _pthread_cond_init(cond, NULL)) != 0) return (rval); if (!_kse_isthreaded()) _kse_setthreaded(1); /* * Enter a loop waiting for a condition signal or broadcast * to wake up this thread. A loop is needed in case the waiting * thread is interrupted by a signal to execute a signal handler. * It is not (currently) possible to remain in the waiting queue * while running a handler. Instead, the thread is interrupted * and backed out of the waiting queue prior to executing the * signal handler. */ /* Lock the condition variable structure: */ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); seqno = (*cond)->c_seqno; do { /* * If the condvar was statically allocated, properly * initialize the tail queue. */ if (((*cond)->c_flags & COND_FLAGS_INITED) == 0) { TAILQ_INIT(&(*cond)->c_queue); (*cond)->c_flags |= COND_FLAGS_INITED; } /* Process according to condition variable type: */ switch ((*cond)->c_type) { /* Fast condition variable: */ case COND_TYPE_FAST: if ((mutex == NULL) || (((*cond)->c_mutex != NULL) && ((*cond)->c_mutex != *mutex))) { /* Return invalid argument error: */ rval = EINVAL; } else { /* Reset the timeout and interrupted flags: */ curthread->timeout = 0; curthread->interrupted = 0; /* * Queue the running thread for the condition * variable: */ cond_queue_enq(*cond, curthread); /* Unlock the mutex: */ if (mutex_locked && ((rval = _mutex_cv_unlock(mutex)) != 0)) { /* * Cannot unlock the mutex; remove the * running thread from the condition * variable queue: */ cond_queue_remove(*cond, curthread); } else { /* Remember the mutex: */ (*cond)->c_mutex = *mutex; /* * Don't unlock the mutex the next * time through the loop (if the * thread has to be requeued after * handling a signal). */ mutex_locked = 0; /* * This thread is active and is in a * critical region (holding the cv * lock); we should be able to safely * set the state. */ THR_SCHED_LOCK(curthread, curthread); /* Set the wakeup time: */ curthread->wakeup_time.tv_sec = abstime->tv_sec; curthread->wakeup_time.tv_nsec = abstime->tv_nsec; THR_SET_STATE(curthread, PS_COND_WAIT); /* Remember the CV: */ curthread->data.cond = *cond; curthread->sigbackout = cond_wait_backout; THR_SCHED_UNLOCK(curthread, curthread); /* Unlock the CV structure: */ THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); /* Schedule the next thread: */ _thr_sched_switch(curthread); /* * XXX - This really isn't a good check * since there can be more than one * thread waiting on the CV. Signals * sent to threads waiting on mutexes * or CVs should really be deferred * until the threads are no longer * waiting, but POSIX says that signals * should be sent "as soon as possible". */ done = (seqno != (*cond)->c_seqno); if (done && !THR_IN_CONDQ(curthread)) { /* * The thread is dequeued, so * it is safe to clear these. */ curthread->data.cond = NULL; curthread->sigbackout = NULL; check_continuation(curthread, NULL, mutex); return (_mutex_cv_lock(mutex)); } /* Relock the CV structure: */ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); /* * Clear these after taking the lock to * prevent a race condition where a * signal can arrive before dequeueing * the thread. */ curthread->data.cond = NULL; curthread->sigbackout = NULL; done = (seqno != (*cond)->c_seqno); if (THR_IN_CONDQ(curthread)) { cond_queue_remove(*cond, curthread); /* Check for no more waiters: */ if (TAILQ_EMPTY(&(*cond)->c_queue)) (*cond)->c_mutex = NULL; } if (curthread->timeout != 0) { /* The wait timedout. */ rval = ETIMEDOUT; } } } break; /* Trap invalid condition variable types: */ default: /* Return an invalid argument error: */ rval = EINVAL; break; } check_continuation(curthread, *cond, mutex_locked ? NULL : mutex); } while ((done == 0) && (rval == 0)); /* Unlock the condition variable structure: */ THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); if (mutex_locked == 0) _mutex_cv_lock(mutex); /* Return the completion status: */ return (rval); }
int _pthread_cond_signal(pthread_cond_t * cond) { struct pthread *curthread = _get_curthread(); struct pthread *pthread; struct kse_mailbox *kmbx; int rval = 0; THR_ASSERT(curthread->locklevel == 0, "cv_timedwait: locklevel is not zero!"); if (cond == NULL) rval = EINVAL; /* * If the condition variable is statically initialized, perform dynamic * initialization. */ else if (*cond != NULL || (rval = _pthread_cond_init(cond, NULL)) == 0) { /* Lock the condition variable structure: */ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); /* Process according to condition variable type: */ switch ((*cond)->c_type) { /* Fast condition variable: */ case COND_TYPE_FAST: /* Increment the sequence number: */ (*cond)->c_seqno++; /* * Wakeups have to be done with the CV lock held; * otherwise there is a race condition where the * thread can timeout, run on another KSE, and enter * another blocking state (including blocking on a CV). */ if ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) != NULL) { THR_SCHED_LOCK(curthread, pthread); cond_queue_remove(*cond, pthread); pthread->sigbackout = NULL; if ((pthread->kseg == curthread->kseg) && (pthread->active_priority > curthread->active_priority)) curthread->critical_yield = 1; kmbx = _thr_setrunnable_unlocked(pthread); THR_SCHED_UNLOCK(curthread, pthread); if (kmbx != NULL) kse_wakeup(kmbx); } /* Check for no more waiters: */ if (TAILQ_EMPTY(&(*cond)->c_queue)) (*cond)->c_mutex = NULL; break; /* Trap invalid condition variable types: */ default: /* Return an invalid argument error: */ rval = EINVAL; break; } /* Unlock the condition variable structure: */ THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); } /* Return the completion status: */ return (rval); }
int _pthread_cond_broadcast(pthread_cond_t * cond) { struct pthread *curthread = _get_curthread(); struct pthread *pthread; struct kse_mailbox *kmbx; int rval = 0; THR_ASSERT(curthread->locklevel == 0, "cv_timedwait: locklevel is not zero!"); if (cond == NULL) rval = EINVAL; /* * If the condition variable is statically initialized, perform dynamic * initialization. */ else if (*cond != NULL || (rval = _pthread_cond_init(cond, NULL)) == 0) { /* Lock the condition variable structure: */ THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); /* Process according to condition variable type: */ switch ((*cond)->c_type) { /* Fast condition variable: */ case COND_TYPE_FAST: /* Increment the sequence number: */ (*cond)->c_seqno++; /* * Enter a loop to bring all threads off the * condition queue: */ while ((pthread = TAILQ_FIRST(&(*cond)->c_queue)) != NULL) { THR_SCHED_LOCK(curthread, pthread); cond_queue_remove(*cond, pthread); pthread->sigbackout = NULL; if ((pthread->kseg == curthread->kseg) && (pthread->active_priority > curthread->active_priority)) curthread->critical_yield = 1; kmbx = _thr_setrunnable_unlocked(pthread); THR_SCHED_UNLOCK(curthread, pthread); if (kmbx != NULL) kse_wakeup(kmbx); } /* There are no more waiting threads: */ (*cond)->c_mutex = NULL; break; /* Trap invalid condition variable types: */ default: /* Return an invalid argument error: */ rval = EINVAL; break; } /* Unlock the condition variable structure: */ THR_LOCK_RELEASE(curthread, &(*cond)->c_lock); } /* Return the completion status: */ return (rval); }
void _thread_cleanupspecific(void) { struct pthread *curthread = _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--; } else if (curthread->specific[key].data != NULL) { /* * This can happen if the key is deleted via * pthread_key_delete without first setting the value * to NULL in all threads. POSIX says that the * destructor is not invoked in this case. */ curthread->specific[key].data = NULL; curthread->specific_data_count--; } /* * If there is a destructor, 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); }