| // | |
| // Copyright 2023 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. | |
| // | |
| // GlobalMutex_unittest: | |
| // Tests of the Scoped<*>GlobalMutexLock classes | |
| // | |
| #include <gtest/gtest.h> | |
| #include "libANGLE/GlobalMutex.h" | |
| namespace | |
| { | |
| template <class ScopedGlobalLockT, class... Args> | |
| void runBasicGlobalMutexTest(bool expectToPass, Args &&...args) | |
| { | |
| constexpr size_t kThreadCount = 16; | |
| constexpr size_t kIterationCount = 50'000; | |
| std::array<std::thread, kThreadCount> threads; | |
| std::mutex mutex; | |
| std::condition_variable condVar; | |
| size_t readyCount = 0; | |
| std::atomic<size_t> testVar; | |
| for (size_t i = 0; i < kThreadCount; ++i) | |
| { | |
| threads[i] = std::thread([&]() { | |
| { | |
| std::unique_lock<std::mutex> lock(mutex); | |
| ++readyCount; | |
| if (readyCount < kThreadCount) | |
| { | |
| condVar.wait(lock, [&]() { return readyCount == kThreadCount; }); | |
| } | |
| else | |
| { | |
| condVar.notify_all(); | |
| } | |
| } | |
| for (size_t j = 0; j < kIterationCount; ++j) | |
| { | |
| ScopedGlobalLockT lock(std::forward<Args>(args)...); | |
| const int local = testVar.load(std::memory_order_relaxed); | |
| const int newValue = local + 1; | |
| testVar.store(newValue, std::memory_order_relaxed); | |
| } | |
| }); | |
| } | |
| for (size_t i = 0; i < kThreadCount; ++i) | |
| { | |
| threads[i].join(); | |
| } | |
| if (expectToPass) | |
| { | |
| EXPECT_EQ(testVar.load(), kThreadCount * kIterationCount); | |
| } | |
| else | |
| { | |
| EXPECT_LE(testVar.load(), kThreadCount * kIterationCount); | |
| } | |
| } | |
| // Tests basic usage of ScopedGlobalMutexLock. | |
| TEST(GlobalMutexTest, ScopedGlobalMutexLock) | |
| { | |
| runBasicGlobalMutexTest<egl::ScopedGlobalMutexLock>(true); | |
| } | |
| // Tests basic usage of ScopedOptionalGlobalMutexLock (Enabled). | |
| TEST(GlobalMutexTest, ScopedOptionalGlobalMutexLockEnabled) | |
| { | |
| runBasicGlobalMutexTest<egl::ScopedOptionalGlobalMutexLock>(true, true); | |
| } | |
| // Tests basic usage of ScopedOptionalGlobalMutexLock (Disabled). | |
| TEST(GlobalMutexTest, ScopedOptionalGlobalMutexLockDisabled) | |
| { | |
| runBasicGlobalMutexTest<egl::ScopedOptionalGlobalMutexLock>(false, false); | |
| } | |
| #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION) | |
| // Tests that ScopedGlobalMutexLock can be recursively locked. | |
| TEST(GlobalMutexTest, RecursiveScopedGlobalMutexLock) | |
| { | |
| egl::ScopedGlobalMutexLock lock; | |
| egl::ScopedGlobalMutexLock lock2; | |
| } | |
| // Tests that ScopedOptionalGlobalMutexLock can be recursively locked. | |
| TEST(GlobalMutexTest, RecursiveScopedOptionalGlobalMutexLock) | |
| { | |
| egl::ScopedOptionalGlobalMutexLock lock(true); | |
| egl::ScopedOptionalGlobalMutexLock lock2(true); | |
| } | |
| #endif | |
| } // anonymous namespace |