master
c 137 lines 4.52 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 #define MAX_USEC 512 // Maximum backoff limit in microseconds
6
7 #define WRITER_BIT (1U << 31)
8 #define READER_MASK (~WRITER_BIT)
9
10 // ----------------------------------------------------------------------------
11 // rw_spinlock implementation
12
13 void rw_spinlock_init_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
14 rw_spinlock->writer = 0;
15 rw_spinlock->counter = 0;
16 }
17
18 ALWAYS_INLINE bool rw_spinlock_tryread_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
19 size_t spins = 0;
20
21 uint32_t val = __atomic_add_fetch(&rw_spinlock->counter, 1, __ATOMIC_ACQUIRE);
22
23 // Check if a writer holds the lock
24 if (val & WRITER_BIT) {
25 // Undo our increment and fail
26 __atomic_sub_fetch(&rw_spinlock->counter, 1, __ATOMIC_RELEASE);
27 return false;
28 }
29
30 worker_spinlock_contention(func, spins);
31 nd_thread_rwspinlock_read_locked();
32 return true;
33 }
34
35 ALWAYS_INLINE void rw_spinlock_read_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
36 size_t spins = 0;
37 usec_t usec = 1;
38 usec_t deadlock_timestamp = 0;
39
40 while (true) {
41 // Optimistically increment reader count
42 uint32_t val = __atomic_add_fetch(&rw_spinlock->counter, 1, __ATOMIC_ACQUIRE);
43
44 // Check if a writer holds the lock
45 if (!(val & WRITER_BIT)) {
46 // no writer, we are in
47 worker_spinlock_contention(func, spins);
48 nd_thread_rwspinlock_read_locked();
49 return;
50 }
51
52 // Undo our increment and retry
53 __atomic_sub_fetch(&rw_spinlock->counter, 1, __ATOMIC_RELEASE);
54
55 spins++;
56
57 // Check for deadlock every SPINS_BEFORE_DEADLOCK_CHECK iterations
58 if ((spins % SPINS_BEFORE_DEADLOCK_CHECK) == 0) {
59 spinlock_deadlock_detect(&deadlock_timestamp, "rw-spinlock read lock", func);
60 }
61
62 microsleep(usec);
63 usec = usec >= MAX_USEC ? MAX_USEC : usec * 2;
64 }
65 }
66
67 ALWAYS_INLINE void rw_spinlock_read_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
68 __atomic_sub_fetch(&rw_spinlock->counter, 1, __ATOMIC_RELEASE);
69 nd_thread_rwspinlock_read_unlocked();
70 }
71
72 ALWAYS_INLINE bool rw_spinlock_trywrite_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
73 // Optimistically set writer bit
74 uint32_t old = __atomic_fetch_or(&rw_spinlock->counter, WRITER_BIT, __ATOMIC_ACQUIRE);
75
76 if(old == 0) {
77 rw_spinlock->writer = gettid_cached();
78 worker_spinlock_contention(func, 0);
79 nd_thread_rwspinlock_write_locked();
80 return true;
81 }
82
83 // Check if we were the only one
84 if (old & WRITER_BIT) {
85 // there is a writer inside (keep the writer bit there)
86 }
87 else /* if ((old & READER_MASK) != 0) */ {
88 // there are readers inside, remove the writer bit we added
89 __atomic_and_fetch(&rw_spinlock->counter, ~WRITER_BIT, __ATOMIC_RELEASE);
90 }
91
92 return false;
93 }
94
95 ALWAYS_INLINE void rw_spinlock_write_lock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func) {
96 size_t spins = 0;
97 usec_t usec = 1;
98 usec_t deadlock_timestamp = 0;
99
100 while (1) {
101 // Optimistically set writer bit
102 uint32_t old = __atomic_fetch_or(&rw_spinlock->counter, WRITER_BIT, __ATOMIC_ACQUIRE);
103
104 // Check if we were the only one
105 if (old == 0) {
106 rw_spinlock->writer = gettid_cached();
107 worker_spinlock_contention(func, spins);
108 nd_thread_rwspinlock_write_locked();
109 return;
110 }
111
112 // Check if we were the only one
113 if (old & WRITER_BIT) {
114 // there is a writer inside (keep the writer bit there)
115 }
116 else /* if ((old & READER_MASK) != 0) */ {
117 // there are readers inside, remove the writer bit we added
118 __atomic_and_fetch(&rw_spinlock->counter, ~WRITER_BIT, __ATOMIC_RELEASE);
119 }
120
121 spins++;
122
123 // Check for deadlock every SPINS_BEFORE_DEADLOCK_CHECK iterations
124 if ((spins % SPINS_BEFORE_DEADLOCK_CHECK) == 0) {
125 spinlock_deadlock_detect(&deadlock_timestamp, "rw-spinlock write lock", func);
126 }
127
128 microsleep(usec);
129 usec = usec >= MAX_USEC ? MAX_USEC : usec * 2;
130 }
131 }
132
133 ALWAYS_INLINE void rw_spinlock_write_unlock_with_trace(RW_SPINLOCK *rw_spinlock, const char *func __maybe_unused) {
134 rw_spinlock->writer = 0;
135 __atomic_and_fetch(&rw_spinlock->counter, ~WRITER_BIT, __ATOMIC_RELEASE);
136 nd_thread_rwspinlock_write_unlocked();
137 }