1#include "sync/tests/test_internal.h"
2
3#ifdef DEBUG_LOCK_CHK
4
5#include <sync/lock_chk_assert.h>
6#include <sync/lock_chk_internal.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 <time/spin_sleep.h>
13
14LOCK_CHK_CLASS_DECLARE_LOCAL(assert_spin_class);
15LOCK_CHK_CLASS_DECLARE_LOCAL(assert_qspin_class);
16LOCK_CHK_CLASS_DECLARE_LOCAL(assert_mutex_class);
17LOCK_CHK_CLASS_DECLARE_LOCAL(assert_mutex_simple_class);
18LOCK_CHK_CLASS_DECLARE_LOCAL(assert_rwlock_class);
19LOCK_CHK_CLASS_DECLARE_LOCAL(assert_cross_thread_class);
20
21TEST_DECLARE_UNIT(lock_chk, assert_held_roundtrip_spin) {
22 struct spinlock s;
23 spinlock_init_chk(&s, LOCK_CHK_CLASS(assert_spin_class), LOCK_CHKD_FULL);
24
25 SPINLOCK_ASSERT_NOT_HELD(&s);
26 enum irql old = spin_lock(&s);
27 SPINLOCK_ASSERT_HELD(&s);
28 LOCK_CHK_ASSERT_HELD(&s);
29 spin_unlock(&s, old);
30 SPINLOCK_ASSERT_NOT_HELD(&s);
31 LOCK_CHK_ASSERT_NOT_HELD(&s);
32
33 return TEST_SUCCESS;
34}
35
36TEST_DECLARE_UNIT(lock_chk, assert_held_roundtrip_qspin) {
37 struct qspinlock s;
38 qspinlock_init_chk(&s, LOCK_CHK_CLASS(assert_qspin_class), LOCK_CHKD_FULL);
39
40 QSPINLOCK_ASSERT_NOT_HELD(&s);
41 enum irql old = qspin_lock(&s);
42 QSPINLOCK_ASSERT_HELD(&s);
43 qspin_unlock(&s, old);
44 QSPINLOCK_ASSERT_NOT_HELD(&s);
45
46 return TEST_SUCCESS;
47}
48
49TEST_DECLARE_UNIT(lock_chk, assert_held_roundtrip_mutex) {
50 struct mutex m;
51 mutex_init_chk(&m, LOCK_CHK_CLASS(assert_mutex_class), LOCK_CHKD_FULL);
52
53 MUTEX_ASSERT_NOT_HELD(&m);
54 mutex_lock(&m);
55 MUTEX_ASSERT_HELD(&m);
56 LOCK_CHK_ASSERT_HELD(&m);
57 mutex_unlock(&m);
58 MUTEX_ASSERT_NOT_HELD(&m);
59 LOCK_CHK_ASSERT_NOT_HELD(&m);
60
61 return TEST_SUCCESS;
62}
63
64TEST_DECLARE_UNIT(lock_chk, assert_held_roundtrip_mutex_simple) {
65 struct mutex_simple m;
66 mutex_simple_init_chk(&m, LOCK_CHK_CLASS(assert_mutex_simple_class),
67 LOCK_CHKD_FULL);
68
69 MUTEX_SIMPLE_ASSERT_NOT_HELD(&m);
70 mutex_simple_lock(&m);
71 MUTEX_SIMPLE_ASSERT_HELD(&m);
72 LOCK_CHK_ASSERT_HELD(&m);
73 mutex_simple_unlock(&m);
74 MUTEX_SIMPLE_ASSERT_NOT_HELD(&m);
75 LOCK_CHK_ASSERT_NOT_HELD(&m);
76
77 return TEST_SUCCESS;
78}
79
80TEST_DECLARE_UNIT(lock_chk, assert_held_roundtrip_rwlock) {
81 struct rwlock rw;
82 rwlock_init_chk(&rw, THREAD_PRIO_CLASS_TIMESHARE,
83 LOCK_CHK_CLASS(assert_rwlock_class), LOCK_CHKD_FULL);
84
85 RWLOCK_ASSERT_NOT_HELD(&rw);
86
87 rw_read_lock(&rw);
88 RWLOCK_ASSERT_READ(&rw);
89 LOCK_CHK_ASSERT_HELD(&rw, RWLOCK_ACQUIRE_READ);
90 rw_unlock(&rw);
91 RWLOCK_ASSERT_NOT_HELD(&rw);
92
93 rw_write_lock(&rw);
94 RWLOCK_ASSERT_WRITE(&rw);
95 LOCK_CHK_ASSERT_HELD(&rw, RWLOCK_ACQUIRE_WRITE);
96 rw_unlock(&rw);
97 RWLOCK_ASSERT_NOT_HELD(&rw);
98 LOCK_CHK_ASSERT_NOT_HELD(&rw);
99
100 return TEST_SUCCESS;
101}
102
103static struct mutex assert_cross_thread_mutex;
104static _Atomic bool assert_cross_thread_held = false;
105static _Atomic bool assert_cross_thread_release = false;
106
107static void assert_cross_thread_worker(void *arg) {
108 unused(arg);
109 mutex_lock(&assert_cross_thread_mutex);
110 atomic_store_explicit(&assert_cross_thread_held, true,
111 memory_order_release);
112 while (!atomic_load_explicit(&assert_cross_thread_release,
113 memory_order_acquire))
114 sleep_spin_ms(1);
115 mutex_unlock(&assert_cross_thread_mutex);
116}
117
118TEST_DECLARE_UNIT(lock_chk, assert_not_held_cross_thread_mutex) {
119 mutex_init_chk(&assert_cross_thread_mutex,
120 LOCK_CHK_CLASS(assert_cross_thread_class), LOCK_CHKD_FULL);
121
122 struct thread *th = thread_spawn_joinable("assert_cross_thread_worker",
123 assert_cross_thread_worker, NULL);
124
125 while (
126 !atomic_load_explicit(&assert_cross_thread_held, memory_order_acquire))
127 sleep_spin_ms(1);
128
129 /* Worker holds this right now */
130 TEST_ASSERT_TRUE(mutex_locked(&assert_cross_thread_mutex));
131 MUTEX_ASSERT_NOT_HELD(&assert_cross_thread_mutex);
132
133 atomic_store_explicit(&assert_cross_thread_release, true,
134 memory_order_release);
135 thread_join(th);
136
137 MUTEX_ASSERT_NOT_HELD(&assert_cross_thread_mutex);
138
139 return TEST_SUCCESS;
140}
141
142#endif /* DEBUG_LOCK_CHK */
143