• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz>
4  *
5  * Block several threads on a private mutex, then wake them up.
6  */
7 
8 #include <sys/types.h>
9 
10 #include "futextest.h"
11 #include "futex_utils.h"
12 #include "tst_safe_pthread.h"
13 
14 static futex_t futex = FUTEX_INITIALIZER;
15 
16 static volatile int threads_flags[55];
17 
18 static struct futex_test_variants variants[] = {
19 #if (__NR_futex != __LTP__NR_INVALID_SYSCALL)
20 	{ .fntype = FUTEX_FN_FUTEX, .desc = "syscall with old kernel spec"},
21 #endif
22 
23 #if (__NR_futex_time64 != __LTP__NR_INVALID_SYSCALL)
24 	{ .fntype = FUTEX_FN_FUTEX64, .desc = "syscall time64 with kernel spec"},
25 #endif
26 };
27 
threads_awake(void)28 static int threads_awake(void)
29 {
30 	int ret = 0;
31 	unsigned int i;
32 
33 	for (i = 0; i < ARRAY_SIZE(threads_flags); i++) {
34 		if (threads_flags[i])
35 			ret++;
36 	}
37 
38 	return ret;
39 }
40 
clear_threads_awake(void)41 static void clear_threads_awake(void)
42 {
43 	unsigned int i;
44 
45 	for (i = 0; i < ARRAY_SIZE(threads_flags); i++)
46 		threads_flags[i] = 0;
47 }
48 
threaded(void * arg)49 static void *threaded(void *arg)
50 {
51 	struct futex_test_variants *tv = &variants[tst_variant];
52 	long i = (long)arg;
53 
54 	futex_wait(tv->fntype, &futex, futex, NULL, FUTEX_PRIVATE_FLAG);
55 
56 	threads_flags[i] = 1;
57 
58 	return NULL;
59 }
60 
do_child(void)61 static void do_child(void)
62 {
63 	struct futex_test_variants *tv = &variants[tst_variant];
64 	int i, j, awake;
65 	pthread_t t[55];
66 
67 	for (i = 0; i < (int)ARRAY_SIZE(t); i++)
68 		SAFE_PTHREAD_CREATE(&t[i], NULL, threaded, (void*)((long)i));
69 
70 	while (wait_for_threads(ARRAY_SIZE(t)))
71 		usleep(100);
72 
73 	for (i = 1; i <= 10; i++) {
74 		clear_threads_awake();
75 		TEST(futex_wake(tv->fntype, &futex, i, FUTEX_PRIVATE_FLAG));
76 		if (i != TST_RET) {
77 			tst_res(TFAIL | TTERRNO,
78 			         "futex_wake() woken up %li threads, expected %i",
79 			         TST_RET, i);
80 		}
81 
82 		for (j = 0; j < 100000; j++) {
83 			awake = threads_awake();
84 			if (awake == i)
85 				break;
86 
87 			usleep(100);
88 		}
89 
90 		if (awake == i) {
91 			tst_res(TPASS, "futex_wake() woken up %i threads", i);
92 		} else {
93 			tst_res(TFAIL, "Woken up %i threads, expected %i",
94 				awake, i);
95 		}
96 	}
97 
98 	TEST(futex_wake(tv->fntype, &futex, 1, FUTEX_PRIVATE_FLAG));
99 	if (TST_RET) {
100 		tst_res(TFAIL | TTERRNO, "futex_wake() woken up %li, none were waiting",
101 			TST_RET);
102 	} else {
103 		tst_res(TPASS, "futex_wake() woken up 0 threads");
104 	}
105 
106 	for (i = 0; i < (int)ARRAY_SIZE(t); i++)
107 		SAFE_PTHREAD_JOIN(t[i], NULL);
108 
109 	exit(0);
110 }
111 
112 /*
113  * We do the real test in a child because with the test -i parameter the loop
114  * that checks that all threads are sleeping may fail with ENOENT. That is
115  * because some of the threads from previous run may still be there.
116  *
117  * Which is because the userspace part of pthread_join() sleeps in a futex on a
118  * pthread tid which is woken up at the end of the exit_mm(tsk) which is before
119  * the process is removed from the parent thread_group list. So there is a
120  * small race window where the readdir() returns the process tid as a directory
121  * under /proc/$PID/tasks/, but the subsequent open() fails with ENOENT because
122  * the thread was removed meanwhile.
123  */
run(void)124 static void run(void)
125 {
126 	if (!SAFE_FORK())
127 		do_child();
128 }
129 
setup(void)130 static void setup(void)
131 {
132 	struct futex_test_variants *tv = &variants[tst_variant];
133 
134 	tst_res(TINFO, "Testing variant: %s", tv->desc);
135 	futex_supported_by_kernel(tv->fntype);
136 }
137 
138 static struct tst_test test = {
139 	.setup = setup,
140 	.test_all = run,
141 	.test_variants = ARRAY_SIZE(variants),
142 	.forks_child = 1,
143 };
144