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