diff --git a/src/system/kernel/condition_variable.cpp b/src/system/kernel/condition_variable.cpp index 04cfc722e1..7c624ea5f4 100644 --- a/src/system/kernel/condition_variable.cpp +++ b/src/system/kernel/condition_variable.cpp @@ -398,17 +398,27 @@ ConditionVariable::_NotifyLocked(bool all, status_t result) panic("entries count was not decremented for a long time!"); } } else { - const status_t waitStatus = atomic_get_and_set(&entry->fWaitStatus, result); + status_t waitStatus = atomic_get_and_set(&entry->fWaitStatus, result); - // We need not waste time trying to unblock the thread if it already unblocked. - if (waitStatus == STATUS_WAITING && thread_is_blocked(thread)) - thread_unblock(thread, result); + SpinLocker threadLocker(thread->scheduler_lock); + if (waitStatus == STATUS_WAITING && thread->state != B_THREAD_WAITING) { + // The thread is not in B_THREAD_WAITING state, so we must unblock it early, + // in case it tries to re-block itself immediately after we unset fVariable. + thread_unblock_locked(thread, result); + waitStatus = result; + } // No matter what the thread is doing, as we were the ones to clear its // fThread, so we are the ones responsible for decrementing fEntriesCount. // (We may not validly access the entry once we unset its fVariable.) atomic_pointer_set(&entry->fVariable, (ConditionVariable*)NULL); atomic_add(&fEntriesCount, -1); + + // If the thread was in B_THREAD_WAITING state, we unblock it after unsetting + // fVariable, because otherwise it will wake up before thread_unblock returns + // and spin while waiting for us to do so. + if (waitStatus == STATUS_WAITING) + thread_unblock_locked(thread, result); } if (!all)