blob: f7944388192f24221bf4dacd8e9544ada0de7d0b [file] [log] [blame]
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/components/nearby/condition_variable_impl.h"
#include "chromeos/components/nearby/lock_base.h"
namespace chromeos {
namespace nearby {
ConditionVariableImpl::ConditionVariableImpl(location::nearby::Lock* lock)
: lock_(lock),
notify_has_been_called_(base::WaitableEvent::ResetPolicy::AUTOMATIC) {}
ConditionVariableImpl::~ConditionVariableImpl() = default;
void ConditionVariableImpl::notify() {
// Signal() will signal a single outstanding call to Wait(), since it's set to
// ResetPolicy::AUTOMATIC.
notify_has_been_called_.Signal();
}
location::nearby::Exception::Value ConditionVariableImpl::wait() {
lock_->unlock();
// If |lock_| is still held by the current thread here (e.g., due to it being
// a recursive lock), then no other thread will be able to acquire |lock_| in
// order to modify whatever condition this ConditionVariable is waiting on.
// In that case, the following call to WaitableEvent::Wait() will likely block
// forever.
DCHECK(!(static_cast<LockBase* const>(lock_)->IsHeldByCurrentThread()));
notify_has_been_called_.Wait();
lock_->lock();
// Because |notify_has_been_called_| is set to ResetPolicy::AUTOMATIC,
// Signal() will only wake up ONE outstanding call to Wait().
// ConditionVariable::notify() is expected to notify ALL outstanding threads,
// but since WaitableEvent does not currently have a public broadcast method,
// this Signal() will make sure to wake up the next outstanding thread.
//
// NOTE: There exists a small inefficiency here where if this is the last
// waiting thread, then the Signal() will persist until the next call to
// notify_has_been_called_.Wait(), causing that next call to return
// immediately. This should be fine since calling ConditionVariable::wait()
// should be done within a while-loop anyways, as in:
//
// lock.lock();
// while (!condition_is_true)
// condition_variable.wait();
// // do stuff while condition is true
// lock.unlock();
//
// The persisting Signal() should merely cause one extra loop around, properly
// blocking the second time ConditionVariable::wait() is called.
notify_has_been_called_.Signal();
return location::nearby::Exception::NONE;
}
} // namespace nearby
} // namespace chromeos