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 12int 13test1(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; 88bail: 89 lws_dsh_destroy(&dsh); 90 91 return 1; 92} 93 94int 95test3(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 156bail2: 157 lws_dsh_destroy(&dsh2); 158 159bail: 160 lws_dsh_destroy(&dsh); 161 162 return 1; 163} 164 165int 166test4(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; 245bail: 246 lws_dsh_destroy(&dsh); 247 248 return 1; 249} 250 251int 252test5(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 339int 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