]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
nptl: extend test coverage for sched_yield
authorSergey Kolosov <skolosov@redhat.com>
Tue, 28 Jan 2025 22:56:26 +0000 (23:56 +0100)
committerDJ Delorie <dj@redhat.com>
Fri, 7 Mar 2025 22:50:44 +0000 (17:50 -0500)
We add sched_yield() API testing to the existing thread affinity
test case because it allows us to test sched_yield() operation
in the following scenarios:

  * On a main thread.
  * On multiple threads simultaneously.
  * On every CPU the system reports simultaneously.

The ensures we exercise sched_yield() in as many scenarios as
we would exercise calls to the affinity functions.

Additionally, the test is improved by adding a semaphore to coordinate
all the threads running, so that an early starter thread won't consume
cpu resources that could be used to start the other threads.

Co-authored-by: DJ Delorie <dj@redhat.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
sysdeps/unix/sysv/linux/tst-skeleton-affinity.c
sysdeps/unix/sysv/linux/tst-skeleton-thread-affinity.c

index c2bb80d092e4c93496238e61247b251e803a1723..cf97c3f471a229109a3f703540f8b988550f9165 100644 (file)
@@ -38,6 +38,7 @@
 #include <sched.h>
 #include <stdbool.h>
 #include <stdio.h>
+#include <support/test-driver.h>
 
 /* CPU set configuration determined.  Can be used from early_test.  */
 struct conf
@@ -253,12 +254,12 @@ do_test (void)
     if (getaffinity (sizeof (set), &set) < 0 && errno == ENOSYS)
       {
        puts ("warning: getaffinity not supported, test cannot run");
-       return 0;
+       return EXIT_UNSUPPORTED;
       }
     if (sched_getcpu () < 0 && errno == ENOSYS)
       {
        puts ("warning: sched_getcpu not supported, test cannot run");
-       return 0;
+       return EXIT_UNSUPPORTED;
       }
   }
 
index 2519ba4e866d92c741f3bfb10c3ab334854317b5..cf43340ff067302d157b760018703dabc4355115 100644 (file)
@@ -45,10 +45,14 @@ static int still_running;
 /* 0 if no scheduling failures, 1 if failures are encountered.  */
 static int failed;
 
+/* Used to synchronize the threads.  */
+static pthread_barrier_t barrier;
+
 static void *
 thread_burn_one_cpu (void *closure)
 {
   int cpu = (uintptr_t) closure;
+  xpthread_barrier_wait (&barrier);
   while (__atomic_load_n (&still_running, __ATOMIC_RELAXED) == 0)
     {
       int current = sched_getcpu ();
@@ -61,6 +65,11 @@ thread_burn_one_cpu (void *closure)
          __atomic_store_n (&still_running, 1, __ATOMIC_RELAXED);
        }
     }
+  if (sched_yield () != 0)
+    {
+      printf ("error: sched_yield() failed for cpu %d\n", cpu);
+      __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
+    }
   return NULL;
 }
 
@@ -78,6 +87,7 @@ thread_burn_any_cpu (void *closure)
 {
   struct burn_thread *param = closure;
 
+  xpthread_barrier_wait (&barrier);
   /* Schedule this thread around a bit to see if it lands on another
      CPU.  Run this for 2 seconds, once with sched_yield, once
      without.  */
@@ -99,7 +109,11 @@ thread_burn_any_cpu (void *closure)
          CPU_SET_S (cpu, CPU_ALLOC_SIZE (param->conf->set_size),
                     param->seen_set);
          if (pass == 1)
-           sched_yield ();
+           if (sched_yield () != 0)
+             {
+               printf ("error: sched_yield() failed for cpu %d\n", cpu);
+               __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
+             }
        }
     }
   return NULL;
@@ -156,6 +170,7 @@ early_test (struct conf *conf)
     = calloc (conf->last_cpu + 1, sizeof (*other_threads));
   cpu_set_t *initial_set = CPU_ALLOC (conf->set_size);
   cpu_set_t *scratch_set = CPU_ALLOC (conf->set_size);
+  int num_available_cpus = 0;
 
   if (pinned_threads == NULL || other_threads == NULL
       || initial_set == NULL || scratch_set == NULL)
@@ -172,6 +187,7 @@ early_test (struct conf *conf)
     {
       if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set))
        continue;
+      num_available_cpus ++;
       other_threads[cpu].conf = conf;
       other_threads[cpu].initial_set = initial_set;
       other_threads[cpu].thread = cpu;
@@ -194,6 +210,15 @@ early_test (struct conf *conf)
     }
   support_set_small_thread_stack_size (&attr);
 
+  /* This count assumes that all the threads below are created
+     successfully, and call pthread_barrier_wait().  If any threads
+     fail to be created, this function will return FALSE (failure) and
+     the waiting threads will eventually time out the whole test.
+     This is acceptable because we're not testing thread creation and
+     assume all threads will be created, and failure here implies a
+     failure outside the test's scope.  */
+  xpthread_barrier_init (&barrier, NULL, num_available_cpus * 2 + 1);
+
   /* Spawn a thread pinned to each available CPU.  */
   for (int cpu = 0; cpu <= conf->last_cpu; ++cpu)
     {
@@ -245,6 +270,15 @@ early_test (struct conf *conf)
        }
     }
 
+  /* Test that sched_yield() works correctly in the main thread.  This
+     also gives the kernel an opportunity to run the other threads,
+     randomizing thread startup a bit.  */
+  if (sched_yield () != 0)
+    {
+      printf ("error: sched_yield() failed for main thread\n");
+      __atomic_store_n (&failed, 1, __ATOMIC_RELAXED);
+    }
+
   /* Main thread.  */
   struct burn_thread main_thread;
   main_thread.conf = conf;