diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/WTF/wtf/Condition.h | |
parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/WTF/wtf/Condition.h')
-rw-r--r-- | Source/WTF/wtf/Condition.h | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/Source/WTF/wtf/Condition.h b/Source/WTF/wtf/Condition.h new file mode 100644 index 000000000..80c07293a --- /dev/null +++ b/Source/WTF/wtf/Condition.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WTF_Condition_h +#define WTF_Condition_h + +#include <chrono> +#include <functional> +#include <wtf/CurrentTime.h> +#include <wtf/Noncopyable.h> +#include <wtf/ParkingLot.h> + +namespace WTF { + +// This is a condition variable that is suitable for use with any lock-like object, including +// our own WTF::Lock. It features standard wait()/notifyOne()/notifyAll() methods in addition to +// a variety of wait-with-timeout methods. This includes methods that use WTF's own notion of +// time, like wall-clock time (i.e. currentTime()) and monotonic time (i.e. +// monotonicallyIncreasingTime()). This is a very efficient condition variable. It only requires +// one byte of memory. notifyOne() and notifyAll() require just a load and branch for the fast +// case where no thread is waiting. This condition variable, when used with WTF::Lock, can +// outperform a system condition variable and lock by up to 58x. + +// This is a struct without a constructor or destructor so that it can be statically initialized. +// Use Lock in instance variables. +struct ConditionBase { + typedef ParkingLot::Clock Clock; + + // Wait on a parking queue while releasing the given lock. It will unlock the lock just before + // parking, and relock it upon wakeup. Returns true if we woke up due to some call to + // notifyOne() or notifyAll(). Returns false if we woke up due to a timeout. Note that this form + // of waitUntil() has some quirks: + // + // No spurious wake-up: in order for this to return before the timeout, some notifyOne() or + // notifyAll() call must have happened. No scenario other than timeout or notify can lead to this + // method returning. This means, for example, that you can't use pthread cancelation or signals to + // cause early return. + // + // Past timeout: it's possible for waitUntil() to be called with a timeout in the past. In that + // case, waitUntil() will still release the lock and reacquire it. waitUntil() will always return + // false in that case. This is subtly different from some pthread_cond_timedwait() implementations, + // which may not release the lock for past timeout. But, this behavior is consistent with OpenGroup + // documentation for timedwait(). + template<typename LockType> + bool waitUntil(LockType& lock, Clock::time_point timeout) + { + bool result; + if (timeout < Clock::now()) { + lock.unlock(); + result = false; + } else { + result = ParkingLot::parkConditionally( + &m_hasWaiters, + [this] () -> bool { + // Let everyone know that we will be waiting. Do this while we hold the queue lock, + // to prevent races with notifyOne(). + m_hasWaiters.store(true); + return true; + }, + [&lock] () { lock.unlock(); }, + timeout); + } + lock.lock(); + return result; + } + + // Wait until the given predicate is satisfied. Returns true if it is satisfied in the end. + // May return early due to timeout. + template<typename LockType, typename Functor> + bool waitUntil(LockType& lock, Clock::time_point timeout, const Functor& predicate) + { + while (!predicate()) { + if (!waitUntil(lock, timeout)) + return predicate(); + } + return true; + } + + // Wait until the given predicate is satisfied. Returns true if it is satisfied in the end. + // May return early due to timeout. + template<typename LockType, typename DurationType, typename Functor> + bool waitFor( + LockType& lock, const DurationType& relativeTimeout, const Functor& predicate) + { + return waitUntil(lock, absoluteFromRelative(relativeTimeout), predicate); + } + + template<typename LockType> + void wait(LockType& lock) + { + waitUntil(lock, Clock::time_point::max()); + } + + template<typename LockType, typename Functor> + void wait(LockType& lock, const Functor& predicate) + { + while (!predicate()) + wait(lock); + } + + template<typename LockType, typename TimeType> + bool waitUntil(LockType& lock, const TimeType& timeout) + { + if (timeout == TimeType::max()) { + wait(lock); + return true; + } + return waitForImpl(lock, timeout - TimeType::clock::now()); + } + + template<typename LockType> + bool waitUntilWallClockSeconds(LockType& lock, double absoluteTimeoutSeconds) + { + return waitForSecondsImpl(lock, absoluteTimeoutSeconds - currentTime()); + } + + template<typename LockType> + bool waitUntilMonotonicClockSeconds(LockType& lock, double absoluteTimeoutSeconds) + { + return waitForSecondsImpl(lock, absoluteTimeoutSeconds - monotonicallyIncreasingTime()); + } + + // Note that this method is extremely fast when nobody is waiting. It is not necessary to try to + // avoid calling this method. + void notifyOne() + { + if (!m_hasWaiters.load()) { + // At this exact instant, there is nobody waiting on this condition. The way to visualize + // this is that if unparkOne() ran to completion without obstructions at this moment, it + // wouldn't wake anyone up. Hence, we have nothing to do! + return; + } + + ParkingLot::unparkOne( + &m_hasWaiters, + [this] (bool, bool mayHaveMoreThreads) { + if (!mayHaveMoreThreads) + m_hasWaiters.store(false); + }); + } + + void notifyAll() + { + if (!m_hasWaiters.load()) { + // See above. + return; + } + + // It's totally safe for us to set this to false without any locking, because this thread is + // guaranteed to then unparkAll() anyway. So, if there is a race with some thread calling + // wait() just before this store happens, that thread is guaranteed to be awoken by the call to + // unparkAll(), below. + m_hasWaiters.store(false); + + ParkingLot::unparkAll(&m_hasWaiters); + } + +protected: + template<typename LockType> + bool waitForSecondsImpl(LockType& lock, double relativeTimeoutSeconds) + { + double relativeTimeoutNanoseconds = relativeTimeoutSeconds * (1000.0 * 1000.0 * 1000.0); + + if (!(relativeTimeoutNanoseconds > 0)) { + // This handles insta-timeouts as well as NaN. + lock.unlock(); + lock.lock(); + return false; + } + + if (relativeTimeoutNanoseconds > static_cast<double>(std::numeric_limits<int64_t>::max())) { + // If the timeout in nanoseconds cannot be expressed using a 64-bit integer, then we + // might as well wait forever. + wait(lock); + return true; + } + + auto relativeTimeout = + std::chrono::nanoseconds(static_cast<int64_t>(relativeTimeoutNanoseconds)); + + return waitForImpl(lock, relativeTimeout); + } + + template<typename LockType, typename DurationType> + bool waitForImpl(LockType& lock, const DurationType& relativeTimeout) + { + return waitUntil(lock, absoluteFromRelative(relativeTimeout)); + } + + template<typename DurationType> + Clock::time_point absoluteFromRelative(const DurationType& relativeTimeout) + { + if (relativeTimeout < DurationType::zero()) + return Clock::time_point::min(); + + if (relativeTimeout > Clock::duration::max()) { + // This is highly unlikely. But if it happens, we want to not do anything dumb. Sleeping + // without a timeout seems sensible when the timeout duration is greater than what can be + // expressed using steady_clock. + return Clock::time_point::max(); + } + + Clock::duration myRelativeTimeout = + std::chrono::duration_cast<Clock::duration>(relativeTimeout); + + return Clock::now() + myRelativeTimeout; + } + + Atomic<bool> m_hasWaiters; +}; + +class Condition : public ConditionBase { + WTF_MAKE_NONCOPYABLE(Condition); +public: + Condition() + { + m_hasWaiters.store(false); + } +}; + +typedef ConditionBase StaticCondition; + +} // namespace WTF + +using WTF::Condition; +using WTF::StaticCondition; + +#endif // WTF_Condition_h + |