diff options
Diffstat (limited to 'Source/WTF/wtf/Lock.h')
-rw-r--r-- | Source/WTF/wtf/Lock.h | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/Source/WTF/wtf/Lock.h b/Source/WTF/wtf/Lock.h new file mode 100644 index 000000000..9dec84c1f --- /dev/null +++ b/Source/WTF/wtf/Lock.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015-2016 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_Lock_h +#define WTF_Lock_h + +#include <wtf/LockAlgorithm.h> +#include <wtf/Locker.h> +#include <wtf/Noncopyable.h> + +namespace TestWebKitAPI { +struct LockInspector; +}; + +namespace WTF { + +typedef LockAlgorithm<uint8_t, 1, 2> DefaultLockAlgorithm; + +// This is a fully adaptive mutex that only requires 1 byte of storage. It has fast paths that are +// competetive to a spinlock (uncontended locking is inlined and is just a CAS, microcontention is +// handled by spinning and yielding), and a slow path that is competetive to std::mutex (if a lock +// cannot be acquired in a short period of time, the thread is put to sleep until the lock is +// available again). It uses less memory than a std::mutex. This lock guarantees eventual stochastic +// fairness, even in programs that relock the lock immediately after unlocking it. Except when there +// are collisions between this lock and other locks in the ParkingLot, this lock will guarantee that +// at worst one call to unlock() per millisecond will do a direct hand-off to the thread that is at +// the head of the queue. When there are collisions, each collision increases the fair unlock delay +// by one millisecond in the worst case. + +// This is a struct without a constructor or destructor so that it can be statically initialized. +// Use Lock in instance variables. +struct LockBase { + void lock() + { + if (UNLIKELY(!DefaultLockAlgorithm::lockFastAssumingZero(m_byte))) + lockSlow(); + } + + bool tryLock() + { + return DefaultLockAlgorithm::tryLock(m_byte); + } + + // Need this version for std::unique_lock. + bool try_lock() + { + return tryLock(); + } + + // Relinquish the lock. Either one of the threads that were waiting for the lock, or some other + // thread that happens to be running, will be able to grab the lock. This bit of unfairness is + // called barging, and we allow it because it maximizes throughput. However, we bound how unfair + // barging can get by ensuring that every once in a while, when there is a thread waiting on the + // lock, we hand the lock to that thread directly. Every time unlock() finds a thread waiting, + // we check if the last time that we did a fair unlock was more than roughly 1ms ago; if so, we + // unlock fairly. Fairness matters most for long critical sections, and this virtually + // guarantees that long critical sections always get a fair lock. + void unlock() + { + if (UNLIKELY(!DefaultLockAlgorithm::unlockFastAssumingZero(m_byte))) + unlockSlow(); + } + + // This is like unlock() but it guarantees that we unlock the lock fairly. For short critical + // sections, this is much slower than unlock(). For long critical sections, unlock() will learn + // to be fair anyway. However, if you plan to relock the lock right after unlocking and you want + // to ensure that some other thread runs in the meantime, this is probably the function you + // want. + void unlockFairly() + { + if (UNLIKELY(!DefaultLockAlgorithm::unlockFastAssumingZero(m_byte))) + unlockFairlySlow(); + } + + void safepoint() + { + if (UNLIKELY(!DefaultLockAlgorithm::safepointFast(m_byte))) + safepointSlow(); + } + + bool isHeld() const + { + return DefaultLockAlgorithm::isLocked(m_byte); + } + + bool isLocked() const + { + return isHeld(); + } + +protected: + friend struct TestWebKitAPI::LockInspector; + + static const uint8_t isHeldBit = 1; + static const uint8_t hasParkedBit = 2; + + WTF_EXPORT_PRIVATE void lockSlow(); + WTF_EXPORT_PRIVATE void unlockSlow(); + WTF_EXPORT_PRIVATE void unlockFairlySlow(); + WTF_EXPORT_PRIVATE void safepointSlow(); + + // Method used for testing only. + bool isFullyReset() const + { + return !m_byte.load(); + } + + Atomic<uint8_t> m_byte; +}; + +class Lock : public LockBase { + WTF_MAKE_NONCOPYABLE(Lock); + WTF_MAKE_FAST_ALLOCATED; +public: + Lock() + { + m_byte.store(0, std::memory_order_relaxed); + } +}; + +typedef LockBase StaticLock; +typedef Locker<LockBase> LockHolder; + +} // namespace WTF + +using WTF::Lock; +using WTF::LockHolder; +using WTF::StaticLock; + +#endif // WTF_Lock_h + |