master
c 123 lines 5.55 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 // Deterministic unit tests for the baseline rw-spinlock API semantics that
6 // already hold on master. These tests intentionally avoid timing/sleep-based
7 // concurrency assertions (kept in the rwlockstest benchmark) so they stay
8 // reproducible. They also intentionally do NOT assert writer-priority,
9 // writer-starvation prevention, or writer-pending reader blocking; those
10 // behaviors belong with the PR that introduces them.
11
12 #define RW_TEST(condition, msg) do { \
13 if (!(condition)) { \
14 fprintf(stderr, "rw-spinlock unittest FAILED: %s (%s:%d)\n", \
15 (msg), __FUNCTION__, __LINE__); \
16 errors++; \
17 } \
18 } while(0)
19
20 int rw_spinlock_unittest(void) {
21 int errors = 0;
22
23 fprintf(stderr, "\nrunning rw-spinlock unittest\n");
24
25 // ----------------------------------------------------------------------
26 // initialization: a freshly initialized lock has no writer and no readers,
27 // and a static initializer produces the same state.
28 {
29 RW_SPINLOCK lock;
30 rw_spinlock_init(&lock);
31 RW_TEST(lock.writer == 0, "init clears writer");
32 RW_TEST(lock.counter == 0, "init clears counter");
33
34 RW_SPINLOCK lock_static = RW_SPINLOCK_INITIALIZER;
35 RW_TEST(lock_static.writer == 0, "static initializer clears writer");
36 RW_TEST(lock_static.counter == 0, "static initializer clears counter");
37 }
38
39 // ----------------------------------------------------------------------
40 // tryread on a free lock succeeds, and read_unlock restores the free state.
41 {
42 RW_SPINLOCK lock = RW_SPINLOCK_INITIALIZER;
43 RW_TEST(rw_spinlock_tryread_lock(&lock) == true, "tryread succeeds on free lock");
44 rw_spinlock_read_unlock(&lock);
45 RW_TEST(lock.counter == 0, "read_unlock restores free state");
46 }
47
48 // ----------------------------------------------------------------------
49 // concurrent (and recursive) readers: a shared lock admits multiple
50 // simultaneous read holders. With no writer held or pending, the same
51 // thread can take the read lock repeatedly; each acquisition succeeds and
52 // each unlock is balanced.
53 {
54 RW_SPINLOCK lock = RW_SPINLOCK_INITIALIZER;
55 const int readers = 5;
56
57 for (int i = 0; i < readers; i++)
58 RW_TEST(rw_spinlock_tryread_lock(&lock) == true, "additional reader admitted");
59
60 // a writer must not be able to enter while readers are present
61 RW_TEST(rw_spinlock_trywrite_lock(&lock) == false, "trywrite fails while readers held");
62
63 for (int i = 0; i < readers; i++)
64 rw_spinlock_read_unlock(&lock);
65
66 RW_TEST(lock.counter == 0, "all readers released");
67
68 // once readers are gone, a writer can enter
69 RW_TEST(rw_spinlock_trywrite_lock(&lock) == true, "trywrite succeeds after readers drain");
70 rw_spinlock_write_unlock(&lock);
71 RW_TEST(lock.counter == 0, "write_unlock restores free state");
72 }
73
74 // ----------------------------------------------------------------------
75 // blocking read_lock path (non-contended): read_lock/read_unlock balance
76 // and admit multiple holders just like tryread.
77 {
78 RW_SPINLOCK lock = RW_SPINLOCK_INITIALIZER;
79 rw_spinlock_read_lock(&lock);
80 rw_spinlock_read_lock(&lock);
81 RW_TEST(rw_spinlock_trywrite_lock(&lock) == false, "trywrite fails while blocking readers held");
82 rw_spinlock_read_unlock(&lock);
83 rw_spinlock_read_unlock(&lock);
84 RW_TEST(lock.counter == 0, "blocking readers released");
85 }
86
87 // ----------------------------------------------------------------------
88 // trywrite on a free lock succeeds and records the writer; write_unlock
89 // clears the writer and the writer bit.
90 {
91 RW_SPINLOCK lock = RW_SPINLOCK_INITIALIZER;
92 RW_TEST(rw_spinlock_trywrite_lock(&lock) == true, "trywrite succeeds on free lock");
93 RW_TEST(lock.writer == gettid_cached(), "trywrite records the writer tid");
94 rw_spinlock_write_unlock(&lock);
95 RW_TEST(lock.writer == 0, "write_unlock clears writer");
96 RW_TEST(lock.counter == 0, "write_unlock restores free state");
97 }
98
99 // ----------------------------------------------------------------------
100 // writer exclusivity: while a writer holds the lock, neither a reader nor
101 // another writer may enter.
102 {
103 RW_SPINLOCK lock = RW_SPINLOCK_INITIALIZER;
104 rw_spinlock_write_lock(&lock);
105 RW_TEST(rw_spinlock_tryread_lock(&lock) == false, "tryread fails while writer held");
106 RW_TEST(rw_spinlock_trywrite_lock(&lock) == false, "second trywrite fails while writer held");
107 rw_spinlock_write_unlock(&lock);
108
109 // after release both a reader and a writer can enter again
110 RW_TEST(rw_spinlock_tryread_lock(&lock) == true, "tryread succeeds after writer released");
111 rw_spinlock_read_unlock(&lock);
112 RW_TEST(rw_spinlock_trywrite_lock(&lock) == true, "trywrite succeeds after writer released");
113 rw_spinlock_write_unlock(&lock);
114 RW_TEST(lock.counter == 0, "lock free after exclusivity test");
115 }
116
117 if (errors)
118 fprintf(stderr, "rw-spinlock unittest: %d ERROR(S)\n", errors);
119 else
120 fprintf(stderr, "rw-spinlock unittest: OK\n");
121
122 return errors;
123 }