1 //
2 // Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
3 // Copyright 2018 Capitar IT Group BV <info@capitar.com>
4 //
5 // This software is supplied under the terms of the MIT License, a
6 // copy of which should be located in the distribution where this
7 // file was obtained (LICENSE.txt). A copy of the license may also be
8 // found online at https://opensource.org/licenses/MIT.
9 //
10
11 #include <nuts.h>
12
13 struct add_arg {
14 int cnt;
15 nng_duration delay;
16 nng_mtx * mx;
17 nng_cv * cv;
18 };
19
20 void
add(void * arg)21 add(void *arg)
22 {
23 struct add_arg *aa = arg;
24
25 if (aa->delay > 0) {
26 nng_msleep(aa->delay);
27 }
28 nng_mtx_lock(aa->mx);
29 aa->cnt++;
30 nng_cv_wake(aa->cv);
31 nng_mtx_unlock(aa->mx);
32 }
33
34 #ifdef __has_feature
35 #if __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer)
36 #define RELAXED_CLOCKS
37 #endif
38 #endif
39
40 void
test_sleep(void)41 test_sleep(void)
42 {
43 uint64_t start;
44 NUTS_CLOCK(start);
45 nng_msleep(100);
46 NUTS_AFTER(start + 100);
47 #ifndef RELAXED_CLOCKS
48 NUTS_BEFORE(start + 500);
49 #endif
50 }
51
52 void
test_clock(void)53 test_clock(void)
54 {
55 uint64_t s0, s1;
56 nng_time t0, t1;
57
58 NUTS_CLOCK(s0);
59 t0 = nng_clock();
60 nng_msleep(200);
61 t1 = nng_clock();
62 NUTS_CLOCK(s1);
63
64 NUTS_TRUE(t1 > t0);
65 NUTS_TRUE((t1 - t0) >= 200);
66 NUTS_TRUE((t1 - t0) < 500);
67
68 #ifndef RELAXED_CLOCKS
69 NUTS_TRUE(abs((int) (s1 - s0) - (int) (t1 - t0)) < 50);
70 #endif
71 }
72
73 void
test_mutex(void)74 test_mutex(void)
75 {
76 nng_mtx *mx, *mx2;
77
78 NUTS_PASS(nng_mtx_alloc(&mx));
79 nng_mtx_lock(mx);
80 nng_mtx_unlock(mx);
81
82 nng_mtx_lock(mx);
83 nng_mtx_unlock(mx);
84 nng_mtx_free(mx);
85
86 // Verify that the mutexes are not always the same!
87 NUTS_PASS(nng_mtx_alloc(&mx));
88 NUTS_PASS(nng_mtx_alloc(&mx2));
89 NUTS_TRUE(mx != mx2);
90 nng_mtx_free(mx);
91 nng_mtx_free(mx2);
92 }
93
94 void
test_thread(void)95 test_thread(void)
96 {
97 nng_thread * thr;
98 struct add_arg aa;
99
100 NUTS_PASS(nng_mtx_alloc(&aa.mx));
101 NUTS_PASS(nng_cv_alloc(&aa.cv, aa.mx));
102 aa.cnt = 0;
103 aa.delay = 0;
104
105 NUTS_PASS(nng_thread_create(&thr, add, &aa));
106 nng_thread_destroy(thr);
107 NUTS_TRUE(aa.cnt == 1);
108
109 nng_cv_free(aa.cv);
110 nng_mtx_free(aa.mx);
111 }
112
113 void
test_cond_var(void)114 test_cond_var(void)
115 {
116 nng_thread * thr;
117 struct add_arg aa;
118
119 NUTS_PASS(nng_mtx_alloc(&aa.mx));
120 NUTS_PASS(nng_cv_alloc(&aa.cv, aa.mx));
121 aa.cnt = 0;
122 aa.delay = 0;
123
124 NUTS_PASS(nng_thread_create(&thr, add, &aa));
125
126 nng_mtx_lock(aa.mx);
127 while (aa.cnt == 0) {
128 nng_cv_wait(aa.cv);
129 }
130 nng_mtx_unlock(aa.mx);
131 nng_thread_destroy(thr);
132 NUTS_TRUE(aa.cnt == 1);
133
134 nng_cv_free(aa.cv);
135 nng_mtx_free(aa.mx);
136 }
137
138 void
test_cond_wake(void)139 test_cond_wake(void)
140 {
141 nng_thread * thr;
142 struct add_arg aa;
143 nng_time now;
144
145 NUTS_PASS(nng_mtx_alloc(&aa.mx));
146 NUTS_PASS(nng_cv_alloc(&aa.cv, aa.mx));
147 aa.cnt = 0;
148 aa.delay = 200;
149
150 now = nng_clock();
151
152 NUTS_PASS(nng_thread_create(&thr, add, &aa));
153
154 nng_mtx_lock(aa.mx);
155 nng_cv_until(aa.cv, now + 500);
156 nng_mtx_unlock(aa.mx);
157
158 NUTS_TRUE(nng_clock() >= now + 200);
159 NUTS_TRUE(nng_clock() < now + 500);
160
161 nng_thread_destroy(thr);
162 nng_cv_free(aa.cv);
163 nng_mtx_free(aa.mx);
164 }
165
166 void
test_cond_until(void)167 test_cond_until(void)
168 {
169 struct add_arg aa;
170 nng_time now;
171
172 NUTS_PASS(nng_mtx_alloc(&aa.mx));
173 NUTS_PASS(nng_cv_alloc(&aa.cv, aa.mx));
174 aa.cnt = 0;
175 aa.delay = 0;
176
177 now = nng_clock();
178 nng_mtx_lock(aa.mx);
179 nng_cv_until(aa.cv, now + 100);
180 nng_mtx_unlock(aa.mx);
181
182 NUTS_TRUE(nng_clock() >= now);
183 #ifdef NO_SPRIOUS_WAKEUPS
184 // Some systems (e.g. Win32) will occasionally wake a threaed
185 // spuriously. We therefore can't rely on condwait to be
186 // an absolute guarantee of minimum time passage.
187 NUTS_TRUE(nng_clock() >= now + 100);
188 #endif
189 NUTS_TRUE(nng_clock() < now + 1000);
190
191 nng_cv_free(aa.cv);
192 nng_mtx_free(aa.mx);
193 }
194
195 void
test_random(void)196 test_random(void)
197 {
198 int same = 0;
199 uint32_t values[1000];
200
201 for (int i = 0; i < 1000; i++) {
202 values[i] = nng_random();
203 }
204 for (int i = 0; i < 1000; i++) {
205 for (int j = 0; j < i; j++) {
206 if (values[j] == values[i]) {
207 same++;
208 }
209 }
210 }
211
212 // 1% reproduction is *highly* unlikely.
213 // There are 4 billion possible options, we are only looking at
214 // 1000 of them. In general, it would be an extreme outlier
215 // to see more than 2 repeats, unless your RNG is biased.
216 NUTS_TRUE(same < 5);
217 }
218
219 NUTS_TESTS = {
220 { "sleep", test_sleep },
221 { "clock", test_clock },
222 { "mutex", test_mutex },
223 { "thread", test_thread },
224 { "cond var", test_cond_var },
225 { "cond wake", test_cond_wake },
226 { "cond until", test_cond_until },
227 { "random", test_random },
228 { NULL, NULL },
229 };
230