File tree 3 files changed +137
-0
lines changed
3 files changed +137
-0
lines changed Original file line number Diff line number Diff line change
1
+ //
2
+ // Created by konstantin on 28.07.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ #include < atomic>
8
+
9
+ namespace NSync {
10
+
11
+ class QueueSpinLock final {
12
+ public:
13
+ class Guard final {
14
+ public:
15
+ explicit Guard (QueueSpinLock& host) : host(host) {
16
+ host.Acquire (this );
17
+ }
18
+ ~Guard () {
19
+ host.Release (this );
20
+ }
21
+
22
+ void SetOwner () {
23
+ is_owner.store (true );
24
+ }
25
+
26
+ void SetNext (Guard* guard) {
27
+ next.store (guard);
28
+ }
29
+
30
+ bool IsOwner () const {
31
+ return is_owner.load ();
32
+ }
33
+
34
+ bool HasNext () const {
35
+ return next.load () != nullptr ;
36
+ }
37
+
38
+ void SetNextOwner () {
39
+ next.load ()->SetOwner ();
40
+ }
41
+
42
+ private:
43
+ QueueSpinLock& host;
44
+ std::atomic<Guard*> next{};
45
+ std::atomic<bool > is_owner{};
46
+ };
47
+ private:
48
+ void Acquire (Guard* guard) {
49
+ auto ancestor = tail_.exchange (guard);
50
+ if (ancestor == nullptr ) {
51
+ guard->SetOwner ();
52
+ return ;
53
+ }
54
+
55
+ ancestor->SetNext (guard);
56
+ while (!guard->IsOwner ()) {}
57
+ }
58
+
59
+ void Release (Guard* guard) {
60
+ if (guard->HasNext ()) {
61
+ guard->SetNextOwner ();
62
+ return ;
63
+ }
64
+
65
+ Guard* old_guard = guard;
66
+ while (!tail_.compare_exchange_weak (old_guard, nullptr )) {
67
+ if (guard->HasNext ()) {
68
+ guard->SetNextOwner ();
69
+ return ;
70
+ }
71
+ old_guard = guard;
72
+ }
73
+ }
74
+
75
+ std::atomic<Guard*> tail_{};
76
+ };
77
+
78
+ } // namespace NSync
Original file line number Diff line number Diff line change @@ -5,6 +5,7 @@ test_source_files = [
5
5
' test_intrusive_list.cpp' ,
6
6
' test_ms_queue.cpp' ,
7
7
' test_async_mutex.cpp' ,
8
+ ' test_queue_spinlock.cpp' ,
8
9
]
9
10
10
11
cpp = meson .get_compiler(' cpp' )
Original file line number Diff line number Diff line change
1
+ //
2
+ // Created by konstantin on 28.07.24.
3
+ //
4
+
5
+ #include " gtest/gtest.h"
6
+
7
+ #include < thread>
8
+
9
+ #include < components/sync/queue_spinlock.h>
10
+
11
+ TEST (TestQueueSpinlock, LockUnlock) {
12
+ NSync::QueueSpinLock spinlock;
13
+
14
+ {
15
+ NSync::QueueSpinLock::Guard guard (spinlock); // <-- Acquired
16
+ // Critical section
17
+ } // <-- Released
18
+ }
19
+
20
+ TEST (TestQueueSpinlock, SequentialLockUnlock) {
21
+ NSync::QueueSpinLock spinlock;
22
+
23
+ {
24
+ NSync::QueueSpinLock::Guard guard (spinlock);
25
+ // Critical section
26
+ }
27
+
28
+ {
29
+ NSync::QueueSpinLock::Guard guard (spinlock);
30
+ // Critical section
31
+ }
32
+ }
33
+
34
+ TEST (TestQueueSpinlock, ConcurrentIncrements) {
35
+ NSync::QueueSpinLock spinlock;
36
+ size_t counter = 0 ;
37
+
38
+ const size_t kIncrementsPerThread = 1000 ;
39
+
40
+ auto contender = [&] {
41
+ for (size_t i = 0 ; i < kIncrementsPerThread ; ++i) {
42
+ NSync::QueueSpinLock::Guard guard (spinlock);
43
+
44
+ size_t current = counter;
45
+ std::this_thread::yield ();
46
+ counter = current + 1 ;
47
+ }
48
+ };
49
+
50
+ std::thread t1 (contender);
51
+ std::thread t2 (contender);
52
+ t1.join ();
53
+ t2.join ();
54
+
55
+ std::cout << " Shared counter value: " << counter << std::endl;
56
+
57
+ ASSERT_EQ (counter, 2 * kIncrementsPerThread );
58
+ }
You can’t perform that action at this time.
0 commit comments