]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/tests/xnu_quick_test/sched_tests.c
xnu-1699.22.73.tar.gz
[apple/xnu.git] / tools / tests / xnu_quick_test / sched_tests.c
diff --git a/tools/tests/xnu_quick_test/sched_tests.c b/tools/tests/xnu_quick_test/sched_tests.c
new file mode 100644 (file)
index 0000000..6dd23bf
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  sched_tests.c
+ *  xnu_quick_test
+ *
+ *  Copyright 2011 Apple Inc. All rights reserved.
+ *
+ */
+
+#include "tests.h"
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach/semaphore.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/param.h>
+#include <pthread.h>
+
+#define DEBUG 0
+
+#if DEBUG
+#define dprintf(...) printf(__VA_ARGS__)
+#else
+#define dprintf(...) do { } while(0)
+#endif
+
+static uint64_t
+nanos_to_abs(uint64_t ns, uint32_t numer, uint32_t denom)
+{
+       return (uint64_t)(ns * (((double)denom) / ((double)numer)));
+}
+
+static void set_realtime(void) {
+       struct mach_timebase_info mti;
+       thread_time_constraint_policy_data_t pol;
+       kern_return_t kret;
+
+       kret = mach_timebase_info(&mti);
+       if (kret != KERN_SUCCESS) {
+               warnx("Could not get timebase info %d", kret);
+               return;
+       }
+
+       /* 1s 100ms 10ms */
+       pol.period      = nanos_to_abs(1000000000, mti.numer, mti.denom);
+       pol.constraint  = nanos_to_abs(100000000,  mti.numer, mti.denom);
+       pol.computation = nanos_to_abs(10000000,   mti.numer, mti.denom);
+       pol.preemptible = 0; /* Ignored by OS */
+
+       kret = thread_policy_set(mach_thread_self(), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &pol, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+       if (kret != KERN_SUCCESS) {
+               warnx("Failed to set realtime %d", kret);
+       }
+}
+
+struct t1_ctx {
+       pthread_t __p;
+       int currentThread;
+       int totalThreads;
+       boolean_t useRealtime;
+       semaphore_t wait_to_start;
+       semaphore_t next_waiter;
+
+       semaphore_t common_sema; /* main thing everyone blocks on */
+       uint64_t wakeup_time; /* out parameter */
+};
+
+void *t1(void *arg) {
+       struct t1_ctx *ctx = (struct t1_ctx *)arg;
+       kern_return_t kret;
+
+       dprintf("thread %d (pthread %p) started\n", ctx->currentThread, pthread_self());
+
+       /* Wait to allow previous thread to block on common semaphore */
+       kret = semaphore_wait(ctx->wait_to_start);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_wait(wait_to_start) thread %d failed %d",
+                         ctx->currentThread, kret);
+       }
+
+       sleep(1);
+
+       if (ctx->useRealtime) {
+               dprintf("thread %d going realtime\n", ctx->currentThread);
+               set_realtime();
+       }
+
+       kret = semaphore_signal(ctx->next_waiter);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_signal(next_waiter) thread %d failed %d",
+                         ctx->currentThread, kret);
+       }
+
+       /*
+        * We have 1 second to block on the common semaphore before
+        * the next thread does.
+        */
+       dprintf("thread %d blocking on common semaphore\n", ctx->currentThread);
+
+       kret = semaphore_wait(ctx->common_sema);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_wait(common_sema) thread %d failed %d",
+                         ctx->currentThread, kret);
+       }
+
+       /* Save our time for analysis */
+       ctx->wakeup_time = mach_absolute_time();
+       dprintf("thread %d woke up at %llu\n", ctx->currentThread, ctx->wakeup_time);
+
+       kret = semaphore_signal(ctx->common_sema);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_signal(common_sema) thread %d failed %d",
+                         ctx->currentThread, kret);
+       }
+
+       return NULL;
+}
+          
+
+
+
+int sched_tests( void * the_argp )
+{
+       kern_return_t kret;
+       int ret;
+       int i;
+       semaphore_t common_sema;
+       semaphore_t all_checked_in;
+       
+       struct t1_ctx ctxs[3];
+       
+       /*
+        * Test 8979062. Ensure that a realtime thread that
+        * blocks on a semaphore after a non-realtime thread
+        * gets woken up first.
+        */
+
+       kret = semaphore_create(mach_task_self(), &common_sema, SYNC_POLICY_FIFO /* not really, in this case */, 0);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_create failed: %d", kret);
+               return -1;
+       }
+
+       kret = semaphore_create(mach_task_self(), &all_checked_in, SYNC_POLICY_FIFO, 0);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_create failed: %d", kret);
+               return -1;
+       }
+
+       memset(&ctxs, 0x00, sizeof(ctxs));
+       for (i=0; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) {
+               ctxs[i].__p = NULL; /* set later */
+               ctxs[i].currentThread = i;
+               ctxs[i].totalThreads = sizeof(ctxs)/sizeof(ctxs[0]);
+               ctxs[i].useRealtime = FALSE;
+
+               kret = semaphore_create(mach_task_self(), &ctxs[i].wait_to_start, SYNC_POLICY_FIFO /* not really, in this case */, 0);
+               if (kret != KERN_SUCCESS) {
+                       warnx("semaphore_create failed: %d", kret);
+                       return -1;
+               }
+               ctxs[i].next_waiter = MACH_PORT_NULL; /* set later */
+               ctxs[i].common_sema = common_sema;
+               ctxs[i].wakeup_time = 0;
+       }
+
+       ctxs[1].useRealtime = TRUE;
+
+       for (i=1; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) {
+               ctxs[i-1].next_waiter = ctxs[i].wait_to_start;
+       }
+       ctxs[i-1].next_waiter = all_checked_in;
+
+
+       for (i=0; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) {
+               ret = pthread_create(&ctxs[i].__p, NULL, t1, &ctxs[i]);
+               if (ret != 0) {
+                       warn("pthread_create failed");
+                       return -1;
+               }
+       }
+
+       /* wake up first thread */
+       kret = semaphore_signal(ctxs[0].wait_to_start);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_signal(initial wait_to_start) failed %d", kret);
+               return -1;
+       }
+
+       /* Wait for everyone to have blocked */
+       kret = semaphore_wait(all_checked_in);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_wait(all_checked_in) failed %d", kret);
+               return -1;
+       }
+
+       /* Give some slack for last guy */
+       sleep(1);
+
+       kret = semaphore_signal(common_sema);
+       if (kret != KERN_SUCCESS) {
+               warnx("semaphore_signal(initial common_sema) failed %d", kret);
+               return -1;
+       }
+
+       for (i=0; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) {
+               ret = pthread_join(ctxs[i].__p, NULL);
+               if (ret != 0) {
+                       warn("pthread_join failed");
+                       return -1;
+               }
+       }
+
+       dprintf("All threads joined\n");
+
+       /*
+        * Our expectation is that thread 1 was realtime and
+        * finished first, followed by 0 and then 2
+        */
+       if ((ctxs[1].wakeup_time < ctxs[0].wakeup_time)
+               && (ctxs[0].wakeup_time < ctxs[2].wakeup_time)) {
+               /* success */
+       } else {
+               warnx("Threads woken out of order %llu %llu %llu",
+                         ctxs[0].wakeup_time, ctxs[1].wakeup_time,
+                         ctxs[2].wakeup_time);
+               return -1;
+       }
+
+       return 0;
+}
+