X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/060df5ea7c632b1ac8cc8aac1fb59758165c2084..6d2010ae8f7a6078e10b361c6962983bab233e0f:/tools/tests/xnu_quick_test/sched_tests.c?ds=inline diff --git a/tools/tests/xnu_quick_test/sched_tests.c b/tools/tests/xnu_quick_test/sched_tests.c new file mode 100644 index 000000000..6dd23bf68 --- /dev/null +++ b/tools/tests/xnu_quick_test/sched_tests.c @@ -0,0 +1,231 @@ +/* + * sched_tests.c + * xnu_quick_test + * + * Copyright 2011 Apple Inc. All rights reserved. + * + */ + +#include "tests.h" +#include +#include +#include +#include +#include +#include +#include + +#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; +} +