1#include "sync/lock_chk/internal.h"
2#include "sync/tests/test_internal.h"
3
4#ifdef DEBUG_LOCK_CHK
5
6#include <sync/lock_chk_assert.h>
7#include <sync/mutex.h>
8#include <sync/mutex_simple.h>
9#include <sync/qspinlock.h>
10#include <sync/rwlock.h>
11#include <sync/spinlock.h>
12#include <test/fleet.h>
13#include <test/sync.h>
14#include <time/spin_sleep.h>
15
16LOCK_CHK_CLASS_DEFINE_LOCAL(assert_spin_class);
17LOCK_CHK_CLASS_DEFINE_LOCAL(assert_qspin_class);
18LOCK_CHK_CLASS_DEFINE_LOCAL(assert_mutex_class);
19LOCK_CHK_CLASS_DEFINE_LOCAL(assert_mutex_simple_class);
20LOCK_CHK_CLASS_DEFINE_LOCAL(assert_rwlock_class);
21LOCK_CHK_CLASS_DEFINE_LOCAL(assert_cross_thread_class);
22
23TEST_DEFINE_UNIT(lock_chk, assert_held_roundtrip_spin) {
24 struct spinlock s;
25 spinlock_init_chk(&s, LOCK_CHK_CLASS(assert_spin_class), LOCK_CHKD_FULL);
26
27 SPINLOCK_ASSERT_NOT_HELD(&s);
28 enum irql old = spin_lock(&s);
29 SPINLOCK_ASSERT_HELD(&s);
30 LOCK_CHK_ASSERT_HELD(&s);
31 spin_unlock(&s, old);
32 SPINLOCK_ASSERT_NOT_HELD(&s);
33 LOCK_CHK_ASSERT_NOT_HELD(&s);
34
35 return TEST_SUCCESS;
36}
37
38TEST_DEFINE_UNIT(lock_chk, assert_held_roundtrip_qspin) {
39 struct qspinlock s;
40 qspinlock_init_chk(&s, LOCK_CHK_CLASS(assert_qspin_class), LOCK_CHKD_FULL);
41
42 QSPINLOCK_ASSERT_NOT_HELD(&s);
43 enum irql old = qspin_lock(&s);
44 QSPINLOCK_ASSERT_HELD(&s);
45 qspin_unlock(&s, old);
46 QSPINLOCK_ASSERT_NOT_HELD(&s);
47
48 return TEST_SUCCESS;
49}
50
51TEST_DEFINE_UNIT(lock_chk, assert_held_roundtrip_mutex) {
52 struct mutex m;
53 mutex_init_chk(&m, LOCK_CHK_CLASS(assert_mutex_class), LOCK_CHKD_FULL);
54
55 MUTEX_ASSERT_NOT_HELD(&m);
56 mutex_lock(&m);
57 MUTEX_ASSERT_HELD(&m);
58 LOCK_CHK_ASSERT_HELD(&m);
59 mutex_unlock(&m);
60 MUTEX_ASSERT_NOT_HELD(&m);
61 LOCK_CHK_ASSERT_NOT_HELD(&m);
62
63 return TEST_SUCCESS;
64}
65
66TEST_DEFINE_UNIT(lock_chk, assert_held_roundtrip_mutex_simple) {
67 struct mutex_simple m;
68 mutex_simple_init_chk(&m, LOCK_CHK_CLASS(assert_mutex_simple_class),
69 LOCK_CHKD_FULL);
70
71 MUTEX_SIMPLE_ASSERT_NOT_HELD(&m);
72 mutex_simple_lock(&m);
73 MUTEX_SIMPLE_ASSERT_HELD(&m);
74 LOCK_CHK_ASSERT_HELD(&m);
75 mutex_simple_unlock(&m);
76 MUTEX_SIMPLE_ASSERT_NOT_HELD(&m);
77 LOCK_CHK_ASSERT_NOT_HELD(&m);
78
79 return TEST_SUCCESS;
80}
81
82TEST_DEFINE_UNIT(lock_chk, assert_held_roundtrip_rwlock) {
83 struct rwlock rw;
84 rwlock_init_chk(&rw, THREAD_PRIO_CLASS_TIMESHARE,
85 LOCK_CHK_CLASS(assert_rwlock_class), LOCK_CHKD_FULL);
86
87 RWLOCK_ASSERT_NOT_HELD(&rw);
88
89 rw_read_lock(&rw);
90 RWLOCK_ASSERT_READ(&rw);
91 LOCK_CHK_ASSERT_HELD(&rw, RWLOCK_READ);
92 rw_unlock(&rw);
93 RWLOCK_ASSERT_NOT_HELD(&rw);
94
95 rw_write_lock(&rw);
96 RWLOCK_ASSERT_WRITE(&rw);
97 LOCK_CHK_ASSERT_HELD(&rw, RWLOCK_WRITE);
98 rw_unlock(&rw);
99 RWLOCK_ASSERT_NOT_HELD(&rw);
100 LOCK_CHK_ASSERT_NOT_HELD(&rw);
101
102 return TEST_SUCCESS;
103}
104
105struct cross_thread_fix {
106 struct mutex lock;
107 struct test_latch held;
108 struct test_latch release;
109};
110
111static bool assert_cross_thread_worker(struct test_fleet *f,
112 struct test_conc_worker *w) {
113 struct cross_thread_fix *fix = w->arg;
114
115 mutex_lock(&fix->lock);
116 test_latch_set(&fix->held);
117 TEST_WORKER_CHECK_GOTO(f, test_latch_wait_timeout(&fix->release, 5000),
118 out);
119 mutex_unlock(&fix->lock);
120 return true;
121
122out:
123 mutex_unlock(&fix->lock);
124 return false;
125}
126
127TEST_DEFINE_UNIT(lock_chk, assert_not_held_cross_thread_mutex) {
128 struct test_fleet *fleet = test_fleet_init(ctx);
129
130 struct cross_thread_fix *fix = test_new(fix);
131
132 mutex_init_chk(&fix->lock, LOCK_CHK_CLASS(assert_cross_thread_class),
133 LOCK_CHKD_FULL);
134 test_latch_init(&fix->held);
135 test_latch_init(&fix->release);
136
137 test_fleet_spawn(fleet, "cross_thread", assert_cross_thread_worker,
138 .arg = fix);
139 test_fleet_start_all(fleet);
140
141 /* Every assert below can return early */
142 TEST_ASSERT(test_latch_wait_timeout(&fix->held, 5000));
143
144 /* Worker holds this right now */
145 TEST_ASSERT_TRUE(mutex_locked(&fix->lock));
146 MUTEX_ASSERT_NOT_HELD(&fix->lock);
147
148 test_latch_set(&fix->release);
149
150 struct test_verdict v = test_fleet_join(fleet);
151 if (v.result != TEST_RESULT_OK)
152 return v;
153
154 MUTEX_ASSERT_NOT_HELD(&fix->lock);
155 return TEST_SUCCESS;
156}
157
158#endif /* DEBUG_LOCK_CHK */
159