xref: /linux/tools/testing/selftests/futex/include/futex_thread.h (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #ifndef _FUTEX_THREAD_H
4 #define _FUTEX_THREAD_H
5 #include <errno.h>
6 #include <pthread.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include "kselftest_harness.h"
12 
13 #define USEC_PER_SEC		1000000L
14 #define WAIT_FOR_THREAD_SECS	1
15 #define WAIT_FOR_THREAD_USECS	(WAIT_FOR_THREAD_SECS * USEC_PER_SEC)
16 #define WAIT_THREAD_RETRIES	100
17 
18 struct futex_thread {
19 	pthread_t		thread;
20 	pthread_barrier_t	barrier;
21 	pid_t			tid;
22 	int			(*threadfn)(void *arg);
23 	void			*arg;
24 	int			retval;
25 };
26 
27 static inline int __wait_for_thread(FILE *fp, struct __test_metadata *_metadata)
28 {
29 	unsigned int sleep_time_us = WAIT_FOR_THREAD_USECS / WAIT_THREAD_RETRIES;
30 	char buf[80] = "";
31 
32 	for (int i = 0; i < WAIT_THREAD_RETRIES; i++) {
33 		if (!fgets(buf, sizeof(buf), fp))
34 			return EIO;
35 		if (!strncmp(buf, "futex", 5))
36 			return 0;
37 		usleep(sleep_time_us);
38 		rewind(fp);
39 	}
40 
41 	TH_LOG("/proc/$PID/wchan contains \"%s\". Trying to continue.", buf);
42 	return 0;
43 }
44 
45 static void *__futex_thread_fn(void *arg)
46 {
47 	struct futex_thread *t = arg;
48 
49 	t->tid = gettid();
50 	pthread_barrier_wait(&t->barrier);
51 	t->retval = t->threadfn(t->arg);
52 	return NULL;
53 }
54 
55 /**
56  * futex_wait_for_thread - Wait for the child thread to sleep in the futex context
57  * @t:          Thread handle.
58  * @_metadata:	Test metadata for TH_LOG() context
59  */
60 static inline int futex_wait_for_thread(struct futex_thread *t, struct __test_metadata *_metadata)
61 {
62 	char fname[80];
63 	FILE *fp;
64 	int res;
65 
66 	snprintf(fname, sizeof(fname), "/proc/%d/wchan", t->tid);
67 	fp = fopen(fname, "r");
68 	if (!fp) {
69 		/* If /proc/... is not available, sleep */
70 		if (errno != ENOENT)
71 			return errno;
72 		TH_LOG("/proc/$PID/wchan not accessible, continue with sleep()");
73 		sleep(WAIT_FOR_THREAD_SECS);
74 		return 0;
75 	}
76 
77 	res = __wait_for_thread(fp, _metadata);
78 	fclose(fp);
79 	return res;
80 }
81 
82 /**
83  * futex_thread_create - Create a new thread for testing.
84  * @t:        The handle of the newly created thread.
85  * @threadfn: The new thread starts execution by invoking threadfn
86  * @arg:      The parameters passed to threadfn.
87  */
88 static inline int futex_thread_create(struct futex_thread *t, int (*threadfn)(void *), void *arg)
89 {
90 	pthread_barrier_init(&t->barrier, NULL, 2);
91 
92 	t->tid = 0;
93 	t->threadfn = threadfn;
94 	t->arg = arg;
95 
96 	if (pthread_create(&t->thread, NULL, __futex_thread_fn, t) < 0) {
97 		int ret = errno;
98 		pthread_barrier_destroy(&t->barrier);
99 		return ret;
100 	}
101 
102 	pthread_barrier_wait(&t->barrier);
103 	return 0;
104 }
105 
106 /**
107  * futex_thread_destroy - Wait for and reclaim the resources of the thread.
108  * @t:      Thread handle.
109  */
110 static inline int futex_thread_destroy(struct futex_thread *t)
111 {
112 	pthread_join(t->thread, NULL);
113 	pthread_barrier_destroy(&t->barrier);
114 	return t->retval;
115 }
116 
117 #endif
118