Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | // SPDX-License-Identifier: GPL-2.0-or-later /****************************************************************************** * * Copyright © International Business Machines Corp., 2009 * * DESCRIPTION * Block on a futex and wait for timeout. * * AUTHOR * Darren Hart <dvhart@linux.intel.com> * * HISTORY * 2009-Nov-6: Initial version by Darren Hart <dvhart@linux.intel.com> * 2021-Apr-26: More test cases by André Almeida <andrealmeid@collabora.com> * *****************************************************************************/ #include <pthread.h> #include "futextest.h" #include "logging.h" #define TEST_NAME "futex-wait-timeout" static long timeout_ns = 100000; /* 100us default timeout */ static futex_t futex_pi; static pthread_barrier_t barrier; void usage(char *prog) { printf("Usage: %s\n", prog); printf(" -c Use color\n"); printf(" -h Display this help message\n"); printf(" -t N Timeout in nanoseconds (default: 100,000)\n"); printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n", VQUIET, VCRITICAL, VINFO); } /* * Get a PI lock and hold it forever, so the main thread lock_pi will block * and we can test the timeout */ void *get_pi_lock(void *arg) { int ret; volatile futex_t lock = 0; ret = futex_lock_pi(&futex_pi, NULL, 0, 0); if (ret != 0) error("futex_lock_pi failed\n", ret); pthread_barrier_wait(&barrier); /* Blocks forever */ ret = futex_wait(&lock, 0, NULL, 0); error("futex_wait failed\n", ret); return NULL; } /* * Check if the function returned the expected error */ static void test_timeout(int res, int *ret, char *test_name, int err) { if (!res || errno != err) { ksft_test_result_fail("%s returned %d\n", test_name, res < 0 ? errno : res); *ret = RET_FAIL; } else { ksft_test_result_pass("%s succeeds\n", test_name); } } /* * Calculate absolute timeout and correct overflow */ static int futex_get_abs_timeout(clockid_t clockid, struct timespec *to, long timeout_ns) { if (clock_gettime(clockid, to)) { error("clock_gettime failed\n", errno); return errno; } to->tv_nsec += timeout_ns; if (to->tv_nsec >= 1000000000) { to->tv_sec++; to->tv_nsec -= 1000000000; } return 0; } int main(int argc, char *argv[]) { futex_t f1 = FUTEX_INITIALIZER; int res, ret = RET_PASS; struct timespec to; pthread_t thread; int c; while ((c = getopt(argc, argv, "cht:v:")) != -1) { switch (c) { case 'c': log_color(1); break; case 'h': usage(basename(argv[0])); exit(0); case 't': timeout_ns = atoi(optarg); break; case 'v': log_verbosity(atoi(optarg)); break; default: usage(basename(argv[0])); exit(1); } } ksft_print_header(); ksft_set_plan(7); ksft_print_msg("%s: Block on a futex and wait for timeout\n", basename(argv[0])); ksft_print_msg("\tArguments: timeout=%ldns\n", timeout_ns); pthread_barrier_init(&barrier, NULL, 2); pthread_create(&thread, NULL, get_pi_lock, NULL); /* initialize relative timeout */ to.tv_sec = 0; to.tv_nsec = timeout_ns; res = futex_wait(&f1, f1, &to, 0); test_timeout(res, &ret, "futex_wait relative", ETIMEDOUT); /* FUTEX_WAIT_BITSET with CLOCK_REALTIME */ if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) return RET_FAIL; res = futex_wait_bitset(&f1, f1, &to, 1, FUTEX_CLOCK_REALTIME); test_timeout(res, &ret, "futex_wait_bitset realtime", ETIMEDOUT); /* FUTEX_WAIT_BITSET with CLOCK_MONOTONIC */ if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) return RET_FAIL; res = futex_wait_bitset(&f1, f1, &to, 1, 0); test_timeout(res, &ret, "futex_wait_bitset monotonic", ETIMEDOUT); /* FUTEX_WAIT_REQUEUE_PI with CLOCK_REALTIME */ if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) return RET_FAIL; res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, FUTEX_CLOCK_REALTIME); test_timeout(res, &ret, "futex_wait_requeue_pi realtime", ETIMEDOUT); /* FUTEX_WAIT_REQUEUE_PI with CLOCK_MONOTONIC */ if (futex_get_abs_timeout(CLOCK_MONOTONIC, &to, timeout_ns)) return RET_FAIL; res = futex_wait_requeue_pi(&f1, f1, &futex_pi, &to, 0); test_timeout(res, &ret, "futex_wait_requeue_pi monotonic", ETIMEDOUT); /* Wait until the other thread calls futex_lock_pi() */ pthread_barrier_wait(&barrier); pthread_barrier_destroy(&barrier); /* * FUTEX_LOCK_PI with CLOCK_REALTIME * Due to historical reasons, FUTEX_LOCK_PI supports only realtime * clock, but requires the caller to not set CLOCK_REALTIME flag. * * If you call FUTEX_LOCK_PI with a monotonic clock, it'll be * interpreted as a realtime clock, and (unless you mess your machine's * time or your time machine) the monotonic clock value is always * smaller than realtime and the syscall will timeout immediately. */ if (futex_get_abs_timeout(CLOCK_REALTIME, &to, timeout_ns)) return RET_FAIL; res = futex_lock_pi(&futex_pi, &to, 0, 0); test_timeout(res, &ret, "futex_lock_pi realtime", ETIMEDOUT); /* Test operations that don't support FUTEX_CLOCK_REALTIME */ res = futex_lock_pi(&futex_pi, NULL, 0, FUTEX_CLOCK_REALTIME); test_timeout(res, &ret, "futex_lock_pi invalid timeout flag", ENOSYS); ksft_print_cnts(); return ret; } |