This test item has extremely high requirements for timing and can only
pass the test under specific conditions. The following situations will
lead to test failure:

    MainThread                  Thread1
        │
  pthread_create-------------------┐
        │                          │
 futex_cmp_requeue                 │
        │                     futex_wait
        │                          │

If the child thread is not waiting in the futex_wait function when the
main thread reaches the futex_cmp_requeue function, the test will fail.

This patch avoids this problem by checking whether the child thread is
in a sleeping state in the main thread.

Fixes: 7cb5dd8e2c8c ("selftests: futex: Add futex compare requeue test")
Signed-off-by: Yuwen Chen <[email protected]>
Co-developed-by: Edward Liaw <[email protected]>
Signed-off-by: Edward Liaw <[email protected]>
---
v1->v2:
    1. Fix the issue of abnormal use of fscanf in the get_thread_state function
    2. Add timeout logic

v2->v3: 
https://lore.kernel.org/all/[email protected]/
    1. Use /proc/[pid]/wchan instead of /proc/[pid]/stat to check if a process 
has entered the sleep state
    2. Refactor part of the logic to facilitate code reuse.

 .../futex/functional/futex_requeue.c          | 91 ++++++++++++++++---
 1 file changed, 77 insertions(+), 14 deletions(-)

diff --git a/tools/testing/selftests/futex/functional/futex_requeue.c 
b/tools/testing/selftests/futex/functional/futex_requeue.c
index dcf0d5f2f3122..7163e827691e4 100644
--- a/tools/testing/selftests/futex/functional/futex_requeue.c
+++ b/tools/testing/selftests/futex/functional/futex_requeue.c
@@ -11,8 +11,9 @@
 #include "futextest.h"
 #include "kselftest_harness.h"
 
-#define timeout_ns  30000000
-#define WAKE_WAIT_US 10000
+#define FUTEX_WAIT_TIMEOUT_S   3 /* 3s */
+#define WAIT_THREAD_RETRIES    100
+#define WAIT_THREAD_DELAY_US   (10000 * 100) /* 1s */
 
 volatile futex_t *f1;
 
@@ -20,8 +21,8 @@ void *waiterfn(void *arg)
 {
        struct timespec to;
 
-       to.tv_sec = 0;
-       to.tv_nsec = timeout_ns;
+       to.tv_sec = FUTEX_WAIT_TIMEOUT_S;
+       to.tv_nsec = 0;
 
        if (futex_wait(f1, *f1, &to, 0))
                printf("waiter failed errno %d\n", errno);
@@ -29,20 +30,83 @@ void *waiterfn(void *arg)
        return NULL;
 }
 
+struct futex_thread {
+       pthread_t thread;
+       pthread_barrier_t barrier;
+       pid_t tid;
+       void *(*threadfn)(void *);
+       void *arg;
+};
+
+static int wait_for_thread(FILE *fp, int timeout_us)
+{
+       char buf[80] = "";
+
+       for (int i = 0; i < WAIT_THREAD_RETRIES; i++) {
+               if (!fgets(buf, sizeof(buf), fp))
+                       return -EIO;
+               if (!strncmp(buf, "futex", 5))
+                       return 0;
+               usleep(timeout_us / WAIT_THREAD_RETRIES);
+               rewind(fp);
+       }
+       return -ETIMEDOUT;
+}
+
+int futex_wait_for_thread(struct futex_thread *t, int timeout_us)
+{
+       char fname[80];
+       FILE *fp;
+       int res;
+
+       snprintf(fname, sizeof(fname), "/proc/%d/wchan", t->tid);
+       fp = fopen(fname, "r");
+       if (!fp)
+               return -EIO;
+       res = wait_for_thread(fp, timeout_us);
+       fclose(fp);
+       return res;
+}
+
+static void *futex_thread_fn(void *arg)
+{
+       struct futex_thread *t = arg;
+
+       t->tid = gettid();
+       pthread_barrier_wait(&t->barrier);
+       return t->threadfn(t->arg);
+}
+
+int futex_thread_create(struct futex_thread *t, void *(*threadfn)(void *), 
void *arg)
+{
+       int ret;
+
+       pthread_barrier_init(&t->barrier, NULL, 2);
+       t->tid = 0;
+       t->threadfn = threadfn;
+       t->arg = arg;
+
+       ret = pthread_create(&t->thread, NULL, futex_thread_fn, t);
+       if (ret)
+               return ret;
+
+       pthread_barrier_wait(&t->barrier);
+       return 0;
+}
+
 TEST(requeue_single)
 {
        volatile futex_t _f1 = 0;
        volatile futex_t f2 = 0;
-       pthread_t waiter[10];
+       struct futex_thread waiter;
 
        f1 = &_f1;
 
        /*
         * Requeue a waiter from f1 to f2, and wake f2.
         */
-       ASSERT_EQ(0, pthread_create(&waiter[0], NULL, waiterfn, NULL));
-
-       usleep(WAKE_WAIT_US);
+       EXPECT_EQ(0, futex_thread_create(&waiter, waiterfn, NULL));
+       futex_wait_for_thread(&waiter, WAIT_THREAD_DELAY_US);
 
        EXPECT_EQ(1, futex_cmp_requeue(f1, 0, &f2, 0, 1, 0));
        EXPECT_EQ(1, futex_wake(&f2, 1, 0));
@@ -52,8 +116,7 @@ TEST(requeue_multiple)
 {
        volatile futex_t _f1 = 0;
        volatile futex_t f2 = 0;
-       pthread_t waiter[10];
-       int i;
+       struct futex_thread waiter[10];
 
        f1 = &_f1;
 
@@ -61,10 +124,10 @@ TEST(requeue_multiple)
         * Create 10 waiters at f1. At futex_requeue, wake 3 and requeue 7.
         * At futex_wake, wake INT_MAX (should be exactly 7).
         */
-       for (i = 0; i < 10; i++)
-               ASSERT_EQ(0, pthread_create(&waiter[i], NULL, waiterfn, NULL));
-
-       usleep(WAKE_WAIT_US);
+       for (int i = 0; i < 10; i++) {
+               EXPECT_EQ(0, futex_thread_create(&waiter[i], waiterfn, NULL));
+               futex_wait_for_thread(&waiter[i], WAIT_THREAD_DELAY_US / 10);
+       }
 
        EXPECT_EQ(10, futex_cmp_requeue(f1, 0, &f2, 3, 7, 0));
        EXPECT_EQ(7, futex_wake(&f2, INT_MAX, 0));
-- 
2.34.1


Reply via email to