Hash :
97cb3eb8
Author :
Date :
2024-04-16T10:25:49
Split ASSERT and logs into separate header This is because SimpleMutex.h wants to ASSERT, which was defined in debug.h. That file has a function that returns a reference to a mutex that would eventually be changed to SimpleMutex. The circular dependency cannot be resolved with a forward declaration with SimpleMutex being defined with `using`. Bug: angleproject:8667 Change-Id: I9a3acb6d07c6702048b47a72d8411b0fc2166922 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5458631 Reviewed-by: Roman Lavrov <romanl@google.com> Commit-Queue: Roman Lavrov <romanl@google.com> Auto-Submit: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
//
// 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_