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

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

Move some internal interfaces to private headers.

  • Property mode set to 100644
File size: 19.1 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 <assert.h>
44#include <async.h>
45#include <fibril.h>
46#include <fibril_synch.h>
47#include <compiler/barrier.h>
48#include <str.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 errno_t (*fibril_func_t)(void *);
170
171static bool create_fibril(errno_t (*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 errno_t 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 fibril_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 fibril_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 fibril_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 errno_t 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 fibril_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 fibril_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 errno_t new_reader(two_reader_info_t *arg)
432{
433 rcu_register_fibril();
434
435 /* Wait until rcu_sync() starts. */
436 while (!arg->synching) {
437 fibril_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 fibril_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 fibril_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 fibril_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 fibril_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 errno_t 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 fibril_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 fibril_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 errno_t 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 errno_t 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 bool create_threads(size_t cnt)
760{
761 /* Sanity check. */
762 assert(cnt < 1024);
763 fibril_test_spawn_runners(cnt);
764 return true;
765}
766
767/*--------------------------------------------------------------------*/
768static test_desc_t *find_test(const char *name)
769{
770 /* First match for test name. */
771 for (size_t k = 0; k < test_desc_cnt; ++k) {
772 test_desc_t *t = &test_desc[k];
773
774 if (t->func && 0 == str_cmp(t->name, name))
775 return t;
776 }
777
778 /* Try to match the test number. */
779 uint32_t test_num = 0;
780
781 if (EOK == str_uint32_t(name, NULL, 0, true, &test_num)) {
782 if (test_num < test_desc_cnt && test_desc[test_num].func) {
783 return &test_desc[test_num];
784 }
785 }
786
787 return NULL;
788}
789
790static void list_tests(void)
791{
792 printf("Available tests: \n");
793
794 for (size_t i = 0; i < test_desc_cnt; ++i) {
795 test_desc_t *t = &test_desc[i];
796
797 if (!t->func)
798 continue;
799
800 const char *type = "";
801
802 if (t->type == T_SANITY)
803 type = " (sanity)";
804 if (t->type == T_STRESS)
805 type = " (stress)";
806
807 printf("%zu: %s ..%s %s\n", i, t->name, type, t->desc);
808 }
809}
810
811
812static void print_usage(void)
813{
814 printf("Usage: rcutest [test_name|test_number] {number_of_threads}\n");
815 list_tests();
816
817 printf("\nExample usage:\n");
818 printf("\trcutest *\n");
819 printf("\trcutest sanity-tests\n");
820}
821
822
823static bool parse_cmd_line(int argc, char **argv, test_info_t *info)
824{
825 if (argc != 2 && argc != 3) {
826 print_usage();
827 return false;
828 }
829
830 info->desc = find_test(argv[1]);
831
832 if (!info->desc) {
833 printf("Non-existent test '%s'.\n", argv[1]);
834 list_tests();
835 return false;
836 }
837
838 if (argc == 3) {
839 uint32_t thread_cnt = 0;
840 errno_t ret = str_uint32_t(argv[2], NULL, 0, true, &thread_cnt);
841
842 if (ret == EOK && 1 <= thread_cnt && thread_cnt <= 64) {
843 info->thread_cnt = thread_cnt;
844 } else {
845 info->thread_cnt = 1;
846 printf("Err: Invalid number of threads '%s'; using 1.\n", argv[2]);
847 }
848 } else {
849 info->thread_cnt = 1;
850 }
851
852 return true;
853}
854
855int main(int argc, char **argv)
856{
857 rcu_register_fibril();
858
859 test_info_t info;
860
861 bool ok = parse_cmd_line(argc, argv, &info);
862 ok = ok && create_threads(info.thread_cnt - 1);
863
864 if (ok) {
865 assert(1 <= info.thread_cnt);
866 test_desc_t *t = info.desc;
867
868 printf("Running '%s' (in %zu threads)...\n", t->name, info.thread_cnt);
869 ok = t->func(&info);
870
871 printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name);
872
873 rcu_deregister_fibril();
874
875 /* Let the kernel clean up the created background threads. */
876 return ok ? 0 : 1;
877 } else {
878 rcu_deregister_fibril();
879 return 2;
880 }
881}
882
883
884/**
885 * @}
886 */
Note: See TracBrowser for help on using the repository browser.