• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz>
3  *
4  * Licensed under the GNU GPLv2 or later.
5  * This program is free software;  you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  * the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program;  if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19  /*
20   * Block several threads on a private mutex, then wake them up.
21   */
22 
23 #include <errno.h>
24 #include <pthread.h>
25 
26 #include "test.h"
27 #include "safe_macros.h"
28 #include "futextest.h"
29 #include "futex_utils.h"
30 
31 const char *TCID="futex_wake02";
32 const int TST_TOTAL=11;
33 
34 static futex_t futex = FUTEX_INITIALIZER;
35 
36 static volatile int threads_flags[55];
37 
threads_awake(void)38 static int threads_awake(void)
39 {
40 	int ret = 0;
41 	unsigned int i;
42 
43 	for (i = 0; i < ARRAY_SIZE(threads_flags); i++) {
44 		if (threads_flags[i])
45 			ret++;
46 	}
47 
48 	return ret;
49 }
50 
clear_threads_awake(void)51 static void clear_threads_awake(void)
52 {
53 	unsigned int i;
54 
55 	for (i = 0; i < ARRAY_SIZE(threads_flags); i++)
56 		threads_flags[i] = 0;
57 }
58 
threaded(void * arg)59 static void *threaded(void *arg)
60 {
61 	long i = (long)arg;
62 
63 	futex_wait(&futex, futex, NULL, FUTEX_PRIVATE_FLAG);
64 
65 	threads_flags[i] = 1;
66 
67 	return NULL;
68 }
69 
do_child(void)70 static void do_child(void)
71 {
72 	int res, i, j, awake;
73 	pthread_t t[55];
74 
75 	for (i = 0; i < (int)ARRAY_SIZE(t); i++) {
76 		res = pthread_create(&t[i], NULL, threaded, (void*)((long)i));
77 		if (res) {
78 			tst_brkm(TBROK, NULL, "pthread_create(): %s",
79 			         tst_strerrno(res));
80 		}
81 	}
82 
83 	while (wait_for_threads(ARRAY_SIZE(t)))
84 		usleep(100);
85 
86 	for (i = 1; i <= 10; i++) {
87 		clear_threads_awake();
88 		res = futex_wake(&futex, i, FUTEX_PRIVATE_FLAG);
89 		if (i != res) {
90 			tst_resm(TFAIL,
91 			         "futex_wake() woken up %i threads, expected %i",
92 			         res, i);
93 		}
94 
95 		for (j = 0; j < 100000; j++) {
96 			awake = threads_awake();
97 			if (awake == i)
98 				break;
99 
100 			usleep(100);
101 		}
102 
103 		if (awake == i) {
104 			tst_resm(TPASS, "futex_wake() woken up %i threads", i);
105 		} else {
106 			tst_resm(TFAIL, "Woken up %i threads, expected %i",
107 			         awake, i);
108 		}
109 	}
110 
111 	res = futex_wake(&futex, 1, FUTEX_PRIVATE_FLAG);
112 
113 	if (res) {
114 		tst_resm(TFAIL, "futex_wake() woken up %i, none were waiting",
115 		         res);
116 	} else {
117 		tst_resm(TPASS, "futex_wake() woken up 0 threads");
118 	}
119 
120 	for (i = 0; i < (int)ARRAY_SIZE(t); i++)
121 		pthread_join(t[i], NULL);
122 
123 	tst_exit();
124 }
125 
126 /*
127  * We do the real test in a child because with the test -i parameter the loop
128  * that checks that all threads are sleeping may fail with ENOENT. That is
129  * because some of the threads from previous run may still be there.
130  *
131  * Which is because the userspace part of pthread_join() sleeps in a futex on a
132  * pthread tid which is woken up at the end of the exit_mm(tsk) which is before
133  * the process is removed from the parent thread_group list. So there is a
134  * small race window where the readdir() returns the process tid as a directory
135  * under /proc/$PID/tasks/, but the subsequent open() fails with ENOENT because
136  * the thread was removed meanwhile.
137  */
verify_futex_wake(void)138 static void verify_futex_wake(void)
139 {
140 	int pid;
141 
142 	pid = tst_fork();
143 
144 	switch (pid) {
145 	case 0:
146 		do_child();
147 	case -1:
148 		tst_brkm(TBROK | TERRNO, NULL, "fork() failed");
149 	default:
150 		tst_record_childstatus(NULL, pid);
151 	}
152 }
153 
main(int argc,char * argv[])154 int main(int argc, char *argv[])
155 {
156 	int lc;
157 
158 	tst_parse_opts(argc, argv, NULL, NULL);
159 
160 	for (lc = 0; TEST_LOOPING(lc); lc++)
161 		verify_futex_wake();
162 
163 	tst_exit();
164 }
165