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

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

Unify various barrier includes into <barrier.h>

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