xref: /linux/tools/testing/selftests/futex/functional/futex_wait_timeout.c (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /******************************************************************************
3  *
4  *   Copyright © International Business Machines  Corp., 2009
5  *
6  * DESCRIPTION
7  *      Block on a futex and wait for timeout.
8  *
9  * AUTHOR
10  *      Darren Hart <dvhart@linux.intel.com>
11  *
12  * HISTORY
13  *      2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com>
14  *      2021-Apr-26: More test cases by André Almeida <andrealmeid@collabora.com>
15  *
16  *****************************************************************************/
17 
18 #include <pthread.h>
19 
20 #include "futextest.h"
21 #include "futex2test.h"
22 #include "kselftest_harness.h"
23 
24 static long timeout_ns = 100000;	/* 100us default timeout */
25 static futex_t futex_pi;
26 static pthread_barrier_t barrier;
27 
28 /*
29  * Get a PI lock and hold it forever, so the main thread lock_pi will block
30  * and we can test the timeout
31  */
32 void *get_pi_lock(void *arg)
33 {
34 	struct __test_metadata *_metadata = (struct __test_metadata *)arg;
35 	int ret;
36 	volatile futex_t lock = 0;
37 
38 	ret = futex_lock_pi(&futex_pi, NULL, 0, 0);
39 	ASSERT_EQ(ret, 0)
40 		TH_LOG("futex_lock_pi failed");
41 
42 	pthread_barrier_wait(&barrier);
43 
44 	/* Blocks forever */
45 	ret = futex_wait(&lock, 0, NULL, 0);
46 	ASSERT_TRUE(0)
47 		TH_LOG("futex_wait returned unexpectedly: %d", ret);
48 
49 	return NULL;
50 }
51 
52 #define TEST_TIMEOUT(_res, _test_name, _err) do {				\
53 	if ((_res) < 0 && errno == ENOSYS && (_err) != ENOSYS) {		\
54 		SKIP(return, "%s is not supported (ENOSYS)", _test_name);	\
55 	}									\
56 	EXPECT_EQ((_res), -1)							\
57 		TH_LOG("%s returned unexpected result: %d", _test_name, (_res));\
58 	if ((_res) == -1) {							\
59 		EXPECT_EQ(errno, (_err)) {					\
60 			TH_LOG("%s returned unexpected errno: %d (expected %d)",\
61 			       _test_name, errno, (_err));			\
62 		}								\
63 	}									\
64 } while (0)
65 
66 #define GET_ABS_TIMEOUT(_clockid, _to, _timeout_ns) do {		\
67 	ASSERT_EQ(clock_gettime((_clockid), (_to)), 0)			\
68 		TH_LOG("clock_gettime failed");				\
69 	(_to)->tv_nsec += (_timeout_ns);				\
70 	if ((_to)->tv_nsec >= 1000000000) {				\
71 		(_to)->tv_sec++;					\
72 		(_to)->tv_nsec -= 1000000000;				\
73 	}								\
74 } while (0)
75 
76 TEST(wait_bitset)
77 {
78 	futex_t f1 = FUTEX_INITIALIZER;
79 	struct timespec to;
80 	int res;
81 
82 	/* initialize relative timeout */
83 	to.tv_sec = 0;
84 	to.tv_nsec = timeout_ns;
85 
86 	res = futex_wait(&f1, f1, &to, 0);
87 	TEST_TIMEOUT(res, "futex_wait relative", ETIMEDOUT);
88 
89 	/* FUTEX_WAIT_BITSET with CLOCK_REALTIME */
90 	GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns);
91 	res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME);
92 	TEST_TIMEOUT(res, "futex_wait_bitset realtime", ETIMEDOUT);
93 
94 	/* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */
95 	GET_ABS_TIMEOUT(CLOCK_MONOTONIC, &to, timeout_ns);
96 	res = futex_wait_bitset(&f1, f1, &to, 1, 0);
97 	TEST_TIMEOUT(res, "futex_wait_bitset monotonic", ETIMEDOUT);
98 }
99 
100 TEST(requeue_pi)
101 {
102 	futex_t f1 = FUTEX_INITIALIZER;
103 	struct timespec to;
104 	int res;
105 
106 	/* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */
107 	GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns);
108 	res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME);
109 	TEST_TIMEOUT(res, "futex_wait_requeue_pi realtime", ETIMEDOUT);
110 
111 	/* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */
112 	GET_ABS_TIMEOUT(CLOCK_MONOTONIC, &to, timeout_ns);
113 	res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0);
114 	TEST_TIMEOUT(res, "futex_wait_requeue_pi monotonic", ETIMEDOUT);
115 }
116 
117 TEST(lock_pi)
118 {
119 	struct timespec to;
120 	pthread_t thread;
121 	int res;
122 
123 	/* Create a thread that will lock forever so any waiter will timeout */
124 	pthread_barrier_init(&barrier, NULL, 2);
125 	ASSERT_EQ(pthread_create(&thread, NULL, get_pi_lock, _metadata), 0)
126 		TH_LOG("pthread_create failed");
127 
128 	/* Wait until the other thread calls futex_lock_pi() */
129 	pthread_barrier_wait(&barrier);
130 	pthread_barrier_destroy(&barrier);
131 
132 	/*
133 	 * FUTEX_LOCK_PI with CLOCK_REALTIME
134 	 * Due to historical reasons, FUTEX_LOCK_PI supports only realtime
135 	 * clock, but requires the caller to not set CLOCK_REALTIME flag.
136 	 *
137 	 * If you call FUTEX_LOCK_PI with a monotonic clock, it'll be
138 	 * interpreted as a realtime clock, and (unless you mess your machine's
139 	 * time or your time machine) the monotonic clock value is always
140 	 * smaller than realtime and the syscall will timeout immediately.
141 	 */
142 	GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns);
143 	res = futex_lock_pi(&futex_pi, &to, 0, 0);
144 	TEST_TIMEOUT(res, "futex_lock_pi realtime", ETIMEDOUT);
145 
146 	/* Test operations that don't support FUTEX_CLOCK_REALTIME */
147 	res = futex_lock_pi(&futex_pi, NULL, 0, FUTEX_CLOCK_REALTIME);
148 	TEST_TIMEOUT(res, "futex_lock_pi invalid timeout flag", ENOSYS);
149 }
150 
151 TEST(waitv)
152 {
153 	futex_t f1 = FUTEX_INITIALIZER;
154 	struct futex_waitv waitv = {
155 		.uaddr		= (uintptr_t)&f1,
156 		.val		= f1,
157 		.flags		= FUTEX_32,
158 		.__reserved	= 0,
159 	};
160 	struct timespec to;
161 	int res;
162 
163 	if (!is_futex_waitv_supported())
164 		SKIP(return, "futex_waitv syscall not supported");
165 
166 	/* futex_waitv with CLOCK_MONOTONIC */
167 	GET_ABS_TIMEOUT(CLOCK_MONOTONIC, &to, timeout_ns);
168 	res = futex_waitv(&waitv, 1, 0, &to, CLOCK_MONOTONIC);
169 	TEST_TIMEOUT(res, "futex_waitv monotonic", ETIMEDOUT);
170 
171 	/* futex_waitv with CLOCK_REALTIME */
172 	GET_ABS_TIMEOUT(CLOCK_REALTIME, &to, timeout_ns);
173 	res = futex_waitv(&waitv, 1, 0, &to, CLOCK_REALTIME);
174 	TEST_TIMEOUT(res, "futex_waitv realtime", ETIMEDOUT);
175 }
176 
177 TEST_HARNESS_MAIN
178