1*8975f5c5SAndroid Build Coastguard Worker // 2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2024 The ANGLE Project Authors. All rights reserved. 3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file. 5*8975f5c5SAndroid Build Coastguard Worker // 6*8975f5c5SAndroid Build Coastguard Worker // SimpleMutex.h: 7*8975f5c5SAndroid Build Coastguard Worker // A simple non-recursive mutex that only supports lock and unlock operations. As such, it can be 8*8975f5c5SAndroid Build Coastguard Worker // implemented more efficiently than a generic mutex such as std::mutex. In the uncontended 9*8975f5c5SAndroid Build Coastguard Worker // paths, the implementation boils down to basically an inlined atomic operation and an untaken 10*8975f5c5SAndroid Build Coastguard Worker // branch. The implementation in this file is inspired by Mesa's src/util/simple_mtx.h, which in 11*8975f5c5SAndroid Build Coastguard Worker // turn is based on "mutex3" in: 12*8975f5c5SAndroid Build Coastguard Worker // 13*8975f5c5SAndroid Build Coastguard Worker // "Futexes Are Tricky" 14*8975f5c5SAndroid Build Coastguard Worker // http://www.akkadia.org/drepper/futex.pdf 15*8975f5c5SAndroid Build Coastguard Worker // 16*8975f5c5SAndroid Build Coastguard Worker // Given that std::condition_variable only interacts with std::mutex, SimpleMutex cannot be used 17*8975f5c5SAndroid Build Coastguard Worker // with condition variables. 18*8975f5c5SAndroid Build Coastguard Worker // 19*8975f5c5SAndroid Build Coastguard Worker 20*8975f5c5SAndroid Build Coastguard Worker #ifndef COMMON_SIMPLEMUTEX_H_ 21*8975f5c5SAndroid Build Coastguard Worker #define COMMON_SIMPLEMUTEX_H_ 22*8975f5c5SAndroid Build Coastguard Worker 23*8975f5c5SAndroid Build Coastguard Worker #include "common/log_utils.h" 24*8975f5c5SAndroid Build Coastguard Worker #include "common/platform.h" 25*8975f5c5SAndroid Build Coastguard Worker 26*8975f5c5SAndroid Build Coastguard Worker #include <atomic> 27*8975f5c5SAndroid Build Coastguard Worker #include <mutex> 28*8975f5c5SAndroid Build Coastguard Worker 29*8975f5c5SAndroid Build Coastguard Worker // Enable futexes on: 30*8975f5c5SAndroid Build Coastguard Worker // 31*8975f5c5SAndroid Build Coastguard Worker // - Linux and derivatives (Android, ChromeOS, etc) 32*8975f5c5SAndroid Build Coastguard Worker // - Windows 8+ 33*8975f5c5SAndroid Build Coastguard Worker // 34*8975f5c5SAndroid Build Coastguard Worker // There is no TSAN support for futex currently, so it is disabled in that case 35*8975f5c5SAndroid Build Coastguard Worker #if !defined(ANGLE_WITH_TSAN) 36*8975f5c5SAndroid Build Coastguard Worker # if defined(ANGLE_PLATFORM_LINUX) || defined(ANGLE_PLATFORM_ANDROID) 37*8975f5c5SAndroid Build Coastguard Worker // Linux has had futexes for a very long time. Assume support. 38*8975f5c5SAndroid Build Coastguard Worker # define ANGLE_USE_FUTEX 1 39*8975f5c5SAndroid Build Coastguard Worker # elif defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_ENABLE_WINDOWS_UWP) && \ 40*8975f5c5SAndroid Build Coastguard Worker !defined(ANGLE_WINDOWS_NO_FUTEX) 41*8975f5c5SAndroid Build Coastguard Worker // Windows has futexes since version 8, which is already end of life (let alone older versions). 42*8975f5c5SAndroid Build Coastguard Worker // Assume support. 43*8975f5c5SAndroid Build Coastguard Worker # define ANGLE_USE_FUTEX 1 44*8975f5c5SAndroid Build Coastguard Worker # endif // defined(ANGLE_PLATFORM_LINUX) || defined(ANGLE_PLATFORM_ANDROID) 45*8975f5c5SAndroid Build Coastguard Worker #endif // !defined(ANGLE_WITH_TSAN) 46*8975f5c5SAndroid Build Coastguard Worker 47*8975f5c5SAndroid Build Coastguard Worker namespace angle 48*8975f5c5SAndroid Build Coastguard Worker { 49*8975f5c5SAndroid Build Coastguard Worker namespace priv 50*8975f5c5SAndroid Build Coastguard Worker { 51*8975f5c5SAndroid Build Coastguard Worker #if ANGLE_USE_FUTEX 52*8975f5c5SAndroid Build Coastguard Worker class MutexOnFutex 53*8975f5c5SAndroid Build Coastguard Worker { 54*8975f5c5SAndroid Build Coastguard Worker public: lock()55*8975f5c5SAndroid Build Coastguard Worker void lock() 56*8975f5c5SAndroid Build Coastguard Worker { 57*8975f5c5SAndroid Build Coastguard Worker uint32_t oldState = kUnlocked; 58*8975f5c5SAndroid Build Coastguard Worker const bool lockTaken = mState.compare_exchange_strong(oldState, kLocked); 59*8975f5c5SAndroid Build Coastguard Worker 60*8975f5c5SAndroid Build Coastguard Worker // In uncontended cases, the lock is acquired and there's nothing to do 61*8975f5c5SAndroid Build Coastguard Worker if (ANGLE_UNLIKELY(!lockTaken)) 62*8975f5c5SAndroid Build Coastguard Worker { 63*8975f5c5SAndroid Build Coastguard Worker ASSERT(oldState == kLocked || oldState == kBlocked); 64*8975f5c5SAndroid Build Coastguard Worker 65*8975f5c5SAndroid Build Coastguard Worker // If not already marked as such, signal that the mutex is contended. 66*8975f5c5SAndroid Build Coastguard Worker if (oldState != kBlocked) 67*8975f5c5SAndroid Build Coastguard Worker { 68*8975f5c5SAndroid Build Coastguard Worker oldState = mState.exchange(kBlocked, std::memory_order_acq_rel); 69*8975f5c5SAndroid Build Coastguard Worker } 70*8975f5c5SAndroid Build Coastguard Worker // Wait until the lock is acquired 71*8975f5c5SAndroid Build Coastguard Worker while (oldState != kUnlocked) 72*8975f5c5SAndroid Build Coastguard Worker { 73*8975f5c5SAndroid Build Coastguard Worker futexWait(); 74*8975f5c5SAndroid Build Coastguard Worker oldState = mState.exchange(kBlocked, std::memory_order_acq_rel); 75*8975f5c5SAndroid Build Coastguard Worker } 76*8975f5c5SAndroid Build Coastguard Worker } 77*8975f5c5SAndroid Build Coastguard Worker } unlock()78*8975f5c5SAndroid Build Coastguard Worker void unlock() 79*8975f5c5SAndroid Build Coastguard Worker { 80*8975f5c5SAndroid Build Coastguard Worker // Unlock the mutex 81*8975f5c5SAndroid Build Coastguard Worker const uint32_t oldState = mState.fetch_add(-1, std::memory_order_acq_rel); 82*8975f5c5SAndroid Build Coastguard Worker 83*8975f5c5SAndroid Build Coastguard Worker // If another thread is waiting on this mutex, wake it up 84*8975f5c5SAndroid Build Coastguard Worker if (ANGLE_UNLIKELY(oldState != kLocked)) 85*8975f5c5SAndroid Build Coastguard Worker { 86*8975f5c5SAndroid Build Coastguard Worker mState.store(kUnlocked, std::memory_order_relaxed); 87*8975f5c5SAndroid Build Coastguard Worker futexWake(); 88*8975f5c5SAndroid Build Coastguard Worker } 89*8975f5c5SAndroid Build Coastguard Worker } assertLocked()90*8975f5c5SAndroid Build Coastguard Worker void assertLocked() { ASSERT(mState.load(std::memory_order_relaxed) != kUnlocked); } 91*8975f5c5SAndroid Build Coastguard Worker 92*8975f5c5SAndroid Build Coastguard Worker private: 93*8975f5c5SAndroid Build Coastguard Worker void futexWait(); 94*8975f5c5SAndroid Build Coastguard Worker void futexWake(); 95*8975f5c5SAndroid Build Coastguard Worker 96*8975f5c5SAndroid Build Coastguard Worker // Note: the ordering of these values is important due to |unlock()|'s atomic decrement. 97*8975f5c5SAndroid Build Coastguard Worker static constexpr uint32_t kUnlocked = 0; 98*8975f5c5SAndroid Build Coastguard Worker static constexpr uint32_t kLocked = 1; 99*8975f5c5SAndroid Build Coastguard Worker static constexpr uint32_t kBlocked = 2; 100*8975f5c5SAndroid Build Coastguard Worker 101*8975f5c5SAndroid Build Coastguard Worker std::atomic_uint32_t mState = 0; 102*8975f5c5SAndroid Build Coastguard Worker }; 103*8975f5c5SAndroid Build Coastguard Worker #else // !ANGLE_USE_FUTEX 104*8975f5c5SAndroid Build Coastguard Worker class MutexOnStd 105*8975f5c5SAndroid Build Coastguard Worker { 106*8975f5c5SAndroid Build Coastguard Worker public: 107*8975f5c5SAndroid Build Coastguard Worker void lock() { mutex.lock(); } 108*8975f5c5SAndroid Build Coastguard Worker void unlock() { mutex.unlock(); } 109*8975f5c5SAndroid Build Coastguard Worker void assertLocked() { ASSERT(isLocked()); } 110*8975f5c5SAndroid Build Coastguard Worker 111*8975f5c5SAndroid Build Coastguard Worker private: 112*8975f5c5SAndroid Build Coastguard Worker bool isLocked() 113*8975f5c5SAndroid Build Coastguard Worker { 114*8975f5c5SAndroid Build Coastguard Worker // This works because angle::SimpleMutex does not support recursion 115*8975f5c5SAndroid Build Coastguard Worker const bool acquiredLock = mutex.try_lock(); 116*8975f5c5SAndroid Build Coastguard Worker if (acquiredLock) 117*8975f5c5SAndroid Build Coastguard Worker { 118*8975f5c5SAndroid Build Coastguard Worker mutex.unlock(); 119*8975f5c5SAndroid Build Coastguard Worker } 120*8975f5c5SAndroid Build Coastguard Worker 121*8975f5c5SAndroid Build Coastguard Worker return !acquiredLock; 122*8975f5c5SAndroid Build Coastguard Worker } 123*8975f5c5SAndroid Build Coastguard Worker 124*8975f5c5SAndroid Build Coastguard Worker std::mutex mutex; 125*8975f5c5SAndroid Build Coastguard Worker }; 126*8975f5c5SAndroid Build Coastguard Worker #endif // ANGLE_USE_FUTEX 127*8975f5c5SAndroid Build Coastguard Worker } // namespace priv 128*8975f5c5SAndroid Build Coastguard Worker 129*8975f5c5SAndroid Build Coastguard Worker #if ANGLE_USE_FUTEX 130*8975f5c5SAndroid Build Coastguard Worker using SimpleMutex = priv::MutexOnFutex; 131*8975f5c5SAndroid Build Coastguard Worker #else 132*8975f5c5SAndroid Build Coastguard Worker using SimpleMutex = priv::MutexOnStd; 133*8975f5c5SAndroid Build Coastguard Worker #endif 134*8975f5c5SAndroid Build Coastguard Worker 135*8975f5c5SAndroid Build Coastguard Worker // A no-op mutex to replace SimpleMutex where a lock is not needed. 136*8975f5c5SAndroid Build Coastguard Worker struct NoOpMutex 137*8975f5c5SAndroid Build Coastguard Worker { lockNoOpMutex138*8975f5c5SAndroid Build Coastguard Worker void lock() {} unlockNoOpMutex139*8975f5c5SAndroid Build Coastguard Worker void unlock() {} try_lockNoOpMutex140*8975f5c5SAndroid Build Coastguard Worker bool try_lock() { return true; } 141*8975f5c5SAndroid Build Coastguard Worker }; 142*8975f5c5SAndroid Build Coastguard Worker } // namespace angle 143*8975f5c5SAndroid Build Coastguard Worker 144*8975f5c5SAndroid Build Coastguard Worker #endif // COMMON_SIMPLEMUTEX_H_ 145