void *waiterfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; futex_t old_val; info("Waiter %ld: running\n", args->id); /* Each thread sleeps for a different amount of time * This is to avoid races, because we don't lock the * external mutex here */ usleep(1000 * (long)args->id); old_val = f1; atomic_inc(&waiters_blocked); info("Calling futex_wait_requeue_pi: %p (%u) -> %p\n", &f1, f1, &f2); args->ret = futex_wait_requeue_pi(&f1, old_val, &f2, args->timeout, FUTEX_PRIVATE_FLAG); info("waiter %ld woke with %d %s\n", args->id, args->ret, args->ret < 0 ? strerror(errno) : ""); atomic_inc(&waiters_woken); if (args->ret < 0) { if (args->timeout && errno == ETIMEDOUT) args->ret = 0; else { args->ret = RET_ERROR; error("futex_wait_requeue_pi\n", errno); } futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); info("Waiter %ld: exiting with %d\n", args->id, args->ret); pthread_exit((void *)&args->ret); }
void *broadcast_wakerfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; int nr_requeue = INT_MAX; int task_count = 0; futex_t old_val; int nr_wake = 1; int i = 0; info("Waker: waiting for waiters to block\n"); while (waiters_blocked.val < THREAD_MAX) usleep(1000); usleep(1000); info("Waker: Calling broadcast\n"); if (args->lock) { info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } continue_requeue: old_val = f1; args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, FUTEX_PRIVATE_FLAG); if (args->ret < 0) { args->ret = RET_ERROR; error("FUTEX_CMP_REQUEUE_PI failed\n", errno); } else if (++i < MAX_WAKE_ITERS) { task_count += args->ret; if (task_count < THREAD_MAX - waiters_woken.val) goto continue_requeue; } else { error("max broadcast iterations (%d) reached with %d/%d tasks woken or requeued\n", 0, MAX_WAKE_ITERS, task_count, THREAD_MAX); args->ret = RET_ERROR; } futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); if (args->lock) futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); if (args->ret > 0) args->ret = task_count; info("Waker: exiting with %d\n", args->ret); pthread_exit((void *)&args->ret); }
void *third_party_blocker(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; int ret2 = 0; args->ret = futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); if (args->ret) goto out; args->ret = futex_wait(&wake_complete, wake_complete, NULL, FUTEX_PRIVATE_FLAG); ret2 = futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); out: if (args->ret || ret2) { error("third_party_blocker() futex error", 0); args->ret = RET_ERROR; } pthread_exit((void *)&args->ret); }
void *waiterfn(void *arg) { unsigned int old_val; int res; waiter_ret = RET_PASS; info("Waiter running\n"); info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2); old_val = f1; res = futex_wait_requeue_pi(&f1, old_val, &(f2), NULL, FUTEX_PRIVATE_FLAG); if (!requeued.val || errno != EWOULDBLOCK) { fail("unexpected return from futex_wait_requeue_pi: %d (%s)\n", res, strerror(errno)); info("w2:futex: %x\n", f2); if (!res) futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); waiter_ret = RET_FAIL; } info("Waiter exiting with %d\n", waiter_ret); pthread_exit(NULL); }
int main(int argc, char *argv[]) { unsigned int old_val; struct sigaction sa; pthread_t waiter; int c, res, ret = RET_PASS; while ((c = getopt(argc, argv, "chv:")) != -1) { switch (c) { case 'c': log_color(1); break; case 'h': usage(basename(argv[0])); exit(0); case 'v': log_verbosity(atoi(optarg)); break; default: usage(basename(argv[0])); exit(1); } } ksft_print_header(); ksft_set_plan(1); ksft_print_msg("%s: Test signal handling during requeue_pi\n", basename(argv[0])); ksft_print_msg("\tArguments: <none>\n"); sa.sa_handler = handle_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGUSR1, &sa, NULL)) { error("sigaction\n", errno); exit(1); } info("m1:f2: %x\n", f2); info("Creating waiter\n"); res = create_rt_thread(&waiter, waiterfn, NULL, SCHED_FIFO, 1); if (res) { error("Creating waiting thread failed", res); ret = RET_ERROR; goto out; } info("Calling FUTEX_LOCK_PI on f2=%x @ %p\n", f2, &f2); info("m2:f2: %x\n", f2); futex_lock_pi(&f2, 0, 0, FUTEX_PRIVATE_FLAG); info("m3:f2: %x\n", f2); while (1) { /* * signal the waiter before requeue, waiter should automatically * restart futex_wait_requeue_pi() in the kernel. Wait for the * waiter to block on f1 again. */ info("Issuing SIGUSR1 to waiter\n"); pthread_kill(waiter, SIGUSR1); usleep(DELAY_US); info("Requeueing waiter via FUTEX_CMP_REQUEUE_PI\n"); old_val = f1; res = futex_cmp_requeue_pi(&f1, old_val, &(f2), 1, 0, FUTEX_PRIVATE_FLAG); /* * If res is non-zero, we either requeued the waiter or hit an * error, break out and handle it. If it is zero, then the * signal may have hit before the the waiter was blocked on f1. * Try again. */ if (res > 0) { atomic_set(&requeued, 1); break; } else if (res < 0) { error("FUTEX_CMP_REQUEUE_PI failed\n", errno); ret = RET_ERROR; break; } } info("m4:f2: %x\n", f2); /* * Signal the waiter after requeue, waiter should return from * futex_wait_requeue_pi() with EWOULDBLOCK. Join the thread here so the * futex_unlock_pi() can't happen before the signal wakeup is detected * in the kernel. */ info("Issuing SIGUSR1 to waiter\n"); pthread_kill(waiter, SIGUSR1); info("Waiting for waiter to return\n"); pthread_join(waiter, NULL); info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); info("m5:f2: %x\n", f2); out: if (ret == RET_PASS && waiter_ret) ret = waiter_ret; print_result(TEST_NAME, ret); return ret; }
void *signal_wakerfn(void *arg) { struct thread_arg *args = (struct thread_arg *)arg; unsigned int old_val; int nr_requeue = 0; int task_count = 0; int nr_wake = 1; int i = 0; info("Waker: waiting for waiters to block\n"); while (waiters_blocked.val < THREAD_MAX) usleep(1000); usleep(1000); while (task_count < THREAD_MAX && waiters_woken.val < THREAD_MAX) { info("task_count: %d, waiters_woken: %d\n", task_count, waiters_woken.val); if (args->lock) { info("Calling FUTEX_LOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_lock_pi(&f2, NULL, 0, FUTEX_PRIVATE_FLAG); } info("Waker: Calling signal\n"); /* cond_signal */ old_val = f1; args->ret = futex_cmp_requeue_pi(&f1, old_val, &f2, nr_wake, nr_requeue, FUTEX_PRIVATE_FLAG); if (args->ret < 0) args->ret = -errno; info("futex: %x\n", f2); if (args->lock) { info("Calling FUTEX_UNLOCK_PI on mutex=%x @ %p\n", f2, &f2); futex_unlock_pi(&f2, FUTEX_PRIVATE_FLAG); } info("futex: %x\n", f2); if (args->ret < 0) { error("FUTEX_CMP_REQUEUE_PI failed\n", errno); args->ret = RET_ERROR; break; } task_count += args->ret; usleep(SIGNAL_PERIOD_US); i++; /* we have to loop at least THREAD_MAX times */ if (i > MAX_WAKE_ITERS + THREAD_MAX) { error("max signaling iterations (%d) reached, giving up on pending waiters.\n", 0, MAX_WAKE_ITERS + THREAD_MAX); args->ret = RET_ERROR; break; } } futex_wake(&wake_complete, 1, FUTEX_PRIVATE_FLAG); if (args->ret >= 0) args->ret = task_count; info("Waker: exiting with %d\n", args->ret); info("Waker: waiters_woken: %d\n", waiters_woken.val); pthread_exit((void *)&args->ret); }