1 /*
2 * lws-api-test-lws_dsh
3 *
4 * Written in 2010-2021 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 */
9
10 #include <libwebsockets.h>
11
12 int
test1(void)13 test1(void)
14 {
15 struct lws_dsh *dsh;
16 size_t size;
17 void *a1;
18
19 /*
20 * test 1: single dsh, alloc 2 kinds and free everything back to a
21 * single free obj
22 */
23
24 dsh = lws_dsh_create(NULL, 16384, 2);
25 if (!dsh) {
26 lwsl_err("%s: Failed to create dsh\n", __func__);
27
28 return 1;
29 }
30
31 if (lws_dsh_alloc_tail(dsh, 0, "hello", 5, NULL, 0)) {
32 lwsl_err("%s: Failed to alloc 1\n", __func__);
33
34 goto bail;
35 }
36
37 if (lws_dsh_alloc_tail(dsh, 1, "some other string", 17, NULL, 0)) {
38 lwsl_err("%s: Failed to alloc 2\n", __func__);
39
40 goto bail;
41 }
42
43 if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) {
44 lwsl_err("%s: Failed to alloc 3\n", __func__);
45
46 goto bail;
47 }
48
49 if (lws_dsh_get_head(dsh, 1, &a1, &size)) {
50 lwsl_err("%s: no head 1\n", __func__);
51
52 goto bail;
53 }
54 if (size != 17 || memcmp(a1, "some other string", 17)) {
55 lwsl_err("%s: test 1 mismatch\n", __func__);
56
57 goto bail;
58 }
59 lws_dsh_free(&a1);
60
61 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
62 lwsl_err("%s: no head 2\n", __func__);
63
64 goto bail;
65 }
66 if (size != 5 || memcmp(a1, "hello", 5)) {
67 lwsl_err("%s: test 2 mismatch\n", __func__);
68
69 goto bail;
70 }
71 lws_dsh_free(&a1);
72
73 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
74 lwsl_err("%s: no head 3\n", __func__);
75
76 goto bail;
77 }
78 if (size != 11 || memcmp(a1, "hello again", 11)) {
79 lwsl_err("%s: test 3 mismatch\n", __func__);
80
81 goto bail;
82 }
83 lws_dsh_free(&a1);
84
85 lws_dsh_destroy(&dsh);
86
87 return 0;
88 bail:
89 lws_dsh_destroy(&dsh);
90
91 return 1;
92 }
93
94 int
test3(void)95 test3(void)
96 {
97 struct lws_dsh *dsh, *dsh2;
98 lws_dll2_owner_t owner;
99 uint8_t blob[4096];
100
101 memset(blob, 0, sizeof(blob));
102
103 /*
104 * test 3: multiple dsh, umeetable allocation request
105 */
106
107 lws_dll2_owner_clear(&owner);
108
109 dsh = lws_dsh_create(&owner, 4096, 2);
110 if (!dsh) {
111 lwsl_err("%s: Failed to create dsh1\n", __func__);
112
113 return 1;
114 }
115
116 dsh2 = lws_dsh_create(&owner, 4096, 2);
117 if (!dsh2) {
118 lwsl_err("%s: Failed to create dsh2\n", __func__);
119
120 goto bail;
121 }
122
123 if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) {
124 lwsl_err("%s: Failed to alloc 1\n", __func__);
125
126 goto bail2;
127 }
128
129 if (lws_dsh_alloc_tail(dsh2, 0, "hello", 5, NULL, 0)) {
130 lwsl_err("%s: Failed to alloc 2\n", __func__);
131
132 goto bail2;
133 }
134
135 /*
136 * There's just no room for this, we expect it to fail
137 */
138
139 if (!lws_dsh_alloc_tail(dsh, 0, blob, 5000, NULL, 0)) {
140 lwsl_err("%s: Didn't fail to alloc as expected\n", __func__);
141
142 goto bail2;
143 }
144
145 if (lws_dsh_alloc_tail(dsh2, 0, "hello again", 11, NULL, 0)) {
146 lwsl_err("%s: Failed to alloc 4\n", __func__);
147
148 goto bail2;
149 }
150
151 lws_dsh_destroy(&dsh2);
152 lws_dsh_destroy(&dsh);
153
154 return 0;
155
156 bail2:
157 lws_dsh_destroy(&dsh2);
158
159 bail:
160 lws_dsh_destroy(&dsh);
161
162 return 1;
163 }
164
165 int
test4(void)166 test4(void)
167 {
168 uint8_t blob[4096];
169 struct lws_dsh *dsh;
170 size_t size;
171 void *a1;
172
173 memset(blob, 0, sizeof(blob));
174
175 /*
176 * test 4: use up whole free list, then recover and alloc something
177 * else
178 */
179
180 dsh = lws_dsh_create(NULL, 4096, 2);
181 if (!dsh) {
182 lwsl_err("%s: Failed to create dsh\n", __func__);
183
184 return 1;
185 }
186
187 if (lws_dsh_alloc_tail(dsh, 0, blob, 4000, NULL, 0)) {
188 lwsl_err("%s: Failed to alloc 1\n", __func__);
189
190 goto bail;
191 }
192
193 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
194 lwsl_err("%s: no head 1\n", __func__);
195
196 goto bail;
197 }
198 if (size != 4000) {
199 lwsl_err("%s: test 1 mismatch\n", __func__);
200
201 goto bail;
202 }
203 lws_dsh_free(&a1);
204
205 if (lws_dsh_alloc_tail(dsh, 0, "some other string", 17, NULL, 0)) {
206 lwsl_err("%s: Failed to alloc 2\n", __func__);
207
208 goto bail;
209 }
210
211 if (lws_dsh_alloc_tail(dsh, 0, "hello again", 11, NULL, 0)) {
212 lwsl_err("%s: Failed to alloc 3\n", __func__);
213
214 goto bail;
215 }
216
217 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
218 lwsl_err("%s: no head 1\n", __func__);
219
220 goto bail;
221 }
222 if (size != 17 || memcmp(a1, "some other string", 17)) {
223 lwsl_err("%s: test 1 mismatch\n", __func__);
224
225 goto bail;
226 }
227 lws_dsh_free(&a1);
228
229 if (lws_dsh_get_head(dsh, 0, &a1, &size)) {
230 lwsl_err("%s: no head 2\n", __func__);
231
232 goto bail;
233 }
234 if (size != 11 || memcmp(a1, "hello again", 11)) {
235 lwsl_err("%s: test 2 mismatch (%zu)\n", __func__, size);
236
237 goto bail;
238 }
239
240 lws_dsh_free(&a1);
241
242 lws_dsh_destroy(&dsh);
243
244 return 0;
245 bail:
246 lws_dsh_destroy(&dsh);
247
248 return 1;
249 }
250
251 int
test5(void)252 test5(void)
253 {
254 struct lws_dsh *dsh;
255 unsigned int budget;
256 uint8_t blob[4096];
257 lws_xos_t xos;
258 size_t size;
259 void *a1;
260
261 memset(blob, 0, sizeof(blob));
262 lws_xos_init(&xos, 0x123456789abcdef0ull);
263
264 budget = (unsigned int)(lws_xos(&xos) % 4000) + 4000;
265
266 lwsl_notice("%s: budget %u\n", __func__, budget);
267
268
269 /*
270 * test 5: PRNG-based spamming and erratic bidi draining
271 */
272
273 dsh = lws_dsh_create(NULL, 409600, 2);
274 if (!dsh) {
275 lwsl_err("%s: Failed to create dsh\n", __func__);
276
277 return 1;
278 }
279
280 do {
281
282 if (lws_xos_percent(&xos, 60)) {
283 /* kind 0 is going to try to write */
284
285 size = (size_t)((lws_xos(&xos) & 127) + 1);
286
287 if (!lws_dsh_alloc_tail(dsh, 0, blob, size, NULL, 0))
288 lwsl_notice("%s: kind 0 alloc %d\n", __func__, (int)size);
289 }
290
291 if (lws_xos_percent(&xos, 80)) {
292 /* kind 1 is going to try to write */
293
294 size = (size_t)((lws_xos(&xos) & 127) + 1);
295
296 if (!lws_dsh_alloc_tail(dsh, 1, blob, size, NULL, 0))
297 lwsl_notice("%s: kind 1 alloc %d\n", __func__, (int)size);
298 }
299
300 if (lws_xos_percent(&xos, 40)) {
301 /* kind 0 is going to try to read */
302
303 while (!lws_dsh_get_head(dsh, 0, &a1, &size)) {
304 lwsl_notice("%s: kind 0 read %d\n", __func__, (int)size);
305 lws_dsh_free(&a1);
306 }
307 }
308
309 if (lws_xos_percent(&xos, 30)) {
310 /* kind 1 is going to try to read */
311
312 while (!lws_dsh_get_head(dsh, 1, &a1, &size)) {
313 lwsl_notice("%s: kind 1 read %d\n", __func__, (int)size);
314 lws_dsh_free(&a1);
315 }
316 }
317
318 } while (budget--);
319
320 while (!lws_dsh_get_head(dsh, 0, &a1, &size)) {
321 lwsl_notice("%s: kind 0 read %d\n", __func__, (int)size);
322 lws_dsh_free(&a1);
323 }
324
325 while (!lws_dsh_get_head(dsh, 1, &a1, &size)) {
326 lwsl_notice("%s: kind 1 read %d\n", __func__, (int)size);
327 lws_dsh_free(&a1);
328 }
329
330 #if defined(_DEBUG)
331 lws_dsh_describe(dsh, "test dsh end state");
332 #endif
333
334 lws_dsh_destroy(&dsh);
335
336 return 0;
337 }
338
main(int argc,const char ** argv)339 int main(int argc, const char **argv)
340 {
341 int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
342 int ret = 0, n;
343 const char *p;
344
345 if ((p = lws_cmdline_option(argc, argv, "-d")))
346 logs = atoi(p);
347
348 lws_set_log_level(logs, NULL);
349 lwsl_user("LWS API selftest: lws_dsh\n");
350
351 n = test1();
352 lwsl_user("%s: test1: %d\n", __func__, n);
353 ret |= n;
354
355 n = test3();
356 lwsl_user("%s: test3: %d\n", __func__, n);
357 ret |= n;
358
359 n = test4();
360 lwsl_user("%s: test4: %d\n", __func__, n);
361 ret |= n;
362
363 n = test5();
364 lwsl_user("%s: test5: %d\n", __func__, n);
365 ret |= n;
366
367 lwsl_user("Completed: %s\n", ret ? "FAIL" : "PASS");
368
369 return ret;
370 }
371