/* * Inline followup when releasing a mutex. The mutex has been released * but 'v' either doesn't match id or needs a wakeup. */ void __thr_umtx_unlock(volatile umtx_t *mtx, int v, int id) { if (v & 0x40000000) { _umtx_wakeup_err(mtx, 0); v &= 0x3FFFFFFF; } THR_ASSERT(v == id, "thr_umtx_unlock: wrong owner"); }
int _sigsuspend(const sigset_t *set) { struct pthread *curthread = _get_curthread(); sigset_t oldmask, newmask, tempset; int ret = -1; if (curthread->attr.flags & PTHREAD_SCOPE_SYSTEM) return (__sys_sigsuspend(set)); /* Check if a new signal set was provided by the caller: */ if (set != NULL) { newmask = *set; SIG_CANTMASK(newmask); THR_LOCK_SWITCH(curthread); /* Save current sigmask: */ oldmask = curthread->sigmask; curthread->oldsigmask = &oldmask; /* Change the caller's mask: */ curthread->sigmask = newmask; tempset = curthread->sigpend; SIGSETNAND(tempset, newmask); if (SIGISEMPTY(tempset)) { THR_SET_STATE(curthread, PS_SIGSUSPEND); /* Wait for a signal: */ _thr_sched_switch_unlocked(curthread); } else { curthread->check_pending = 1; THR_UNLOCK_SWITCH(curthread); /* check pending signal I can handle: */ _thr_sig_check_pending(curthread); } if ((curthread->cancelflags & THR_CANCELLING) != 0) curthread->oldsigmask = NULL; else { THR_ASSERT(curthread->oldsigmask == NULL, "oldsigmask is not cleared"); } /* Always return an interrupted error: */ errno = EINTR; } else { /* Return an invalid argument error: */ errno = EINVAL; } /* Return the completion status: */ return (ret); }
/* * Enqueue a waiting thread to a condition queue in descending priority * order. */ static inline void cond_queue_enq(pthread_cond_t cond, struct pthread *pthread) { struct pthread *tid = TAILQ_LAST(&cond->c_queue, cond_head); THR_ASSERT(!THR_IN_SYNCQ(pthread), "cond_queue_enq: thread already queued!"); /* * For the common case of all threads having equal priority, * we perform a quick check against the priority of the thread * at the tail of the queue. */ if ((tid == NULL) || (pthread->active_priority <= tid->active_priority)) TAILQ_INSERT_TAIL(&cond->c_queue, pthread, sqe); else { tid = TAILQ_FIRST(&cond->c_queue); while (pthread->active_priority <= tid->active_priority) tid = TAILQ_NEXT(tid, sqe); TAILQ_INSERT_BEFORE(tid, pthread, sqe); } THR_CONDQ_SET(pthread); pthread->data.cond = cond; }
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); }
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_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); }