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