1/* 2 * sched_tests.c 3 * xnu_quick_test 4 * 5 * Copyright 2011 Apple Inc. All rights reserved. 6 * 7 */ 8 9#include "tests.h" 10#include <mach/mach.h> 11#include <mach/mach_time.h> 12#include <mach/semaphore.h> 13#include <unistd.h> 14#include <err.h> 15#include <sys/param.h> 16#include <pthread.h> 17 18#define DEBUG 0 19 20#if DEBUG 21#define dprintf(...) printf(__VA_ARGS__) 22#else 23#define dprintf(...) do { } while(0) 24#endif 25 26static uint64_t 27nanos_to_abs(uint64_t ns, uint32_t numer, uint32_t denom) 28{ 29 return (uint64_t)(ns * (((double)denom) / ((double)numer))); 30} 31 32static void set_realtime(void) { 33 struct mach_timebase_info mti; 34 thread_time_constraint_policy_data_t pol; 35 kern_return_t kret; 36 37 kret = mach_timebase_info(&mti); 38 if (kret != KERN_SUCCESS) { 39 warnx("Could not get timebase info %d", kret); 40 return; 41 } 42 43 /* 1s 100ms 10ms */ 44 pol.period = nanos_to_abs(1000000000, mti.numer, mti.denom); 45 pol.constraint = nanos_to_abs(100000000, mti.numer, mti.denom); 46 pol.computation = nanos_to_abs(10000000, mti.numer, mti.denom); 47 pol.preemptible = 0; /* Ignored by OS */ 48 49 kret = thread_policy_set(mach_thread_self(), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &pol, THREAD_TIME_CONSTRAINT_POLICY_COUNT); 50 if (kret != KERN_SUCCESS) { 51 warnx("Failed to set realtime %d", kret); 52 } 53} 54 55struct t1_ctx { 56 pthread_t __p; 57 int currentThread; 58 int totalThreads; 59 boolean_t useRealtime; 60 semaphore_t wait_to_start; 61 semaphore_t next_waiter; 62 63 semaphore_t common_sema; /* main thing everyone blocks on */ 64 uint64_t wakeup_time; /* out parameter */ 65}; 66 67void *t1(void *arg) { 68 struct t1_ctx *ctx = (struct t1_ctx *)arg; 69 kern_return_t kret; 70 71 dprintf("thread %d (pthread %p) started\n", ctx->currentThread, pthread_self()); 72 73 /* Wait to allow previous thread to block on common semaphore */ 74 kret = semaphore_wait(ctx->wait_to_start); 75 if (kret != KERN_SUCCESS) { 76 warnx("semaphore_wait(wait_to_start) thread %d failed %d", 77 ctx->currentThread, kret); 78 } 79 80 sleep(1); 81 82 if (ctx->useRealtime) { 83 dprintf("thread %d going realtime\n", ctx->currentThread); 84 set_realtime(); 85 } 86 87 kret = semaphore_signal(ctx->next_waiter); 88 if (kret != KERN_SUCCESS) { 89 warnx("semaphore_signal(next_waiter) thread %d failed %d", 90 ctx->currentThread, kret); 91 } 92 93 /* 94 * We have 1 second to block on the common semaphore before 95 * the next thread does. 96 */ 97 dprintf("thread %d blocking on common semaphore\n", ctx->currentThread); 98 99 kret = semaphore_wait(ctx->common_sema); 100 if (kret != KERN_SUCCESS) { 101 warnx("semaphore_wait(common_sema) thread %d failed %d", 102 ctx->currentThread, kret); 103 } 104 105 /* Save our time for analysis */ 106 ctx->wakeup_time = mach_absolute_time(); 107 dprintf("thread %d woke up at %llu\n", ctx->currentThread, ctx->wakeup_time); 108 109 kret = semaphore_signal(ctx->common_sema); 110 if (kret != KERN_SUCCESS) { 111 warnx("semaphore_signal(common_sema) thread %d failed %d", 112 ctx->currentThread, kret); 113 } 114 115 return NULL; 116} 117 118 119 120 121int sched_tests( void * the_argp ) 122{ 123 kern_return_t kret; 124 int ret; 125 int i; 126 semaphore_t common_sema; 127 semaphore_t all_checked_in; 128 129 struct t1_ctx ctxs[3]; 130 131 /* 132 * Test 8979062. Ensure that a realtime thread that 133 * blocks on a semaphore after a non-realtime thread 134 * gets woken up first. 135 */ 136 137 kret = semaphore_create(mach_task_self(), &common_sema, SYNC_POLICY_FIFO /* not really, in this case */, 0); 138 if (kret != KERN_SUCCESS) { 139 warnx("semaphore_create failed: %d", kret); 140 return -1; 141 } 142 143 kret = semaphore_create(mach_task_self(), &all_checked_in, SYNC_POLICY_FIFO, 0); 144 if (kret != KERN_SUCCESS) { 145 warnx("semaphore_create failed: %d", kret); 146 return -1; 147 } 148 149 memset(&ctxs, 0x00, sizeof(ctxs)); 150 for (i=0; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) { 151 ctxs[i].__p = NULL; /* set later */ 152 ctxs[i].currentThread = i; 153 ctxs[i].totalThreads = sizeof(ctxs)/sizeof(ctxs[0]); 154 ctxs[i].useRealtime = FALSE; 155 156 kret = semaphore_create(mach_task_self(), &ctxs[i].wait_to_start, SYNC_POLICY_FIFO /* not really, in this case */, 0); 157 if (kret != KERN_SUCCESS) { 158 warnx("semaphore_create failed: %d", kret); 159 return -1; 160 } 161 ctxs[i].next_waiter = MACH_PORT_NULL; /* set later */ 162 ctxs[i].common_sema = common_sema; 163 ctxs[i].wakeup_time = 0; 164 } 165 166 ctxs[1].useRealtime = TRUE; 167 168 for (i=1; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) { 169 ctxs[i-1].next_waiter = ctxs[i].wait_to_start; 170 } 171 ctxs[i-1].next_waiter = all_checked_in; 172 173 174 for (i=0; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) { 175 ret = pthread_create(&ctxs[i].__p, NULL, t1, &ctxs[i]); 176 if (ret != 0) { 177 warn("pthread_create failed"); 178 return -1; 179 } 180 } 181 182 /* wake up first thread */ 183 kret = semaphore_signal(ctxs[0].wait_to_start); 184 if (kret != KERN_SUCCESS) { 185 warnx("semaphore_signal(initial wait_to_start) failed %d", kret); 186 return -1; 187 } 188 189 /* Wait for everyone to have blocked */ 190 kret = semaphore_wait(all_checked_in); 191 if (kret != KERN_SUCCESS) { 192 warnx("semaphore_wait(all_checked_in) failed %d", kret); 193 return -1; 194 } 195 196 /* Give some slack for last guy */ 197 sleep(1); 198 199 kret = semaphore_signal(common_sema); 200 if (kret != KERN_SUCCESS) { 201 warnx("semaphore_signal(initial common_sema) failed %d", kret); 202 return -1; 203 } 204 205 for (i=0; i < sizeof(ctxs)/sizeof(ctxs[0]); i++) { 206 ret = pthread_join(ctxs[i].__p, NULL); 207 if (ret != 0) { 208 warn("pthread_join failed"); 209 return -1; 210 } 211 } 212 213 dprintf("All threads joined\n"); 214 215 /* 216 * Our expectation is that thread 1 was realtime and 217 * finished first, followed by 0 and then 2 218 */ 219 if ((ctxs[1].wakeup_time < ctxs[0].wakeup_time) 220 && (ctxs[0].wakeup_time < ctxs[2].wakeup_time)) { 221 /* success */ 222 } else { 223 warnx("Threads woken out of order %llu %llu %llu", 224 ctxs[0].wakeup_time, ctxs[1].wakeup_time, 225 ctxs[2].wakeup_time); 226 return -1; 227 } 228 229 return 0; 230} 231 232