LockTests.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/std/parallel/lock.h>
  9. #include "UserTypes.h"
  10. namespace UnitTest
  11. {
  12. // Fixture for non-typed tests
  13. class LockTest
  14. : public LeakDetectionFixture
  15. {
  16. };
  17. struct LockableBoolHelper
  18. {
  19. LockableBoolHelper() = default;
  20. void lock()
  21. {
  22. m_locked = true;
  23. }
  24. void unlock()
  25. {
  26. m_locked = false;
  27. }
  28. bool try_lock()
  29. {
  30. if (!m_locked)
  31. {
  32. m_locked = true;
  33. return true;
  34. }
  35. return false;
  36. }
  37. bool m_locked{};
  38. };
  39. struct TryLockSetBoolFalse
  40. {
  41. TryLockSetBoolFalse() = default;
  42. void lock()
  43. {
  44. m_locked = true;
  45. }
  46. void unlock()
  47. {
  48. m_locked = false;
  49. }
  50. bool try_lock()
  51. {
  52. m_locked = false;
  53. return m_locked;
  54. }
  55. bool m_locked{};
  56. };
  57. static uint32_t m_nonAtomicAccumulator1{ 0 };
  58. static uint32_t m_nonAtomicAccumulator2{ 0 };
  59. template<typename Lockable1, typename Lockable2>
  60. static void Thread1Test(Lockable1& lockable1, Lockable2& lockable2)
  61. {
  62. LockableBoolHelper lockableBoolHelper;
  63. AZStd::lock(lockable1, lockable2, lockableBoolHelper);
  64. m_nonAtomicAccumulator1 += 2;
  65. m_nonAtomicAccumulator2 += 3;
  66. EXPECT_TRUE(lockableBoolHelper.m_locked);
  67. lockable2.unlock();
  68. lockable1.unlock();
  69. }
  70. template<typename Lockable1, typename Lockable2>
  71. static void Thread2Test(Lockable1& lockable1, Lockable2& lockable2)
  72. {
  73. LockableBoolHelper lockableBoolHelper;
  74. AZStd::lock(lockable1, lockableBoolHelper, lockable2);
  75. m_nonAtomicAccumulator1 += 5;
  76. m_nonAtomicAccumulator2 += 1;
  77. EXPECT_TRUE(lockableBoolHelper.m_locked);
  78. lockable1.unlock();
  79. lockable2.unlock();
  80. }
  81. TEST_F(LockTest, lock_LockTwoMutexesAtOnce_BothLockedSuccess)
  82. {
  83. // lockable bool helper case
  84. LockableBoolHelper testLockable1;
  85. LockableBoolHelper testLockable2;
  86. AZStd::lock(testLockable1, testLockable2);
  87. EXPECT_TRUE(testLockable1.m_locked);
  88. EXPECT_TRUE(testLockable2.m_locked);
  89. }
  90. TEST_F(LockTest, lock_LockMultipleMutexesAtOnceOnMultipleThreadsInDifferentOrders_LockedWithoutDeadlock)
  91. {
  92. // Multi thread case
  93. constexpr size_t numThreads = 2;
  94. AZStd::mutex testMutex1;
  95. AZStd::mutex testMutex2;
  96. AZStd::thread threads[numThreads];
  97. threads[0] = AZStd::thread([&mutex1 = testMutex1, &mutex2 = testMutex2]()
  98. {
  99. Thread1Test(mutex1, mutex2);
  100. });
  101. threads[1] = AZStd::thread([&mutex1 = testMutex1, &mutex2 = testMutex2]()
  102. {
  103. Thread2Test(mutex2, mutex1);
  104. });
  105. threads[0].join();
  106. threads[1].join();
  107. }
  108. TEST_F(LockTest, try_lock_AttemptLockMultipleAtOnce_AllMutexesAreLocked)
  109. {
  110. LockableBoolHelper lockableBool1;
  111. LockableBoolHelper lockableBool2;
  112. EXPECT_EQ(-1, AZStd::try_lock(lockableBool1, lockableBool2));
  113. // LockableBoolHelper locks are still engaged, therefore lockable bool should fail first
  114. EXPECT_EQ(0, AZStd::try_lock(lockableBool1, lockableBool2));
  115. lockableBool1.unlock();
  116. EXPECT_EQ(1, AZStd::try_lock(lockableBool1, lockableBool2));
  117. lockableBool2.unlock();
  118. }
  119. TEST_F(LockTest, try_lock_AttemptLockMultipleAtOnceWith_TryLockSetBoolFalse_Type_ThatAlwaysReturnsFalseForTryLock_TryLockReturnsMutexThatFailedToLock)
  120. {
  121. LockableBoolHelper lockableBool1;
  122. LockableBoolHelper lockableBool2;
  123. TryLockSetBoolFalse lockableTryLockFalse1;
  124. EXPECT_EQ(1, AZStd::try_lock(lockableBool2, lockableTryLockFalse1, lockableBool1));
  125. }
  126. TEST_F(LockTest, try_lock_AttemptLockMultipleAtOnceWithMutexThatIsLocked_TryLockReturnsFirstMutexThatHasBeenLocked)
  127. {
  128. LockableBoolHelper lockableBool1;
  129. LockableBoolHelper lockableBool2;
  130. TryLockSetBoolFalse lockableTryLockFalse1;
  131. EXPECT_EQ(2, AZStd::try_lock(lockableBool1, lockableBool2, lockableTryLockFalse1));
  132. lockableBool1.lock();
  133. // Note lockableBool1 has been swapped with the lockableBool2 parameter and locked
  134. EXPECT_EQ(1, AZStd::try_lock(lockableBool2, lockableBool1, lockableTryLockFalse1));
  135. }
  136. }