/* * 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; }