Index: uspace/app/rcutest/rcutest.c
===================================================================
--- uspace/app/rcutest/rcutest.c	(revision a879d73cc0f7f958b997643d14d34dd72ecbe10e)
+++ uspace/app/rcutest/rcutest.c	(revision d04e46e733c1ef74eb93b6fc924b1cd286dd4502)
@@ -48,8 +48,12 @@
 #include <rcu.h>
 
+#include "fibril_synch.h"
+
 
 #define USECS_PER_SEC (1000 * 1000)
 #define USECS_PER_MS  1000
 
+/* fwd decl. */
+struct test_info;
 
 typedef struct test_desc {
@@ -61,5 +65,5 @@
 		T_STRESS
 	} type;
-	bool (*func)(void);
+	bool (*func)(struct test_info*);
 	const char *name;
 	const char *desc;
@@ -67,12 +71,19 @@
 
 
-static bool run_all_tests(void);
-static bool run_sanity_tests(void);
-static bool run_stress_tests(void);
-
-static bool wait_for_one_reader(void);
-static bool basic_sanity_check(void);
-static bool dont_wait_for_new_reader(void);
-static bool wait_for_exiting_reader(void);
+typedef struct test_info {
+	size_t thread_cnt;
+	test_desc_t *desc;
+} test_info_t;
+
+
+
+static bool run_all_tests(struct test_info*);
+static bool run_sanity_tests(struct test_info*);
+static bool run_stress_tests(struct test_info*);
+
+static bool wait_for_one_reader(struct test_info*);
+static bool basic_sanity_check(struct test_info*);
+static bool dont_wait_for_new_reader(struct test_info*);
+static bool wait_for_exiting_reader(struct test_info*);
 
 static test_desc_t test_desc[] = {
@@ -157,5 +168,5 @@
 /*--------------------------------------------------------------------*/
 
-static bool run_tests(bool (*include_filter)(test_desc_t *)) 
+static bool run_tests(test_info_t *info, bool (*include_filter)(test_desc_t *)) 
 {
 	size_t failed_cnt = 0;
@@ -167,5 +178,5 @@
 		if (t->func && !t->aggregate && include_filter(t)) {
 			printf("Running \'%s\'...\n", t->name);
-			bool ok = test_desc[i].func();
+			bool ok = test_desc[i].func(info);
 			
 			if (ok) {
@@ -198,8 +209,8 @@
 
 /* Runs all available tests tests one-by-one. */
-static bool run_all_tests(void)
+static bool run_all_tests(test_info_t *test_info)
 {
 	printf("Running all tests...\n");
-	return run_tests(all_tests_include_filter);
+	return run_tests(test_info, all_tests_include_filter);
 }
 
@@ -212,8 +223,8 @@
 
 /* Runs all available stress tests one-by-one. */
-static bool run_stress_tests(void)
+static bool run_stress_tests(test_info_t *test_info)
 {
 	printf("Running stress tests...\n");
-	return run_tests(stress_tests_include_filter);
+	return run_tests(test_info, stress_tests_include_filter);
 }
 
@@ -226,8 +237,8 @@
 
 /* Runs all available sanity tests one-by-one. */
-static bool run_sanity_tests(void)
+static bool run_sanity_tests(test_info_t *test_info)
 {
 	printf("Running sanity tests...\n");
-	return run_tests(sanity_tests_include_filter);
+	return run_tests(test_info, sanity_tests_include_filter);
 }
 
@@ -235,5 +246,5 @@
 
 /* Locks/unlocks rcu and synchronizes without contention in a single fibril. */
-static bool basic_sanity_check(void)
+static bool basic_sanity_check(test_info_t *test_info)
 {
 	rcu_read_lock();
@@ -309,5 +320,5 @@
 }
 
-static bool wait_for_one_reader(void)
+static bool wait_for_one_reader(test_info_t *test_info)
 {
 	one_reader_info_t info = { 0 };
@@ -446,5 +457,5 @@
 }
 
-static bool dont_wait_for_new_reader(void)
+static bool dont_wait_for_new_reader(test_info_t *test_info)
 {
 	two_reader_info_t info = { 0 };
@@ -553,5 +564,5 @@
 
 
-static bool wait_for_exiting_reader(void)
+static bool wait_for_exiting_reader(test_info_t *test_info)
 {
 	exit_reader_info_t info = { 0 };
@@ -588,4 +599,36 @@
 
 /*--------------------------------------------------------------------*/
+
+static FIBRIL_MUTEX_INITIALIZE(blocking_mtx);
+
+static void dummy_fibril(void *arg)
+{
+	/* Block on an already locked mutex - enters the fibril manager. */
+	fibril_mutex_lock(&blocking_mtx);
+	assert(false);
+}
+
+static bool create_threads(size_t cnt)
+{
+	/* Sanity check. */
+	assert(cnt < 1024);
+	
+	/* Keep this mutex locked so that dummy fibrils never exit. */
+	bool success = fibril_mutex_trylock(&blocking_mtx);
+	assert(success);
+	
+	for (size_t k = 0; k < cnt; ++k) {
+		thread_id_t tid;
+		
+		int ret = thread_create(dummy_fibril, NULL, "urcu-test-worker", &tid);
+		if (EOK != ret) {
+			printf("Failed to create thread '%zu' (error: %d)\n", k + 1, ret);
+			return false;
+		}
+	}
+	
+	return true;
+}
+
 /*--------------------------------------------------------------------*/
 static test_desc_t *find_test(const char *name)
@@ -604,5 +647,4 @@
 	if (EOK == str_uint32_t(name, NULL, 0, true, &test_num)) {
 		if (test_num < test_desc_cnt && test_desc[test_num].func) {
-			printf("[%u]\n", test_num);
 			return &test_desc[test_num];
 		}
@@ -636,5 +678,5 @@
 static void print_usage(void)
 {
-	printf("Usage: rcutest [test_name|test_number]\n");
+	printf("Usage: rcutest [test_name|test_number] {number_of_threads}\n");
 	list_tests();
 	
@@ -645,31 +687,59 @@
 
 
+static bool parse_cmd_line(int argc, char **argv, test_info_t *info)
+{
+	if (argc != 2 && argc != 3) {
+		print_usage();
+		return false;
+	}
+	
+	info->desc = find_test(argv[1]);
+
+	if (!info->desc) {
+		printf("Non-existent test '%s'.\n", argv[1]);
+		list_tests();
+		return false;
+	}
+	
+	if (argc == 3) {
+		uint32_t thread_cnt = 0;
+		int ret = str_uint32_t(argv[2], NULL, 0, true, &thread_cnt);
+		
+		if (ret == EOK && 1 <= thread_cnt && thread_cnt <= 64) {
+			info->thread_cnt = thread_cnt;
+		} else {
+			info->thread_cnt = 1;
+			printf("Err: Invalid number of threads '%s'; using 1.\n", argv[2]);
+		} 
+	}
+	
+	return true;
+}
+
 int main(int argc, char **argv)
 {
 	rcu_register_fibril();
 	
-	if (argc != 2) {
-		print_usage();
-		
+	test_info_t info;
+	
+	bool ok = parse_cmd_line(argc, argv, &info);
+	ok = ok && create_threads(info.thread_cnt - 1);
+	
+	if (ok) {
+		assert(1 <= info.thread_cnt);
+		test_desc_t *t = info.desc;
+		
+		printf("Running '%s' (in %zu threads)...\n", t->name, info.thread_cnt);
+		ok = t->func(&info);
+
+		printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name);
+
+		rcu_deregister_fibril();
+		
+		/* Let the kernel clean up the created background threads. */
+		return ok ? 0 : 1;
+	} else {
 		rcu_deregister_fibril();
 		return 2;
-	}
-	
-	test_desc_t *t = find_test(argv[1]);
-	
-	if (t) {
-		printf("Running '%s'...\n", t->name);
-		bool ok = t->func();
-		
-		printf("%s: '%s'\n", ok ? "Passed" : "FAILED", t->name);
-		
-		rcu_deregister_fibril();
-		return ok ? 0 : 1;
-	} else {
-		printf("Non-existent test name.\n");
-		list_tests();
-		
-		rcu_deregister_fibril();
-		return 3;
 	}
 }
