#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;
}
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;
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;
uint64_t wakeup_time;
};
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());
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);
}
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);
}
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];
kret = semaphore_create(mach_task_self(), &common_sema, SYNC_POLICY_FIFO , 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;
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 , 0);
if (kret != KERN_SUCCESS) {
warnx("semaphore_create failed: %d", kret);
return -1;
}
ctxs[i].next_waiter = MACH_PORT_NULL;
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;
}
}
kret = semaphore_signal(ctxs[0].wait_to_start);
if (kret != KERN_SUCCESS) {
warnx("semaphore_signal(initial wait_to_start) failed %d", kret);
return -1;
}
kret = semaphore_wait(all_checked_in);
if (kret != KERN_SUCCESS) {
warnx("semaphore_wait(all_checked_in) failed %d", kret);
return -1;
}
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");
if ((ctxs[1].wakeup_time < ctxs[0].wakeup_time)
&& (ctxs[0].wakeup_time < ctxs[2].wakeup_time)) {
} 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;
}