diff options
Diffstat (limited to 'lib/libpthread/thread/thr_kern.c')
-rw-r--r-- | lib/libpthread/thread/thr_kern.c | 273 |
1 files changed, 157 insertions, 116 deletions
diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c index db47878..2df1634 100644 --- a/lib/libpthread/thread/thr_kern.c +++ b/lib/libpthread/thread/thr_kern.c @@ -398,7 +398,6 @@ _kse_lock_wait(struct lock *lock, struct lockuser *lu) */ ts.tv_sec = 0; ts.tv_nsec = 1000000; /* 1 sec */ - KSE_SET_WAIT(curkse); while (_LCK_BUSY(lu)) { /* * Yield the kse and wait to be notified when the lock @@ -408,14 +407,7 @@ _kse_lock_wait(struct lock *lock, struct lockuser *lu) curkse->k_mbx.km_flags |= KMF_NOUPCALL | KMF_NOCOMPLETED; kse_release(&ts); curkse->k_mbx.km_flags = saved_flags; - - /* - * Make sure that the wait flag is set again in case - * we wokeup without the lock being granted. - */ - KSE_SET_WAIT(curkse); } - KSE_CLEAR_WAIT(curkse); } void @@ -423,17 +415,23 @@ _kse_lock_wakeup(struct lock *lock, struct lockuser *lu) { struct kse *curkse; struct kse *kse; + struct kse_mailbox *mbx; curkse = _get_curkse(); kse = (struct kse *)_LCK_GET_PRIVATE(lu); if (kse == curkse) PANIC("KSE trying to wake itself up in lock"); - else if (KSE_WAITING(kse)) { + else { + mbx = &kse->k_mbx; + _lock_grant(lock, lu); /* * Notify the owning kse that it has the lock. + * It is safe to pass invalid address to kse_wakeup + * even if the mailbox is not in kernel at all, + * and waking up a wrong kse is also harmless. */ - KSE_WAKEUP(kse); + kse_wakeup(mbx); } } @@ -446,30 +444,13 @@ void _thr_lock_wait(struct lock *lock, struct lockuser *lu) { struct pthread *curthread = (struct pthread *)lu->lu_private; - int count; - /* - * Spin for a bit. - * - * XXX - We probably want to make this a bit smarter. It - * doesn't make sense to spin unless there is more - * than 1 CPU. A thread that is holding one of these - * locks is prevented from being swapped out for another - * thread within the same scheduling entity. - */ - count = 0; - while (_LCK_BUSY(lu) && count < 300) - count++; - while (_LCK_BUSY(lu)) { - THR_LOCK_SWITCH(curthread); - if (_LCK_BUSY(lu)) { - /* Wait for the lock: */ - atomic_store_rel_int(&curthread->need_wakeup, 1); - THR_SET_STATE(curthread, PS_LOCKWAIT); - _thr_sched_switch(curthread); - } - THR_UNLOCK_SWITCH(curthread); - } + do { + THR_SCHED_LOCK(curthread, curthread); + THR_SET_STATE(curthread, PS_LOCKWAIT); + THR_SCHED_UNLOCK(curthread, curthread); + _thr_sched_switch(curthread); + } while _LCK_BUSY(lu); } void @@ -477,26 +458,14 @@ _thr_lock_wakeup(struct lock *lock, struct lockuser *lu) { struct pthread *thread; struct pthread *curthread; - int unlock; curthread = _get_curthread(); thread = (struct pthread *)_LCK_GET_PRIVATE(lu); - unlock = 0; - if (curthread->kseg == thread->kseg) { - /* Not already locked */ - if (curthread->lock_switch == 0) { - THR_SCHED_LOCK(curthread, thread); - unlock = 1; - } - } else { - THR_SCHED_LOCK(curthread, thread); - unlock = 1; - } + THR_SCHED_LOCK(curthread, thread); + _lock_grant(lock, lu); _thr_setrunnable_unlocked(thread); - atomic_store_rel_int(&thread->need_wakeup, 0); - if (unlock) - THR_SCHED_UNLOCK(curthread, thread); + THR_SCHED_UNLOCK(curthread, thread); } kse_critical_t @@ -537,27 +506,42 @@ _thr_critical_leave(struct pthread *thread) THR_YIELD_CHECK(thread); } +void +_thr_sched_switch(struct pthread *curthread) +{ + struct kse *curkse; + + (void)_kse_critical_enter(); + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + _thr_sched_switch_unlocked(curthread); +} + /* * XXX - We may need to take the scheduling lock before calling * this, or perhaps take the lock within here before * doing anything else. */ void -_thr_sched_switch(struct pthread *curthread) +_thr_sched_switch_unlocked(struct pthread *curthread) { + struct pthread *td; struct pthread_sigframe psf; struct kse *curkse; - volatile int once = 0; + int ret; + volatile int uts_once; + volatile int resume_once = 0; /* We're in the scheduler, 5 by 5: */ - THR_ASSERT(curthread->lock_switch, "lock_switch"); - THR_ASSERT(_kse_in_critical(), "not in critical region"); curkse = _get_curkse(); curthread->need_switchout = 1; /* The thread yielded on its own. */ curthread->critical_yield = 0; /* No need to yield anymore. */ curthread->slice_usec = -1; /* Restart the time slice. */ + /* Thread can unlock the scheduler lock. */ + curthread->lock_switch = 1; + /* * The signal frame is allocated off the stack because * a thread can be interrupted by other signals while @@ -566,19 +550,95 @@ _thr_sched_switch(struct pthread *curthread) sigemptyset(&psf.psf_sigset); curthread->curframe = &psf; - _thread_enter_uts(&curthread->tmbx, &curkse->k_mbx); + /* + * Enter the scheduler if any one of the following is true: + * + * o The current thread is dead; it's stack needs to be + * cleaned up and it can't be done while operating on + * it. + * o There are no runnable threads. + * o The next thread to run won't unlock the scheduler + * lock. A side note: the current thread may be run + * instead of the next thread in the run queue, but + * we don't bother checking for that. + */ + if ((curthread->state == PS_DEAD) || + (((td = KSE_RUNQ_FIRST(curkse)) == NULL) && + (curthread->state != PS_RUNNING)) || + ((td != NULL) && (td->lock_switch == 0))) + _thread_enter_uts(&curthread->tmbx, &curkse->k_mbx); + else { + uts_once = 0; + THR_GETCONTEXT(&curthread->tmbx.tm_context); + if (uts_once == 0) { + uts_once = 1; + + /* Switchout the current thread. */ + kse_switchout_thread(curkse, curthread); + + /* Choose another thread to run. */ + td = KSE_RUNQ_FIRST(curkse); + KSE_RUNQ_REMOVE(curkse, td); + curkse->k_curthread = td; + + /* + * Make sure the current thread's kse points to + * this kse. + */ + td->kse = curkse; + + /* + * Reset accounting. + */ + td->tmbx.tm_uticks = 0; + td->tmbx.tm_sticks = 0; + + /* + * Reset the time slice if this thread is running + * for the first time or running again after using + * its full time slice allocation. + */ + if (td->slice_usec == -1) + td->slice_usec = 0; + + /* Mark the thread active. */ + td->active = 1; + + /* Remove the frame reference. */ + td->curframe = NULL; + /* + * Continue the thread at its current frame: + */ + ret = _thread_switch(&td->tmbx, NULL); + /* This point should not be reached. */ + if (ret != 0) + PANIC("Bad return from _thread_switch"); + PANIC("Thread has returned from _thread_switch"); + } + } + + if (curthread->lock_switch != 0) { + /* + * Unlock the scheduling queue and leave the + * critical region. + */ + /* Don't trust this after a switch! */ + curkse = _get_curkse(); + + curthread->lock_switch = 0; + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + } /* * This thread is being resumed; check for cancellations. */ - if ((once == 0) && (!THR_IN_CRITICAL(curthread))) { - once = 1; - curthread->critical_count++; - THR_UNLOCK_SWITCH(curthread); - curthread->critical_count--; + if ((resume_once == 0) && (!THR_IN_CRITICAL(curthread))) { + resume_once = 1; thr_resume_check(curthread, &curthread->tmbx.tm_context, &psf); - THR_LOCK_SWITCH(curthread); } + + THR_ACTIVATE_LAST_LOCK(curthread); } /* @@ -743,12 +803,10 @@ kse_sched_multi(struct kse *curkse) KSE_CLEAR_WAIT(curkse); } + /* Lock the scheduling lock. */ curthread = curkse->k_curthread; - if (curthread == NULL || curthread->lock_switch == 0) { - /* - * curthread was preempted by upcall, it is not a volunteer - * context switch. Lock the scheduling lock. - */ + if ((curthread == NULL) || (curthread->need_switchout == 0)) { + /* This is an upcall; take the scheduler lock. */ KSE_SCHED_LOCK(curkse, curkse->k_kseg); } @@ -798,14 +856,9 @@ kse_sched_multi(struct kse *curkse) DBG_MSG("Continuing thread %p in critical region\n", curthread); kse_wakeup_multi(curkse); - if (curthread->lock_switch) { - KSE_SCHED_LOCK(curkse, curkse->k_kseg); - ret = _thread_switch(&curthread->tmbx, 0); - } else { - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - ret = _thread_switch(&curthread->tmbx, - &curkse->k_mbx.km_curthread); - } + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + ret = _thread_switch(&curthread->tmbx, + &curkse->k_mbx.km_curthread); if (ret != 0) PANIC("Can't resume thread in critical region\n"); } @@ -895,9 +948,6 @@ kse_sched_multi(struct kse *curkse) kse_wakeup_multi(curkse); - /* Unlock the scheduling queue: */ - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - /* * The thread's current signal frame will only be NULL if it * is being resumed after being blocked in the kernel. In @@ -906,25 +956,30 @@ kse_sched_multi(struct kse *curkse) * signal frame to the thread's context. */ #ifdef NOT_YET - if ((curframe == NULL) && ((curthread->check_pending != 0) || + if ((curframe == NULL) && ((curthread->have_signals != 0) || (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)))) signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); #else - if ((curframe == NULL) && (curthread->check_pending != 0)) + if ((curframe == NULL) && (curthread->have_signals != 0)) signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); #endif /* * Continue the thread at its current frame: */ - if (curthread->lock_switch) { - KSE_SCHED_LOCK(curkse, curkse->k_kseg); - ret = _thread_switch(&curthread->tmbx, 0); + if (curthread->lock_switch != 0) { + /* + * This thread came from a scheduler switch; it will + * unlock the scheduler lock and set the mailbox. + */ + ret = _thread_switch(&curthread->tmbx, NULL); } else { + /* This thread won't unlock the scheduler lock. */ + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); ret = _thread_switch(&curthread->tmbx, - &curkse->k_mbx.km_curthread); + &curkse->k_mbx.km_curthread); } if (ret != 0) PANIC("Thread has returned from _thread_switch"); @@ -977,9 +1032,9 @@ thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { /* Check signals before cancellations. */ - while (curthread->check_pending != 0) { + while (curthread->have_signals != 0) { /* Clear the pending flag. */ - curthread->check_pending = 0; + curthread->have_signals = 0; /* * It's perfectly valid, though not portable, for @@ -1262,6 +1317,11 @@ kse_check_completed(struct kse *kse) THR_SET_STATE(thread, PS_SUSPENDED); else KSE_RUNQ_INSERT_TAIL(kse, thread); + if ((thread->kse != kse) && + (thread->kse->k_curthread == thread)) { + thread->kse->k_curthread = NULL; + thread->active = 0; + } } completed = completed->tm_next; } @@ -1360,12 +1420,15 @@ static void kse_switchout_thread(struct kse *kse, struct pthread *thread) { int level; + int i; /* * Place the currently running thread into the * appropriate queue(s). */ DBG_MSG("Switching out thread %p, state %d\n", thread, thread->state); + + THR_DEACTIVATE_LAST_LOCK(thread); if (thread->blocked != 0) { thread->active = 0; thread->need_switchout = 0; @@ -1473,6 +1536,15 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) } thread->active = 0; thread->need_switchout = 0; + if (thread->check_pending != 0) { + /* Install pending signals into the frame. */ + thread->check_pending = 0; + for (i = 0; i < _SIG_MAXSIG; i++) { + if (sigismember(&thread->sigpend, i) && + !sigismember(&thread->tmbx.tm_context.uc_sigmask, i)) + _thr_sig_add(thread, i, &thread->siginfo[i]); + } + } } /* @@ -1584,37 +1656,6 @@ kse_fini(struct kse *kse) } void -_thr_sig_add(struct pthread *thread, int sig, siginfo_t *info, ucontext_t *ucp) -{ - struct kse *curkse; - - curkse = _get_curkse(); - - KSE_SCHED_LOCK(curkse, thread->kseg); - /* - * A threads assigned KSE can't change out from under us - * when we hold the scheduler lock. - */ - if (THR_IS_ACTIVE(thread)) { - /* Thread is active. Can't install the signal for it. */ - /* Make a note in the thread that it has a signal. */ - sigaddset(&thread->sigpend, sig); - thread->check_pending = 1; - } - else { - /* Make a note in the thread that it has a signal. */ - sigaddset(&thread->sigpend, sig); - thread->check_pending = 1; - - if (thread->blocked != 0) { - /* Tell the kernel to interrupt the thread. */ - kse_thr_interrupt(&thread->tmbx); - } - } - KSE_SCHED_UNLOCK(curkse, thread->kseg); -} - -void _thr_set_timeout(const struct timespec *timeout) { struct pthread *curthread = _get_curthread(); @@ -1675,14 +1716,14 @@ _thr_setrunnable_unlocked(struct pthread *thread) THR_SET_STATE(thread, PS_SUSPENDED); else THR_SET_STATE(thread, PS_RUNNING); - }else if (thread->state != PS_RUNNING) { + } else if (thread->state != PS_RUNNING) { if ((thread->flags & THR_FLAGS_IN_WAITQ) != 0) KSE_WAITQ_REMOVE(thread->kse, thread); if ((thread->flags & THR_FLAGS_SUSPENDED) != 0) THR_SET_STATE(thread, PS_SUSPENDED); else { THR_SET_STATE(thread, PS_RUNNING); - if ((thread->blocked == 0) && + if ((thread->blocked == 0) && (thread->active == 0) && (thread->flags & THR_FLAGS_IN_RUNQ) == 0) THR_RUNQ_INSERT_TAIL(thread); } |