source: mainline/uspace/app/rcutest/rcutest.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 19.6 KB
RevLine 
[94b39ba]1/*
2 * Copyright (c) 2012 Adam Hraska
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup test
30 * @{
31 */
32
33/**
34 * @file rcutest.c
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdint.h>
[c1694b6b]40#include <str_error.h>
[94b39ba]41#include <mem.h>
42#include <errno.h>
43#include <thread.h>
44#include <assert.h>
[29b8138]45#include <async.h>
[94b39ba]46#include <fibril.h>
[5d230a30]47#include <fibril_synch.h>
[29b8138]48#include <compiler/barrier.h>
[5d230a30]49#include <futex.h>
[1d6dd2a]50#include <str.h>
[94b39ba]51
52#include <rcu.h>
53
[d04e46e]54
[94b39ba]55
[29b8138]56#define USECS_PER_SEC (1000 * 1000)
57#define USECS_PER_MS 1000
58
[d04e46e]59/* fwd decl. */
60struct test_info;
[29b8138]61
62typedef struct test_desc {
63 /* Aggregate test that runs other tests already in the table test_desc. */
64 bool aggregate;
65 enum {
66 T_OTHER,
67 T_SANITY,
68 T_STRESS
69 } type;
[d04e46e]70 bool (*func)(struct test_info*);
[29b8138]71 const char *name;
72 const char *desc;
73} test_desc_t;
74
75
[d04e46e]76typedef struct test_info {
77 size_t thread_cnt;
78 test_desc_t *desc;
79} test_info_t;
80
81
[29b8138]82
[d04e46e]83static bool run_all_tests(struct test_info*);
84static bool run_sanity_tests(struct test_info*);
85static bool run_stress_tests(struct test_info*);
86
87static bool wait_for_one_reader(struct test_info*);
88static bool basic_sanity_check(struct test_info*);
89static bool dont_wait_for_new_reader(struct test_info*);
90static bool wait_for_exiting_reader(struct test_info*);
[5d230a30]91static bool seq_test(struct test_info*);
92
[29b8138]93
94static test_desc_t test_desc[] = {
95 {
96 .aggregate = true,
97 .type = T_OTHER,
98 .func = run_all_tests,
99 .name = "*",
100 .desc = "Runs all tests.",
101 },
102 {
103 .aggregate = true,
104 .type = T_SANITY,
105 .func = run_sanity_tests,
106 .name = "sanity-tests",
107 .desc = "Runs all RCU sanity tests.",
108 },
109 {
110 .aggregate = true,
111 .type = T_STRESS,
112 .func = run_stress_tests,
113 .name = "stress-tests",
114 .desc = "Runs all RCU stress tests.",
115 },
116
117 {
118 .aggregate = false,
119 .type = T_SANITY,
120 .func = basic_sanity_check,
121 .name = "basic-sanity",
[a879d73]122 .desc = "Locks/unlocks and syncs in 1 fibril, no contention.",
[29b8138]123 },
124 {
125 .aggregate = false,
126 .type = T_SANITY,
127 .func = wait_for_one_reader,
128 .name = "wait-for-one",
129 .desc = "Syncs with one 2 secs sleeping reader.",
130 },
131 {
132 .aggregate = false,
133 .type = T_SANITY,
134 .func = dont_wait_for_new_reader,
135 .name = "ignore-new-r",
[a879d73]136 .desc = "Syncs with preexisting reader; ignores new reader.",
137 },
138 {
139 .aggregate = false,
140 .type = T_SANITY,
141 .func = wait_for_exiting_reader,
142 .name = "dereg-unlocks",
143 .desc = "Lets deregister_fibril unlock the reader section.",
[29b8138]144 },
[5d230a30]145 {
146 .aggregate = false,
147 .type = T_STRESS,
148 .func = seq_test,
149 .name = "seq",
150 .desc = "Checks lock/unlock/sync w/ global time sequence.",
151 },
[29b8138]152 {
153 .aggregate = false,
154 .type = T_OTHER,
155 .func = NULL,
156 .name = "(null)",
157 .desc = "",
158 },
159};
160
161static const size_t test_desc_cnt = sizeof(test_desc) / sizeof(test_desc[0]);
162
163/*--------------------------------------------------------------------*/
164
[5d230a30]165static size_t next_rand(size_t seed)
166{
167 return (seed * 1103515245 + 12345) & ((1U << 31) - 1);
168}
169
170
[b7fd2a0]171typedef errno_t (*fibril_func_t)(void *);
[29b8138]172
[b7fd2a0]173static bool create_fibril(errno_t (*func)(void*), void *arg)
[29b8138]174{
175 fid_t fid = fibril_create(func, arg);
[a35b458]176
[29b8138]177 if (0 == fid) {
178 printf("Failed to create a fibril!\n");
179 return false;
180 }
[a35b458]181
[29b8138]182 fibril_add_ready(fid);
183 return true;
184}
185
186/*--------------------------------------------------------------------*/
187
[1b20da0]188static bool run_tests(test_info_t *info, bool (*include_filter)(test_desc_t *))
[29b8138]189{
190 size_t failed_cnt = 0;
191 size_t ok_cnt = 0;
[a35b458]192
[29b8138]193 for (size_t i = 0; i < test_desc_cnt; ++i) {
194 test_desc_t *t = &test_desc[i];
[a35b458]195
[29b8138]196 if (t->func && !t->aggregate && include_filter(t)) {
197 printf("Running \'%s\'...\n", t->name);
[d04e46e]198 bool ok = test_desc[i].func(info);
[a35b458]199
[29b8138]200 if (ok) {
201 ++ok_cnt;
202 printf("Passed: \'%s\'\n", t->name);
203 } else {
204 ++failed_cnt;
205 printf("FAILED: \'%s\'\n", t->name);
206 }
207 }
208 }
[a35b458]209
[29b8138]210 printf("\n");
211
212 printf("%zu tests passed\n", ok_cnt);
213
214 if (failed_cnt) {
215 printf("%zu tests failed\n", failed_cnt);
[1b20da0]216 }
[a35b458]217
[29b8138]218 return 0 == failed_cnt;
219}
220
221/*--------------------------------------------------------------------*/
222
223static bool all_tests_include_filter(test_desc_t *desc)
224{
225 return true;
226}
227
228/* Runs all available tests tests one-by-one. */
[d04e46e]229static bool run_all_tests(test_info_t *test_info)
[29b8138]230{
231 printf("Running all tests...\n");
[d04e46e]232 return run_tests(test_info, all_tests_include_filter);
[29b8138]233}
234
235/*--------------------------------------------------------------------*/
236
237static bool stress_tests_include_filter(test_desc_t *desc)
238{
239 return desc->type == T_STRESS;
240}
241
242/* Runs all available stress tests one-by-one. */
[d04e46e]243static bool run_stress_tests(test_info_t *test_info)
[29b8138]244{
245 printf("Running stress tests...\n");
[d04e46e]246 return run_tests(test_info, stress_tests_include_filter);
[29b8138]247}
248
249/*--------------------------------------------------------------------*/
250
251static bool sanity_tests_include_filter(test_desc_t *desc)
252{
253 return desc->type == T_SANITY;
254}
255
256/* Runs all available sanity tests one-by-one. */
[d04e46e]257static bool run_sanity_tests(test_info_t *test_info)
[29b8138]258{
259 printf("Running sanity tests...\n");
[d04e46e]260 return run_tests(test_info, sanity_tests_include_filter);
[29b8138]261}
262
263/*--------------------------------------------------------------------*/
264
265/* Locks/unlocks rcu and synchronizes without contention in a single fibril. */
[d04e46e]266static bool basic_sanity_check(test_info_t *test_info)
[94b39ba]267{
268 rcu_read_lock();
[29b8138]269 /* nop */
270 rcu_read_unlock();
271
[94b39ba]272 rcu_read_lock();
273 /* nop */
274 rcu_read_unlock();
[a35b458]275
[29b8138]276 rcu_synchronize();
277
278 /* Nested lock with yield(). */
279 rcu_read_lock();
280 fibril_yield();
281 rcu_read_lock();
282 fibril_yield();
283 rcu_read_unlock();
284 fibril_yield();
[94b39ba]285 rcu_read_unlock();
[a35b458]286
[29b8138]287 fibril_yield();
288 rcu_synchronize();
[94b39ba]289 rcu_synchronize();
[a35b458]290
[29b8138]291 rcu_read_lock();
292 /* nop */
293 if (!rcu_read_locked())
294 return false;
295
296 rcu_read_unlock();
[a35b458]297
[29b8138]298 return !rcu_read_locked();
[94b39ba]299}
300
[29b8138]301typedef struct one_reader_info {
302 bool entered_cs;
303 bool exited_cs;
304 size_t done_sleeps_cnt;
305 bool synching;
306 bool synched;
[a879d73]307 size_t failed;
[29b8138]308} one_reader_info_t;
309
310
[b7fd2a0]311static errno_t sleeping_reader(one_reader_info_t *arg)
[94b39ba]312{
[29b8138]313 rcu_register_fibril();
[a35b458]314
[29b8138]315 printf("lock{");
316 rcu_read_lock();
[a879d73]317 rcu_read_lock();
[29b8138]318 arg->entered_cs = true;
[a879d73]319 rcu_read_unlock();
[94b39ba]320
[29b8138]321 printf("r-sleep{");
322 /* 2 sec */
323 async_usleep(2 * USECS_PER_SEC);
324 ++arg->done_sleeps_cnt;
325 printf("}");
[a35b458]326
[a879d73]327 if (arg->synched) {
328 arg->failed = 1;
329 printf("Error: rcu_sync exited prematurely.\n");
330 }
[a35b458]331
[29b8138]332 arg->exited_cs = true;
333 rcu_read_unlock();
334 printf("}");
[a35b458]335
[29b8138]336 rcu_deregister_fibril();
337 return 0;
338}
[94b39ba]339
[d04e46e]340static bool wait_for_one_reader(test_info_t *test_info)
[94b39ba]341{
[29b8138]342 one_reader_info_t info = { 0 };
[a35b458]343
[29b8138]344 if (!create_fibril((fibril_func_t) sleeping_reader, &info))
345 return false;
[a35b458]346
[29b8138]347 /* 1 sec, waits for the reader to enter its critical section and sleep. */
348 async_usleep(1 * USECS_PER_SEC);
[a35b458]349
[29b8138]350 if (!info.entered_cs || info.exited_cs) {
351 printf("Error: reader is unexpectedly outside of critical section.\n");
352 return false;
[94b39ba]353 }
[a35b458]354
[29b8138]355 info.synching = true;
356 printf("sync[");
357 rcu_synchronize();
358 printf("]\n");
359 info.synched = true;
360
361 /* Load info.exited_cs */
362 memory_barrier();
[a35b458]363
[a879d73]364 if (!info.exited_cs || info.failed) {
[29b8138]365 printf("Error: rcu_sync() returned before the reader exited its CS.\n");
[1b20da0]366 /*
367 * Sleep some more so we don't free info on stack while the reader
[29b8138]368 * is using it.
369 */
370 /* 1.5 sec */
371 async_usleep(1500 * 1000);
372 return false;
373 } else {
374 return true;
375 }
376}
377
378/*--------------------------------------------------------------------*/
379
[a879d73]380#define WAIT_STEP_US 500 * USECS_PER_MS
[29b8138]381
382typedef struct two_reader_info {
383 bool new_entered_cs;
384 bool new_exited_cs;
385 bool old_entered_cs;
386 bool old_exited_cs;
387 bool synching;
388 bool synched;
389 size_t failed;
390} two_reader_info_t;
391
392
[b7fd2a0]393static errno_t preexisting_reader(two_reader_info_t *arg)
[29b8138]394{
[94b39ba]395 rcu_register_fibril();
[a35b458]396
[29b8138]397 printf("old-lock{");
398 rcu_read_lock();
399 arg->old_entered_cs = true;
[a35b458]400
[29b8138]401 printf("wait-for-sync{");
402 /* Wait for rcu_sync() to start waiting for us. */
403 while (!arg->synching) {
404 async_usleep(WAIT_STEP_US);
405 }
406 printf(" }");
[a35b458]407
[29b8138]408 /* A new reader starts while rcu_sync() is in progress. */
[a35b458]409
[29b8138]410 printf("wait-for-new-R{");
411 /* Wait for the new reader to enter its reader section. */
412 while (!arg->new_entered_cs) {
413 async_usleep(WAIT_STEP_US);
414 }
415 printf(" }");
[a35b458]416
[29b8138]417 arg->old_exited_cs = true;
[a35b458]418
[29b8138]419 assert(!arg->new_exited_cs);
[a35b458]420
[29b8138]421 if (arg->synched) {
422 arg->failed = 1;
423 printf("Error: rcu_sync() did not wait for preexisting reader.\n");
424 }
[a35b458]425
[29b8138]426 rcu_read_unlock();
427 printf(" }");
[a35b458]428
[94b39ba]429 rcu_deregister_fibril();
[29b8138]430 return 0;
431}
432
[b7fd2a0]433static errno_t new_reader(two_reader_info_t *arg)
[29b8138]434{
435 rcu_register_fibril();
[a35b458]436
[29b8138]437 /* Wait until rcu_sync() starts. */
438 while (!arg->synching) {
439 async_usleep(WAIT_STEP_US);
440 }
[a35b458]441
[1b20da0]442 /*
[29b8138]443 * synching is set when rcu_sync() is about to be entered so wait
444 * some more to make sure it really does start executing.
445 */
446 async_usleep(WAIT_STEP_US);
[a35b458]447
[29b8138]448 printf("new-lock(");
449 rcu_read_lock();
450 arg->new_entered_cs = true;
451
452 /* Wait for rcu_sync() exit, ie stop waiting for the preexisting reader. */
453 while (!arg->synched) {
454 async_usleep(WAIT_STEP_US);
455 }
[a35b458]456
[29b8138]457 arg->new_exited_cs = true;
458 /* Write new_exited_cs before exiting reader section. */
459 memory_barrier();
[a35b458]460
[1b20da0]461 /*
462 * Preexisting reader should have exited by now, so rcu_synchronize()
[29b8138]463 * must have returned.
464 */
465 if (!arg->old_exited_cs) {
466 arg->failed = 1;
467 printf("Error: preexisting reader should have exited by now!\n");
468 }
[a35b458]469
[29b8138]470 rcu_read_unlock();
471 printf(")");
472
473 rcu_deregister_fibril();
[94b39ba]474 return 0;
475}
476
[d04e46e]477static bool dont_wait_for_new_reader(test_info_t *test_info)
[29b8138]478{
479 two_reader_info_t info = { 0 };
[a35b458]480
[29b8138]481 if (!create_fibril((fibril_func_t) preexisting_reader, &info))
482 return false;
483
484 if (!create_fibril((fibril_func_t) new_reader, &info))
485 return false;
[a35b458]486
[29b8138]487 /* Waits for the preexisting_reader to enter its CS.*/
488 while (!info.old_entered_cs) {
489 async_usleep(WAIT_STEP_US);
490 }
[a35b458]491
[29b8138]492 assert(!info.old_exited_cs);
493 assert(!info.new_entered_cs);
494 assert(!info.new_exited_cs);
[a35b458]495
[29b8138]496 printf("sync[");
497 info.synching = true;
498 rcu_synchronize();
499 printf(" ]");
[a35b458]500
[29b8138]501 /* Load info.exited_cs */
502 memory_barrier();
[a35b458]503
[29b8138]504 if (!info.old_exited_cs) {
505 printf("Error: rcu_sync() returned before preexisting reader exited.\n");
506 info.failed = 1;
507 }
[a35b458]508
[29b8138]509 bool new_outside_cs = !info.new_entered_cs || info.new_exited_cs;
[a35b458]510
[29b8138]511 /* Test if new reader is waiting in CS before setting synched. */
512 compiler_barrier();
513 info.synched = true;
[a35b458]514
[29b8138]515 if (new_outside_cs) {
516 printf("Error: new reader CS held up rcu_sync(). (4)\n");
517 info.failed = 1;
518 } else {
519 /* Wait for the new reader. */
520 rcu_synchronize();
[a35b458]521
[29b8138]522 if (!info.new_exited_cs) {
523 printf("Error: 2nd rcu_sync() returned before new reader exited.\n");
524 info.failed = 1;
525 }
[a35b458]526
[29b8138]527 printf("\n");
528 }
[a35b458]529
[29b8138]530 if (info.failed) {
[1b20da0]531 /*
532 * Sleep some more so we don't free info on stack while readers
[29b8138]533 * are using it.
534 */
535 async_usleep(WAIT_STEP_US);
536 }
[a35b458]537
[29b8138]538 return 0 == info.failed;
539}
540
541#undef WAIT_STEP_US
[a879d73]542
543/*--------------------------------------------------------------------*/
544#define WAIT_STEP_US 500 * USECS_PER_MS
545
546typedef struct exit_reader_info {
547 bool entered_cs;
548 bool exited_cs;
549 bool synching;
550 bool synched;
551} exit_reader_info_t;
552
553
[b7fd2a0]554static errno_t exiting_locked_reader(exit_reader_info_t *arg)
[a879d73]555{
556 rcu_register_fibril();
[a35b458]557
[a879d73]558 printf("old-lock{");
559 rcu_read_lock();
560 rcu_read_lock();
561 rcu_read_lock();
562 arg->entered_cs = true;
[a35b458]563
[a879d73]564 printf("wait-for-sync{");
565 /* Wait for rcu_sync() to start waiting for us. */
566 while (!arg->synching) {
567 async_usleep(WAIT_STEP_US);
568 }
569 printf(" }");
[a35b458]570
[a879d73]571 rcu_read_unlock();
572 printf(" }");
573
574 arg->exited_cs = true;
575 /* Store exited_cs before unlocking reader section in deregister. */
576 memory_barrier();
[a35b458]577
[a879d73]578 /* Deregister forcefully unlocks the reader section. */
579 rcu_deregister_fibril();
580 return 0;
581}
582
583
[d04e46e]584static bool wait_for_exiting_reader(test_info_t *test_info)
[a879d73]585{
586 exit_reader_info_t info = { 0 };
[a35b458]587
[a879d73]588 if (!create_fibril((fibril_func_t) exiting_locked_reader, &info))
589 return false;
[a35b458]590
[a879d73]591 /* Waits for the preexisting_reader to enter its CS.*/
592 while (!info.entered_cs) {
593 async_usleep(WAIT_STEP_US);
594 }
[a35b458]595
[a879d73]596 assert(!info.exited_cs);
[a35b458]597
[a879d73]598 printf("sync[");
599 info.synching = true;
600 rcu_synchronize();
601 info.synched = true;
602 printf(" ]\n");
[a35b458]603
[a879d73]604 /* Load info.exited_cs */
605 memory_barrier();
[a35b458]606
[a879d73]607 if (!info.exited_cs) {
608 printf("Error: rcu_deregister_fibril did not unlock the CS.\n");
609 return false;
[1b20da0]610 }
[a35b458]611
[a879d73]612 return true;
613}
614
615#undef WAIT_STEP_US
616
617
[5d230a30]618/*--------------------------------------------------------------------*/
619
620typedef struct {
621 atomic_t time;
622 atomic_t max_start_time_of_done_sync;
[a35b458]623
[5d230a30]624 size_t total_workers;
625 size_t done_reader_cnt;
626 size_t done_updater_cnt;
627 fibril_mutex_t done_cnt_mtx;
628 fibril_condvar_t done_cnt_changed;
629
630 size_t read_iters;
631 size_t upd_iters;
[a35b458]632
[5d230a30]633 atomic_t seed;
634 int failed;
635} seq_test_info_t;
636
637
638static void signal_seq_fibril_done(seq_test_info_t *arg, size_t *cnt)
639{
640 fibril_mutex_lock(&arg->done_cnt_mtx);
641 ++*cnt;
[a35b458]642
[5d230a30]643 if (arg->total_workers == arg->done_reader_cnt + arg->done_updater_cnt) {
644 fibril_condvar_signal(&arg->done_cnt_changed);
645 }
[a35b458]646
[5d230a30]647 fibril_mutex_unlock(&arg->done_cnt_mtx);
648}
649
[b7fd2a0]650static errno_t seq_reader(seq_test_info_t *arg)
[5d230a30]651{
652 rcu_register_fibril();
[a35b458]653
[5d230a30]654 size_t seed = (size_t) atomic_preinc(&arg->seed);
655 bool first = (seed == 1);
[a35b458]656
[5d230a30]657 for (size_t k = 0; k < arg->read_iters; ++k) {
658 /* Print progress if the first reader fibril. */
659 if (first && 0 == k % (arg->read_iters/100 + 1)) {
660 printf(".");
661 }
[a35b458]662
[5d230a30]663 rcu_read_lock();
664 atomic_count_t start_time = atomic_preinc(&arg->time);
[a35b458]665
[5d230a30]666 /* Do some work. */
667 seed = next_rand(seed);
668 size_t idle_iters = seed % 8;
[a35b458]669
[5d230a30]670 for (size_t i = 0; i < idle_iters; ++i) {
671 fibril_yield();
672 }
[a35b458]673
[1b20da0]674 /*
[5d230a30]675 * Check if the most recently started rcu_sync of the already
676 * finished rcu_syncs did not happen to start after this reader
677 * and, therefore, should have waited for this reader to exit
678 * (but did not - since it already announced it completed).
679 */
680 if (start_time <= atomic_get(&arg->max_start_time_of_done_sync)) {
681 arg->failed = 1;
682 }
[a35b458]683
[5d230a30]684 rcu_read_unlock();
685 }
[a35b458]686
[5d230a30]687 rcu_deregister_fibril();
688
689 signal_seq_fibril_done(arg, &arg->done_reader_cnt);
690 return 0;
691}
692
[b7fd2a0]693static errno_t seq_updater(seq_test_info_t *arg)
[5d230a30]694{
695 rcu_register_fibril();
[a35b458]696
[5d230a30]697 for (size_t k = 0; k < arg->upd_iters; ++k) {
698 atomic_count_t start_time = atomic_get(&arg->time);
699 rcu_synchronize();
[a35b458]700
[5d230a30]701 /* This is prone to a race but if it happens it errs to the safe side.*/
702 if (atomic_get(&arg->max_start_time_of_done_sync) < start_time) {
703 atomic_set(&arg->max_start_time_of_done_sync, start_time);
704 }
705 }
[a35b458]706
[5d230a30]707 rcu_deregister_fibril();
[a35b458]708
[5d230a30]709 signal_seq_fibril_done(arg, &arg->done_updater_cnt);
710 return 0;
711}
712
713static bool seq_test(test_info_t *test_info)
714{
[1b20da0]715 size_t reader_cnt = test_info->thread_cnt;
716 size_t updater_cnt = test_info->thread_cnt;
[a35b458]717
[5d230a30]718 seq_test_info_t info = {
719 .time = {0},
720 .max_start_time_of_done_sync = {0},
721 .read_iters = 10 * 1000,
722 .upd_iters = 5 * 1000,
723 .total_workers = updater_cnt + reader_cnt,
724 .done_reader_cnt = 0,
725 .done_updater_cnt = 0,
726 .done_cnt_mtx = FIBRIL_MUTEX_INITIALIZER(info.done_cnt_mtx),
727 .done_cnt_changed = FIBRIL_CONDVAR_INITIALIZER(info.done_cnt_changed),
728 .seed = {0},
729 .failed = 0,
730 };
[a35b458]731
[5d230a30]732 /* Create and start worker fibrils. */
733 for (size_t k = 0; k + k < reader_cnt + updater_cnt; ++k) {
734 bool ok = create_fibril((fibril_func_t) seq_reader, &info);
735 ok = ok && create_fibril((fibril_func_t) seq_updater, &info);
[a35b458]736
[5d230a30]737 if (!ok) {
738 /* Let the already created fibrils corrupt the stack. */
739 return false;
740 }
741 }
[a35b458]742
[5d230a30]743 /* Wait for all worker fibrils to complete their work. */
744 fibril_mutex_lock(&info.done_cnt_mtx);
[a35b458]745
[5d230a30]746 while (info.total_workers != info.done_reader_cnt + info.done_updater_cnt) {
747 fibril_condvar_wait(&info.done_cnt_changed, &info.done_cnt_mtx);
748 }
[a35b458]749
[5d230a30]750 fibril_mutex_unlock(&info.done_cnt_mtx);
[a35b458]751
[5d230a30]752 if (info.failed) {
753 printf("Error: rcu_sync() did not wait for a preexisting reader.");
754 }
[a35b458]755
[5d230a30]756 return 0 == info.failed;
757}
758
[29b8138]759/*--------------------------------------------------------------------*/
[d04e46e]760
761static FIBRIL_MUTEX_INITIALIZE(blocking_mtx);
762
763static void dummy_fibril(void *arg)
764{
765 /* Block on an already locked mutex - enters the fibril manager. */
766 fibril_mutex_lock(&blocking_mtx);
767 assert(false);
768}
769
770static bool create_threads(size_t cnt)
771{
772 /* Sanity check. */
773 assert(cnt < 1024);
[a35b458]774
[d04e46e]775 /* Keep this mutex locked so that dummy fibrils never exit. */
776 bool success = fibril_mutex_trylock(&blocking_mtx);
777 assert(success);
[a35b458]778
[d04e46e]779 for (size_t k = 0; k < cnt; ++k) {
780 thread_id_t tid;
[a35b458]781
[b7fd2a0]782 errno_t ret = thread_create(dummy_fibril, NULL, "urcu-test-worker", &tid);
[d04e46e]783 if (EOK != ret) {
[c1694b6b]784 printf("Failed to create thread '%zu' (error: %s)\n", k + 1, str_error_name(ret));
[d04e46e]785 return false;
786 }
787 }
[a35b458]788
[d04e46e]789 return true;
790}
791
[29b8138]792/*--------------------------------------------------------------------*/
793static test_desc_t *find_test(const char *name)
794{
795 /* First match for test name. */
796 for (size_t k = 0; k < test_desc_cnt; ++k) {
797 test_desc_t *t = &test_desc[k];
[a35b458]798
[29b8138]799 if (t->func && 0 == str_cmp(t->name, name))
800 return t;
801 }
[a35b458]802
[29b8138]803 /* Try to match the test number. */
804 uint32_t test_num = 0;
[a35b458]805
[29b8138]806 if (EOK == str_uint32_t(name, NULL, 0, true, &test_num)) {
807 if (test_num < test_desc_cnt && test_desc[test_num].func) {
808 return &test_desc[test_num];
809 }
810 }
[a35b458]811
[29b8138]812 return NULL;
813}
814
815static void list_tests(void)
816{
817 printf("Available tests: \n");
[a35b458]818
[29b8138]819 for (size_t i = 0; i < test_desc_cnt; ++i) {
820 test_desc_t *t = &test_desc[i];
[a35b458]821
[1b20da0]822 if (!t->func)
[29b8138]823 continue;
[a35b458]824
[29b8138]825 const char *type = "";
[a35b458]826
[29b8138]827 if (t->type == T_SANITY)
828 type = " (sanity)";
829 if (t->type == T_STRESS)
830 type = " (stress)";
831
832 printf("%zu: %s ..%s %s\n", i, t->name, type, t->desc);
833 }
834}
835
836
837static void print_usage(void)
838{
[d04e46e]839 printf("Usage: rcutest [test_name|test_number] {number_of_threads}\n");
[29b8138]840 list_tests();
[a35b458]841
[29b8138]842 printf("\nExample usage:\n");
843 printf("\trcutest *\n");
844 printf("\trcutest sanity-tests\n");
845}
846
847
[d04e46e]848static bool parse_cmd_line(int argc, char **argv, test_info_t *info)
[29b8138]849{
[d04e46e]850 if (argc != 2 && argc != 3) {
[29b8138]851 print_usage();
[d04e46e]852 return false;
853 }
[a35b458]854
[d04e46e]855 info->desc = find_test(argv[1]);
856
857 if (!info->desc) {
858 printf("Non-existent test '%s'.\n", argv[1]);
859 list_tests();
860 return false;
861 }
[a35b458]862
[d04e46e]863 if (argc == 3) {
864 uint32_t thread_cnt = 0;
[b7fd2a0]865 errno_t ret = str_uint32_t(argv[2], NULL, 0, true, &thread_cnt);
[a35b458]866
[d04e46e]867 if (ret == EOK && 1 <= thread_cnt && thread_cnt <= 64) {
868 info->thread_cnt = thread_cnt;
869 } else {
870 info->thread_cnt = 1;
871 printf("Err: Invalid number of threads '%s'; using 1.\n", argv[2]);
[1b20da0]872 }
[5d230a30]873 } else {
874 info->thread_cnt = 1;
[29b8138]875 }
[a35b458]876
[d04e46e]877 return true;
878}
879
880int main(int argc, char **argv)
881{
882 rcu_register_fibril();
[a35b458]883
[d04e46e]884 test_info_t info;
[a35b458]885
[d04e46e]886 bool ok = parse_cmd_line(argc, argv, &info);
887 ok = ok && create_threads(info.thread_cnt - 1);
[a35b458]888
[d04e46e]889 if (ok) {
890 assert(1 <= info.thread_cnt);
891 test_desc_t *t = info.desc;
[a35b458]892
[d04e46e]893 printf("Running '%s' (in %zu threads)...\n", t->name, info.thread_cnt);
894 ok = t->func(&info);
895
[29b8138]896 printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name);
[d04e46e]897
[29b8138]898 rcu_deregister_fibril();
[a35b458]899
[d04e46e]900 /* Let the kernel clean up the created background threads. */
[29b8138]901 return ok ? 0 : 1;
902 } else {
903 rcu_deregister_fibril();
[d04e46e]904 return 2;
[29b8138]905 }
906}
907
[94b39ba]908
909/**
910 * @}
911 */
Note: See TracBrowser for help on using the repository browser.