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
Line 
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>
40#include <str_error.h>
41#include <mem.h>
42#include <errno.h>
43#include <thread.h>
44#include <assert.h>
45#include <async.h>
46#include <fibril.h>
47#include <fibril_synch.h>
48#include <compiler/barrier.h>
49#include <futex.h>
50#include <str.h>
51
52#include <rcu.h>
53
54
55
56#define USECS_PER_SEC (1000 * 1000)
57#define USECS_PER_MS 1000
58
59/* fwd decl. */
60struct test_info;
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;
70 bool (*func)(struct test_info*);
71 const char *name;
72 const char *desc;
73} test_desc_t;
74
75
76typedef struct test_info {
77 size_t thread_cnt;
78 test_desc_t *desc;
79} test_info_t;
80
81
82
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*);
91static bool seq_test(struct test_info*);
92
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",
122 .desc = "Locks/unlocks and syncs in 1 fibril, no contention.",
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",
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.",
144 },
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 },
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
165static size_t next_rand(size_t seed)
166{
167 return (seed * 1103515245 + 12345) & ((1U << 31) - 1);
168}
169
170
171typedef errno_t (*fibril_func_t)(void *);
172
173static bool create_fibril(errno_t (*func)(void*), void *arg)
174{
175 fid_t fid = fibril_create(func, arg);
176
177 if (0 == fid) {
178 printf("Failed to create a fibril!\n");
179 return false;
180 }
181
182 fibril_add_ready(fid);
183 return true;
184}
185
186/*--------------------------------------------------------------------*/
187
188static bool run_tests(test_info_t *info, bool (*include_filter)(test_desc_t *))
189{
190 size_t failed_cnt = 0;
191 size_t ok_cnt = 0;
192
193 for (size_t i = 0; i < test_desc_cnt; ++i) {
194 test_desc_t *t = &test_desc[i];
195
196 if (t->func && !t->aggregate && include_filter(t)) {
197 printf("Running \'%s\'...\n", t->name);
198 bool ok = test_desc[i].func(info);
199
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 }
209
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);
216 }
217
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. */
229static bool run_all_tests(test_info_t *test_info)
230{
231 printf("Running all tests...\n");
232 return run_tests(test_info, all_tests_include_filter);
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. */
243static bool run_stress_tests(test_info_t *test_info)
244{
245 printf("Running stress tests...\n");
246 return run_tests(test_info, stress_tests_include_filter);
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. */
257static bool run_sanity_tests(test_info_t *test_info)
258{
259 printf("Running sanity tests...\n");
260 return run_tests(test_info, sanity_tests_include_filter);
261}
262
263/*--------------------------------------------------------------------*/
264
265/* Locks/unlocks rcu and synchronizes without contention in a single fibril. */
266static bool basic_sanity_check(test_info_t *test_info)
267{
268 rcu_read_lock();
269 /* nop */
270 rcu_read_unlock();
271
272 rcu_read_lock();
273 /* nop */
274 rcu_read_unlock();
275
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();
285 rcu_read_unlock();
286
287 fibril_yield();
288 rcu_synchronize();
289 rcu_synchronize();
290
291 rcu_read_lock();
292 /* nop */
293 if (!rcu_read_locked())
294 return false;
295
296 rcu_read_unlock();
297
298 return !rcu_read_locked();
299}
300
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;
307 size_t failed;
308} one_reader_info_t;
309
310
311static errno_t sleeping_reader(one_reader_info_t *arg)
312{
313 rcu_register_fibril();
314
315 printf("lock{");
316 rcu_read_lock();
317 rcu_read_lock();
318 arg->entered_cs = true;
319 rcu_read_unlock();
320
321 printf("r-sleep{");
322 /* 2 sec */
323 async_usleep(2 * USECS_PER_SEC);
324 ++arg->done_sleeps_cnt;
325 printf("}");
326
327 if (arg->synched) {
328 arg->failed = 1;
329 printf("Error: rcu_sync exited prematurely.\n");
330 }
331
332 arg->exited_cs = true;
333 rcu_read_unlock();
334 printf("}");
335
336 rcu_deregister_fibril();
337 return 0;
338}
339
340static bool wait_for_one_reader(test_info_t *test_info)
341{
342 one_reader_info_t info = { 0 };
343
344 if (!create_fibril((fibril_func_t) sleeping_reader, &info))
345 return false;
346
347 /* 1 sec, waits for the reader to enter its critical section and sleep. */
348 async_usleep(1 * USECS_PER_SEC);
349
350 if (!info.entered_cs || info.exited_cs) {
351 printf("Error: reader is unexpectedly outside of critical section.\n");
352 return false;
353 }
354
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();
363
364 if (!info.exited_cs || info.failed) {
365 printf("Error: rcu_sync() returned before the reader exited its CS.\n");
366 /*
367 * Sleep some more so we don't free info on stack while the reader
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
380#define WAIT_STEP_US 500 * USECS_PER_MS
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
393static errno_t preexisting_reader(two_reader_info_t *arg)
394{
395 rcu_register_fibril();
396
397 printf("old-lock{");
398 rcu_read_lock();
399 arg->old_entered_cs = true;
400
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(" }");
407
408 /* A new reader starts while rcu_sync() is in progress. */
409
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(" }");
416
417 arg->old_exited_cs = true;
418
419 assert(!arg->new_exited_cs);
420
421 if (arg->synched) {
422 arg->failed = 1;
423 printf("Error: rcu_sync() did not wait for preexisting reader.\n");
424 }
425
426 rcu_read_unlock();
427 printf(" }");
428
429 rcu_deregister_fibril();
430 return 0;
431}
432
433static errno_t new_reader(two_reader_info_t *arg)
434{
435 rcu_register_fibril();
436
437 /* Wait until rcu_sync() starts. */
438 while (!arg->synching) {
439 async_usleep(WAIT_STEP_US);
440 }
441
442 /*
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);
447
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 }
456
457 arg->new_exited_cs = true;
458 /* Write new_exited_cs before exiting reader section. */
459 memory_barrier();
460
461 /*
462 * Preexisting reader should have exited by now, so rcu_synchronize()
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 }
469
470 rcu_read_unlock();
471 printf(")");
472
473 rcu_deregister_fibril();
474 return 0;
475}
476
477static bool dont_wait_for_new_reader(test_info_t *test_info)
478{
479 two_reader_info_t info = { 0 };
480
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;
486
487 /* Waits for the preexisting_reader to enter its CS.*/
488 while (!info.old_entered_cs) {
489 async_usleep(WAIT_STEP_US);
490 }
491
492 assert(!info.old_exited_cs);
493 assert(!info.new_entered_cs);
494 assert(!info.new_exited_cs);
495
496 printf("sync[");
497 info.synching = true;
498 rcu_synchronize();
499 printf(" ]");
500
501 /* Load info.exited_cs */
502 memory_barrier();
503
504 if (!info.old_exited_cs) {
505 printf("Error: rcu_sync() returned before preexisting reader exited.\n");
506 info.failed = 1;
507 }
508
509 bool new_outside_cs = !info.new_entered_cs || info.new_exited_cs;
510
511 /* Test if new reader is waiting in CS before setting synched. */
512 compiler_barrier();
513 info.synched = true;
514
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();
521
522 if (!info.new_exited_cs) {
523 printf("Error: 2nd rcu_sync() returned before new reader exited.\n");
524 info.failed = 1;
525 }
526
527 printf("\n");
528 }
529
530 if (info.failed) {
531 /*
532 * Sleep some more so we don't free info on stack while readers
533 * are using it.
534 */
535 async_usleep(WAIT_STEP_US);
536 }
537
538 return 0 == info.failed;
539}
540
541#undef WAIT_STEP_US
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
554static errno_t exiting_locked_reader(exit_reader_info_t *arg)
555{
556 rcu_register_fibril();
557
558 printf("old-lock{");
559 rcu_read_lock();
560 rcu_read_lock();
561 rcu_read_lock();
562 arg->entered_cs = true;
563
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(" }");
570
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();
577
578 /* Deregister forcefully unlocks the reader section. */
579 rcu_deregister_fibril();
580 return 0;
581}
582
583
584static bool wait_for_exiting_reader(test_info_t *test_info)
585{
586 exit_reader_info_t info = { 0 };
587
588 if (!create_fibril((fibril_func_t) exiting_locked_reader, &info))
589 return false;
590
591 /* Waits for the preexisting_reader to enter its CS.*/
592 while (!info.entered_cs) {
593 async_usleep(WAIT_STEP_US);
594 }
595
596 assert(!info.exited_cs);
597
598 printf("sync[");
599 info.synching = true;
600 rcu_synchronize();
601 info.synched = true;
602 printf(" ]\n");
603
604 /* Load info.exited_cs */
605 memory_barrier();
606
607 if (!info.exited_cs) {
608 printf("Error: rcu_deregister_fibril did not unlock the CS.\n");
609 return false;
610 }
611
612 return true;
613}
614
615#undef WAIT_STEP_US
616
617
618/*--------------------------------------------------------------------*/
619
620typedef struct {
621 atomic_t time;
622 atomic_t max_start_time_of_done_sync;
623
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;
632
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;
642
643 if (arg->total_workers == arg->done_reader_cnt + arg->done_updater_cnt) {
644 fibril_condvar_signal(&arg->done_cnt_changed);
645 }
646
647 fibril_mutex_unlock(&arg->done_cnt_mtx);
648}
649
650static errno_t seq_reader(seq_test_info_t *arg)
651{
652 rcu_register_fibril();
653
654 size_t seed = (size_t) atomic_preinc(&arg->seed);
655 bool first = (seed == 1);
656
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 }
662
663 rcu_read_lock();
664 atomic_count_t start_time = atomic_preinc(&arg->time);
665
666 /* Do some work. */
667 seed = next_rand(seed);
668 size_t idle_iters = seed % 8;
669
670 for (size_t i = 0; i < idle_iters; ++i) {
671 fibril_yield();
672 }
673
674 /*
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 }
683
684 rcu_read_unlock();
685 }
686
687 rcu_deregister_fibril();
688
689 signal_seq_fibril_done(arg, &arg->done_reader_cnt);
690 return 0;
691}
692
693static errno_t seq_updater(seq_test_info_t *arg)
694{
695 rcu_register_fibril();
696
697 for (size_t k = 0; k < arg->upd_iters; ++k) {
698 atomic_count_t start_time = atomic_get(&arg->time);
699 rcu_synchronize();
700
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 }
706
707 rcu_deregister_fibril();
708
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{
715 size_t reader_cnt = test_info->thread_cnt;
716 size_t updater_cnt = test_info->thread_cnt;
717
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 };
731
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);
736
737 if (!ok) {
738 /* Let the already created fibrils corrupt the stack. */
739 return false;
740 }
741 }
742
743 /* Wait for all worker fibrils to complete their work. */
744 fibril_mutex_lock(&info.done_cnt_mtx);
745
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 }
749
750 fibril_mutex_unlock(&info.done_cnt_mtx);
751
752 if (info.failed) {
753 printf("Error: rcu_sync() did not wait for a preexisting reader.");
754 }
755
756 return 0 == info.failed;
757}
758
759/*--------------------------------------------------------------------*/
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);
774
775 /* Keep this mutex locked so that dummy fibrils never exit. */
776 bool success = fibril_mutex_trylock(&blocking_mtx);
777 assert(success);
778
779 for (size_t k = 0; k < cnt; ++k) {
780 thread_id_t tid;
781
782 errno_t ret = thread_create(dummy_fibril, NULL, "urcu-test-worker", &tid);
783 if (EOK != ret) {
784 printf("Failed to create thread '%zu' (error: %s)\n", k + 1, str_error_name(ret));
785 return false;
786 }
787 }
788
789 return true;
790}
791
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];
798
799 if (t->func && 0 == str_cmp(t->name, name))
800 return t;
801 }
802
803 /* Try to match the test number. */
804 uint32_t test_num = 0;
805
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 }
811
812 return NULL;
813}
814
815static void list_tests(void)
816{
817 printf("Available tests: \n");
818
819 for (size_t i = 0; i < test_desc_cnt; ++i) {
820 test_desc_t *t = &test_desc[i];
821
822 if (!t->func)
823 continue;
824
825 const char *type = "";
826
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{
839 printf("Usage: rcutest [test_name|test_number] {number_of_threads}\n");
840 list_tests();
841
842 printf("\nExample usage:\n");
843 printf("\trcutest *\n");
844 printf("\trcutest sanity-tests\n");
845}
846
847
848static bool parse_cmd_line(int argc, char **argv, test_info_t *info)
849{
850 if (argc != 2 && argc != 3) {
851 print_usage();
852 return false;
853 }
854
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 }
862
863 if (argc == 3) {
864 uint32_t thread_cnt = 0;
865 errno_t ret = str_uint32_t(argv[2], NULL, 0, true, &thread_cnt);
866
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]);
872 }
873 } else {
874 info->thread_cnt = 1;
875 }
876
877 return true;
878}
879
880int main(int argc, char **argv)
881{
882 rcu_register_fibril();
883
884 test_info_t info;
885
886 bool ok = parse_cmd_line(argc, argv, &info);
887 ok = ok && create_threads(info.thread_cnt - 1);
888
889 if (ok) {
890 assert(1 <= info.thread_cnt);
891 test_desc_t *t = info.desc;
892
893 printf("Running '%s' (in %zu threads)...\n", t->name, info.thread_cnt);
894 ok = t->func(&info);
895
896 printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name);
897
898 rcu_deregister_fibril();
899
900 /* Let the kernel clean up the created background threads. */
901 return ok ? 0 : 1;
902 } else {
903 rcu_deregister_fibril();
904 return 2;
905 }
906}
907
908
909/**
910 * @}
911 */
Note: See TracBrowser for help on using the repository browser.