22// Created by X-ray on 05/04/2025.
33//
44
5- #include " spinlock_test.hpp"
5+ #include < future>
6+ #include < base-common/concurrency/spinlock.hpp>
7+ #include < gtest/gtest.h>
8+
9+ inline std::atomic<bool > keep_lock = true ;
10+
11+ template <class T > requires std::is_same_v<T, base::common::concurrency::Spinlock> or std::is_same_v<T, base::common::concurrency::RecursiveSpinlock>
12+ void WaitForUnlock (T& spinlock) {
13+ base::common::concurrency::ScopedSpinlock<T> lock (spinlock);
14+
15+ const auto start_time = std::chrono::steady_clock::now ();
16+ constexpr auto timeout = std::chrono::seconds (5 ); // After 5 seconds we can safely say this isn't going to work
17+
18+ while (keep_lock) {
19+ std::this_thread::yield ();
20+
21+ // Check if we've exceeded the timeout
22+ if (std::chrono::steady_clock::now () - start_time > timeout) {
23+ throw std::runtime_error (" WaitForUnlock: Has timed out" );
24+ }
25+ }
26+ }
27+
28+ TEST (SpinLock, TryLock) {
29+ base::common::concurrency::Spinlock spinlock;
30+ keep_lock = true ;
31+
32+ const auto fut = std::async (std::launch::async, WaitForUnlock<base::common::concurrency::Spinlock>, std::ref (spinlock));
33+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 )); // Need to make sure the new thread actually had time to take the lock
34+
35+ ASSERT_FALSE (spinlock.TryLock ());
36+ keep_lock = false ;
37+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
38+ ASSERT_TRUE (spinlock.TryLock ());
39+ }
40+
41+ TEST (SpinLock, LockUnlock) {
42+ base::common::concurrency::Spinlock spinlock;
43+ keep_lock = false ;
44+
45+ spinlock.Lock ();
46+
47+ const auto fut = std::async (std::launch::async, WaitForUnlock<base::common::concurrency::Spinlock>, std::ref (spinlock));
48+
49+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
50+ ASSERT_NE (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
51+
52+ spinlock.Unlock ();
53+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
54+ ASSERT_EQ (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
55+ }
56+
57+ TEST (SpinLock, ScopedSpinlock) {
58+ base::common::concurrency::Spinlock spinlock;
59+ {
60+ base::common::concurrency::ScopedSpinlock lock (spinlock);
61+ ASSERT_FALSE (spinlock.TryLock ()); // Should not be able to acquire the lock again
62+ }
63+ ASSERT_TRUE (spinlock.TryLock ()); // The lock should now be released
64+ spinlock.Unlock ();
65+ }
66+
67+ // Now for recursive spinlock
68+ TEST (RecursiveSpinLock, TryLock) {
69+ base::common::concurrency::RecursiveSpinlock spinlock;
70+ keep_lock = true ;
71+
72+ auto fut = std::async (std::launch::async, WaitForUnlock<base::common::concurrency::RecursiveSpinlock>, std::ref (spinlock));
73+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 )); // Need to make sure the new thread actually had time to take the lock
74+
75+ ASSERT_FALSE (spinlock.TryLock ());
76+ keep_lock = false ;
77+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
78+ ASSERT_EQ (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
79+
80+ ASSERT_TRUE (spinlock.TryLock ());
81+ ASSERT_TRUE (spinlock.TryLock ());
82+
83+ fut = std::async (std::launch::async, WaitForUnlock<base::common::concurrency::RecursiveSpinlock>, std::ref (spinlock));
84+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
85+ ASSERT_NE (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
86+
87+ keep_lock = false ;
88+ spinlock.Unlock ();
89+ spinlock.Unlock ();
90+
91+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
92+ ASSERT_EQ (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
93+ }
94+
95+ TEST (RecursiveSpinLock, LockUnlock) {
96+ base::common::concurrency::RecursiveSpinlock spinlock;
97+ keep_lock = false ;
98+
99+ spinlock.Lock ();
100+
101+ const auto fut = std::async (std::launch::async, WaitForUnlock<base::common::concurrency::RecursiveSpinlock>, std::ref (spinlock));
102+
103+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
104+ ASSERT_NE (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
105+
106+ spinlock.Unlock ();
107+ std::this_thread::sleep_for (std::chrono::milliseconds (1000 ));
108+ ASSERT_EQ (fut.wait_for (std::chrono::seconds (0 )), std::future_status::ready);
109+ }
110+
111+ TEST (RecursiveSpinLock, ScopedSpinlock) {
112+ base::common::concurrency::RecursiveSpinlock spinlock;
113+ {
114+ base::common::concurrency::ScopedSpinlock lock (spinlock);
115+ ASSERT_TRUE (spinlock.TryLock ()); // Should be able to acquire the lock again (recursive)
116+ spinlock.Unlock ();
117+ }
118+ ASSERT_TRUE (spinlock.TryLock ()); // The lock should now be released
119+ spinlock.Unlock ();
120+ }
0 commit comments