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

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

Remove unnecessary includes from <stdio.h>.

  • Property mode set to 100644
File size: 19.8 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.