1 /******************************************************************************
3 * Copyright © International Business Machines Corp., 2006-2008
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.
11 * This test excercises the futex syscall op codes needed for requeuing
12 * priority inheritance aware POSIX condition variables and mutexes.
15 * Sripathi Kodi <sripathik@in.ibm.com>
16 * Darren Hart <dvhart@linux.intel.com>
19 * 2008-Jan-13: Initial version by Sripathi Kodi <sripathik@in.ibm.com>
20 * 2009-Nov-6: futex test adaptation by Darren Hart <dvhart@linux.intel.com>
22 *****************************************************************************/
32 #include "futextest.h"
35 #define MAX_WAKE_ITERS 1000
37 #define SIGNAL_PERIOD_US 100
39 atomic_t waiters_blocked
= ATOMIC_INITIALIZER
;
40 atomic_t waiters_woken
= ATOMIC_INITIALIZER
;
42 futex_t f1
= FUTEX_INITIALIZER
;
43 futex_t f2
= FUTEX_INITIALIZER
;
44 futex_t wake_complete
= FUTEX_INITIALIZER
;
46 /* Test option defaults */
47 static long timeout_ns
;
54 struct timespec
*timeout
;
58 #define THREAD_ARG_INITIALIZER { 0, NULL, 0, 0 }
60 void usage(char *prog
)
62 printf("Usage: %s\n", prog
);
63 printf(" -b Broadcast wakeup (all waiters)\n");
64 printf(" -c Use color\n");
65 printf(" -h Display this help message\n");
66 printf(" -l Lock the pi futex across requeue\n");
67 printf(" -o Use a third party pi futex owner during requeue (cancels -l)\n");
68 printf(" -t N Timeout in nanoseconds (default: 0)\n");
69 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
70 VQUIET
, VCRITICAL
, VINFO
);
73 int create_rt_thread(pthread_t
*pth
, void*(*func
)(void *), void *arg
,
77 struct sched_param schedp
;
80 pthread_attr_init(&attr
);
81 memset(&schedp
, 0, sizeof(schedp
));
83 ret
= pthread_attr_setinheritsched(&attr
, PTHREAD_EXPLICIT_SCHED
);
85 error("pthread_attr_setinheritsched\n", ret
);
89 ret
= pthread_attr_setschedpolicy(&attr
, policy
);
91 error("pthread_attr_setschedpolicy\n", ret
);
95 schedp
.sched_priority
= prio
;
96 ret
= pthread_attr_setschedparam(&attr
, &schedp
);
98 error("pthread_attr_setschedparam\n", ret
);
102 ret
= pthread_create(pth
, &attr
, func
, arg
);
104 error("pthread_create\n", ret
);
111 void *waiterfn(void *arg
)
113 struct thread_arg
*args
= (struct thread_arg
*)arg
;
116 info("Waiter %ld: running\n", args
->id
);
117 /* Each thread sleeps for a different amount of time
118 * This is to avoid races, because we don't lock the
119 * external mutex here */
120 usleep(1000 * (long)args
->id
);
123 atomic_inc(&waiters_blocked
);
124 info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n",
126 args
->ret
= futex_wait_requeue_pi(&f1
, old_val
, &f2
, args
->timeout
,
129 info("waiter %ld woke with %d %s\n", args
->id
, args
->ret
,
130 args
->ret
< 0 ? strerror(errno
) : "");
131 atomic_inc(&waiters_woken
);
133 if (args
->timeout
&& errno
== ETIMEDOUT
)
136 args
->ret
= RET_ERROR
;
137 error("futex_wait_requeue_pi\n", errno
);
139 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
141 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
143 info("Waiter %ld: exiting with %d\n", args
->id
, args
->ret
);
144 pthread_exit((void *)&args
->ret
);
147 void *broadcast_wakerfn(void *arg
)
149 struct thread_arg
*args
= (struct thread_arg
*)arg
;
150 int nr_requeue
= INT_MAX
;
156 info("Waker: waiting for waiters to block\n");
157 while (waiters_blocked
.val
< THREAD_MAX
)
161 info("Waker: Calling broadcast\n");
163 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2
, &f2
);
164 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
168 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
, nr_wake
, nr_requeue
,
171 args
->ret
= RET_ERROR
;
172 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
173 } else if (++i
< MAX_WAKE_ITERS
) {
174 task_count
+= args
->ret
;
175 if (task_count
< THREAD_MAX
- waiters_woken
.val
)
176 goto continue_requeue
;
178 error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n",
179 0, MAX_WAKE_ITERS
, task_count
, THREAD_MAX
);
180 args
->ret
= RET_ERROR
;
183 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
186 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
189 args
->ret
= task_count
;
191 info("Waker: exiting with %d\n", args
->ret
);
192 pthread_exit((void *)&args
->ret
);
195 void *signal_wakerfn(void *arg
)
197 struct thread_arg
*args
= (struct thread_arg
*)arg
;
198 unsigned int old_val
;
204 info("Waker: waiting for waiters to block\n");
205 while (waiters_blocked
.val
< THREAD_MAX
)
209 while (task_count
< THREAD_MAX
&& waiters_woken
.val
< THREAD_MAX
) {
210 info("task_count: %d, waiters_woken: %d\n",
211 task_count
, waiters_woken
.val
);
213 info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n",
215 futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
217 info("Waker: Calling signal\n");
220 args
->ret
= futex_cmp_requeue_pi(&f1
, old_val
, &f2
,
225 info("futex: %x\n", f2
);
227 info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n",
229 futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
231 info("futex: %x\n", f2
);
233 error("FUTEX_CMP_REQUEUE_PI failed\n", errno
);
234 args
->ret
= RET_ERROR
;
238 task_count
+= args
->ret
;
239 usleep(SIGNAL_PERIOD_US
);
241 /* we have to loop at least THREAD_MAX times */
242 if (i
> MAX_WAKE_ITERS
+ THREAD_MAX
) {
243 error("max signaling iterations (%d) reached, giving up on pending waiters.\n",
244 0, MAX_WAKE_ITERS
+ THREAD_MAX
);
245 args
->ret
= RET_ERROR
;
250 futex_wake(&wake_complete
, 1, FUTEX_PRIVATE_FLAG
);
253 args
->ret
= task_count
;
255 info("Waker: exiting with %d\n", args
->ret
);
256 info("Waker: waiters_woken: %d\n", waiters_woken
.val
);
257 pthread_exit((void *)&args
->ret
);
260 void *third_party_blocker(void *arg
)
262 struct thread_arg
*args
= (struct thread_arg
*)arg
;
265 args
->ret
= futex_lock_pi(&f2
, NULL
, 0, FUTEX_PRIVATE_FLAG
);
268 args
->ret
= futex_wait(&wake_complete
, wake_complete
, NULL
,
270 ret2
= futex_unlock_pi(&f2
, FUTEX_PRIVATE_FLAG
);
273 if (args
->ret
|| ret2
) {
274 error("third_party_blocker() futex error", 0);
275 args
->ret
= RET_ERROR
;
278 pthread_exit((void *)&args
->ret
);
281 int unit_test(int broadcast
, long lock
, int third_party_owner
, long timeout_ns
)
283 void *(*wakerfn
)(void *) = signal_wakerfn
;
284 struct thread_arg blocker_arg
= THREAD_ARG_INITIALIZER
;
285 struct thread_arg waker_arg
= THREAD_ARG_INITIALIZER
;
286 pthread_t waiter
[THREAD_MAX
], waker
, blocker
;
287 struct timespec ts
, *tsp
= NULL
;
288 struct thread_arg args
[THREAD_MAX
];
290 int i
, ret
= RET_PASS
;
295 info("timeout_ns = %ld\n", timeout_ns
);
296 ret
= clock_gettime(CLOCK_MONOTONIC
, &ts
);
297 secs
= (ts
.tv_nsec
+ timeout_ns
) / 1000000000;
298 ts
.tv_nsec
= ((int64_t)ts
.tv_nsec
+ timeout_ns
) % 1000000000;
300 info("ts.tv_sec = %ld\n", ts
.tv_sec
);
301 info("ts.tv_nsec = %ld\n", ts
.tv_nsec
);
306 wakerfn
= broadcast_wakerfn
;
308 if (third_party_owner
) {
309 if (create_rt_thread(&blocker
, third_party_blocker
,
310 (void *)&blocker_arg
, SCHED_FIFO
, 1)) {
311 error("Creating third party blocker thread failed\n",
318 atomic_set(&waiters_woken
, 0);
319 for (i
= 0; i
< THREAD_MAX
; i
++) {
321 args
[i
].timeout
= tsp
;
322 info("Starting thread %d\n", i
);
323 if (create_rt_thread(&waiter
[i
], waiterfn
, (void *)&args
[i
],
325 error("Creating waiting thread failed\n", errno
);
330 waker_arg
.lock
= lock
;
331 if (create_rt_thread(&waker
, wakerfn
, (void *)&waker_arg
,
333 error("Creating waker thread failed\n", errno
);
338 /* Wait for threads to finish */
339 /* Store the first error or failure encountered in waiter_ret */
340 waiter_ret
= &args
[0].ret
;
341 for (i
= 0; i
< THREAD_MAX
; i
++)
342 pthread_join(waiter
[i
],
343 *waiter_ret
? NULL
: (void **)&waiter_ret
);
345 if (third_party_owner
)
346 pthread_join(blocker
, NULL
);
347 pthread_join(waker
, NULL
);
353 else if (waker_arg
.ret
< 0)
355 else if (blocker_arg
.ret
)
356 ret
= blocker_arg
.ret
;
362 int main(int argc
, char *argv
[])
366 while ((c
= getopt(argc
, argv
, "bchlot:v:")) != -1) {
375 usage(basename(argv
[0]));
385 timeout_ns
= atoi(optarg
);
388 log_verbosity(atoi(optarg
));
391 usage(basename(argv
[0]));
396 printf("%s: Test requeue functionality\n", basename(argv
[0]));
397 printf("\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n",
398 broadcast
, locked
, owner
, timeout_ns
);
401 * FIXME: unit_test is obsolete now that we parse options and the
402 * various style of runs are done by run.sh - simplify the code and move
403 * unit_test into main()
405 ret
= unit_test(broadcast
, locked
, owner
, timeout_ns
);