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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since be1b1e68 was 5d230a30, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

urcu: Added stress test checking for premature exits of rcu_sync via sequence numbers.

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