| // |
| // Copyright 2024 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // SimpleMutex.h: |
| // A simple non-recursive mutex that only supports lock and unlock operations. As such, it can be |
| // implemented more efficiently than a generic mutex such as std::mutex. In the uncontended |
| // paths, the implementation boils down to basically an inlined atomic operation and an untaken |
| // branch. The implementation in this file is inspired by Mesa's src/util/simple_mtx.h, which in |
| // turn is based on "mutex3" in: |
| // |
| // "Futexes Are Tricky" |
| // http://www.akkadia.org/drepper/futex.pdf |
| // |
| // Given that std::condition_variable only interacts with std::mutex, SimpleMutex cannot be used |
| // with condition variables. |
| // |
| |
| #ifndef COMMON_SIMPLEMUTEX_H_ |
| #define COMMON_SIMPLEMUTEX_H_ |
| |
| #include "common/log_utils.h" |
| #include "common/platform.h" |
| |
| #include <atomic> |
| #include <mutex> |
| |
| // Enable futexes on: |
| // |
| // - Linux and derivatives (Android, ChromeOS, etc) |
| // - Windows 8+ |
| // |
| // There is no TSAN support for futex currently, so it is disabled in that case |
| #if !defined(ANGLE_WITH_TSAN) |
| # if defined(ANGLE_PLATFORM_LINUX) || defined(ANGLE_PLATFORM_ANDROID) |
| // Linux has had futexes for a very long time. Assume support. |
| # define ANGLE_USE_FUTEX 1 |
| # elif defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_ENABLE_WINDOWS_UWP) && \ |
| !defined(ANGLE_WINDOWS_NO_FUTEX) |
| // Windows has futexes since version 8, which is already end of life (let alone older versions). |
| // Assume support. |
| # define ANGLE_USE_FUTEX 1 |
| # endif // defined(ANGLE_PLATFORM_LINUX) || defined(ANGLE_PLATFORM_ANDROID) |
| #endif // !defined(ANGLE_WITH_TSAN) |
| |
| namespace angle |
| { |
| namespace priv |
| { |
| #if ANGLE_USE_FUTEX |
| class MutexOnFutex |
| { |
| public: |
| void lock() |
| { |
| uint32_t oldState = kUnlocked; |
| const bool lockTaken = mState.compare_exchange_strong(oldState, kLocked); |
| |
| // In uncontended cases, the lock is acquired and there's nothing to do |
| if (ANGLE_UNLIKELY(!lockTaken)) |
| { |
| ASSERT(oldState == kLocked || oldState == kBlocked); |
| |
| // If not already marked as such, signal that the mutex is contended. |
| if (oldState != kBlocked) |
| { |
| oldState = mState.exchange(kBlocked, std::memory_order_acq_rel); |
| } |
| // Wait until the lock is acquired |
| while (oldState != kUnlocked) |
| { |
| futexWait(); |
| oldState = mState.exchange(kBlocked, std::memory_order_acq_rel); |
| } |
| } |
| } |
| void unlock() |
| { |
| // Unlock the mutex |
| const uint32_t oldState = mState.fetch_add(-1, std::memory_order_acq_rel); |
| |
| // If another thread is waiting on this mutex, wake it up |
| if (ANGLE_UNLIKELY(oldState != kLocked)) |
| { |
| mState.store(kUnlocked, std::memory_order_relaxed); |
| futexWake(); |
| } |
| } |
| void assertLocked() { ASSERT(mState.load(std::memory_order_relaxed) != kUnlocked); } |
| |
| private: |
| void futexWait(); |
| void futexWake(); |
| |
| // Note: the ordering of these values is important due to |unlock()|'s atomic decrement. |
| static constexpr uint32_t kUnlocked = 0; |
| static constexpr uint32_t kLocked = 1; |
| static constexpr uint32_t kBlocked = 2; |
| |
| std::atomic_uint32_t mState = 0; |
| }; |
| #else // !ANGLE_USE_FUTEX |
| class MutexOnStd |
| { |
| public: |
| void lock() { mutex.lock(); } |
| void unlock() { mutex.unlock(); } |
| void assertLocked() { ASSERT(isLocked()); } |
| |
| private: |
| bool isLocked() |
| { |
| // This works because angle::SimpleMutex does not support recursion |
| const bool acquiredLock = mutex.try_lock(); |
| if (acquiredLock) |
| { |
| mutex.unlock(); |
| } |
| |
| return !acquiredLock; |
| } |
| |
| std::mutex mutex; |
| }; |
| #endif // ANGLE_USE_FUTEX |
| } // namespace priv |
| |
| #if ANGLE_USE_FUTEX |
| using SimpleMutex = priv::MutexOnFutex; |
| #else |
| using SimpleMutex = priv::MutexOnStd; |
| #endif |
| |
| } // namespace angle |
| |
| #endif // COMMON_SIMPLEMUTEX_H_ |