kse_threads_test.c revision 103973
1/*-
2 * Copyright (c) 2002 Jonathan Mini (mini@freebsd.org).
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/tools/KSE/ksetest/kse_threads_test.c 103973 2002-09-25 18:14:38Z archie $
27 */
28
29#include <sys/types.h>
30#include <sys/signal.h>
31#include <sys/signalvar.h>
32#include <sys/sysctl.h>
33#include <sys/kse.h>
34#include <sys/ucontext.h>
35
36#include <stdarg.h>
37#include <stddef.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sysexits.h>
41#include <time.h>
42#include <unistd.h>
43#include "simplelock.h"
44
45#undef TRACE_UTS
46//#define TRACE_KSE
47
48#ifdef TRACE_UTS
49#define	UPFMT(fmt...)	pfmt(#fmt)
50#define	UPSTR(s)	pstr(s)
51#define	UPCHAR(c)	pchar(c)
52#else
53#define	UPFMT(fmt...)	/* Nothing. */
54#define	UPSTR(s)	/* Nothing. */
55#define	UPCHAR(c)	/* Nothing. */
56#endif
57
58#define MAIN_STACK_SIZE			(1024 * 1024)
59#define THREAD_STACK_SIZE		(32 * 1024)
60
61struct uts_runq {
62	struct kse_thr_mailbox	*head;
63	struct simplelock	lock;
64};
65
66struct uts_data {
67	struct kse_mailbox	mb;
68	struct uts_runq		*runq;
69	struct kse_thr_mailbox	*cur_thread;
70};
71
72static struct uts_runq runq1;
73static struct uts_data data1, data2;
74static struct uts_runq runq2;
75static struct uts_data data3, data4;
76static struct kse_thr_mailbox	*aa;
77
78static int progress = 0;
79
80static void	init_uts(struct uts_data *data, struct uts_runq *q);
81static void	start_uts(struct uts_data *data, int newgrp);
82static void	enter_uts(struct uts_data *);
83static void	pchar(char c);
84static void	pfmt(const char *fmt, ...);
85static void	pstr(const char *s);
86static void	runq_init(struct uts_runq *q);
87static void	runq_insert(struct uts_runq *q, struct kse_thr_mailbox *tm);
88static struct kse_thr_mailbox *runq_remove(struct uts_runq *q);
89static struct kse_thr_mailbox *runq_remove_nolock(struct uts_runq *q);
90static void	thread_start(struct uts_data *data, const void *func, int arg);
91static void	uts(struct kse_mailbox *km);
92
93extern int uts_to_thread(struct kse_thr_mailbox *tdp, struct kse_thr_mailbox **curthreadp);
94
95static void
96nano(int len)
97{
98	struct timespec time_to_sleep;
99	struct timespec time_remaining;
100
101	time_to_sleep.tv_sec = 0;
102	time_to_sleep.tv_nsec = len * 10000;
103	nanosleep(&time_to_sleep, &time_remaining);
104}
105
106void
107aaaa(int c)
108{
109	for (;;) {
110		pchar(c);
111		nano(1);
112	}
113}
114
115static void
116foof(int sig)
117{
118	pfmt("\n[%d]\n", sig);
119//	thread_start(aaaa, '0' + progress++);
120}
121
122static void
123newkse(int v)
124{
125	start_uts(&data4, 0);
126}
127
128#if 0
129void
130spin(int arg)
131{
132	for (;;) enter_uts(); sched_yield();
133}
134#endif
135/*
136 * Test Userland Thread Scheduler (UTS) suite for KSE.
137 */
138int
139main(void)
140{
141	int i;
142
143	runq_init(&runq1);
144	init_uts(&data1, &runq1);
145	init_uts(&data2, &runq1);
146	thread_start(&data1, aaaa, '+');
147	thread_start(&data1, aaaa, '-');
148	start_uts(&data1, 0);
149	start_uts(&data2, 0);
150
151//	start second ksegrp
152	runq_init(&runq2);
153	init_uts(&data3, &runq2);
154	init_uts(&data4, &runq2);
155	thread_start(&data3, newkse, 0);
156	thread_start(&data3, aaaa, '*');
157	thread_start(&data3, aaaa, '.');
158	start_uts(&data3, 1);
159
160	for (i = 0;1;i++) {
161//		if (i < 1000)
162//			thread_start(aaaa, 'a' + (i % 26));
163		pchar('A' + (i % 26));
164		nano(5);
165	}
166	pstr("\n** main() exiting **\n");
167	return (EX_OK);
168}
169
170
171/*
172 * Enter the UTS from a thread.
173 */
174static void
175enter_uts(struct uts_data *data)
176{
177	struct kse_thr_mailbox	*td;
178
179	/* XXX: We should atomically exchange these two. */
180	td = data->mb.km_curthread;
181	data->mb.km_curthread = NULL;
182
183	thread_to_uts(td, &data->mb);
184}
185
186/*
187 * Initialise threading.
188 */
189static void
190init_uts(struct uts_data *data, struct uts_runq *q)
191{
192	struct kse_thr_mailbox *tm;
193	int mib[2];
194	char	*p;
195	size_t len;
196
197	/*
198	 * Create initial thread.
199	 */
200	tm = (struct kse_thr_mailbox *)calloc(1, sizeof(struct kse_thr_mailbox));
201
202	/* Throw us into its context. */
203	getcontext(&tm->tm_context);
204
205	/* Find our stack. */
206	mib[0] = CTL_KERN;
207	mib[1] = KERN_USRSTACK;
208#if 0
209	len = sizeof(p);
210	if (sysctl(mib, 2, &p, &len, NULL, 0) == -1)
211		pstr("sysctl(CTL_KER.KERN_USRSTACK) failed.\n");
212#endif
213	p = (char *)malloc(MAIN_STACK_SIZE) + MAIN_STACK_SIZE;
214	pfmt("main() : 0x%x\n", tm);
215	pfmt("eip -> 0x%x\n", tm->tm_context.uc_mcontext.mc_eip);
216	tm->tm_context.uc_stack.ss_sp = p - MAIN_STACK_SIZE;
217	tm->tm_context.uc_stack.ss_size = MAIN_STACK_SIZE;
218
219	/*
220	 * Create KSE mailbox.
221	 */
222	p = (char *)malloc(THREAD_STACK_SIZE);
223	bzero(&data->mb, sizeof(struct kse_mailbox));
224	data->mb.km_stack.ss_sp = p;
225	data->mb.km_stack.ss_size = THREAD_STACK_SIZE;
226	data->mb.km_func = (void *)uts;
227	data->mb.km_udata = data;
228	data->cur_thread = tm;
229	data->runq = q;
230	pfmt("uts() at : 0x%x\n", uts);
231	pfmt("uts stack at : 0x%x - 0x%x\n", p, p + THREAD_STACK_SIZE);
232}
233
234static void
235start_uts(struct uts_data *data, int newgrp)
236{
237	/*
238	 * Start KSE scheduling.
239	 */
240	pfmt("kse_create() -> %d\n", kse_create(&data->mb, newgrp));
241	data->mb.km_curthread = data->cur_thread;
242
243	/*
244	 * Arrange to deliver signals via KSE.
245	 */
246	signal(SIGURG, foof);
247}
248
249/*
250 * Write a single character to stdout, in a thread-safe manner.
251 */
252static void
253pchar(char c)
254{
255
256	write(STDOUT_FILENO, &c, 1);
257}
258
259/*
260 * Write formatted output to stdout, in a thread-safe manner.
261 *
262 * Recognises the following conversions:
263 *	%c	-> char
264 *	%d	-> signed int (base 10)
265 *	%s	-> string
266 *	%u	-> unsigned int (base 10)
267 *	%x	-> unsigned int (base 16)
268 */
269static void
270pfmt(const char *fmt, ...)
271{
272	static const char digits[16] = "0123456789abcdef";
273	va_list	 ap;
274	char buf[10];
275	char *s;
276	unsigned r, u;
277	int c, d;
278
279	va_start(ap, fmt);
280	while ((c = *fmt++)) {
281		if (c == '%') {
282			c = *fmt++;
283			switch (c) {
284			case 'c':
285				pchar(va_arg(ap, int));
286				continue;
287			case 's':
288				pstr(va_arg(ap, char *));
289				continue;
290			case 'd':
291			case 'u':
292			case 'x':
293				r = ((c == 'u') || (c == 'd')) ? 10 : 16;
294				if (c == 'd') {
295					d = va_arg(ap, unsigned);
296					if (d < 0) {
297						pchar('-');
298						u = (unsigned)(d * -1);
299					} else
300						u = (unsigned)d;
301				} else
302					u = va_arg(ap, unsigned);
303				s = buf;
304				do {
305					*s++ = digits[u % r];
306				} while (u /= r);
307				while (--s >= buf)
308					pchar(*s);
309				continue;
310			}
311		}
312		pchar(c);
313	}
314	va_end(ap);
315}
316
317static void
318pstr(const char *s)
319{
320
321	write(STDOUT_FILENO, s, strlen(s));
322}
323
324static void
325runq_init(struct uts_runq *q)
326{
327	q->head = NULL;
328	simplelock_init(&q->lock);
329}
330
331/*
332 * Insert a thread into the run queue.
333 */
334static void
335runq_insert(struct uts_runq *q, struct kse_thr_mailbox *tm)
336{
337	simplelock_lock(&q->lock);
338	tm->tm_next = q->head;
339	q->head = tm;
340	simplelock_unlock(&q->lock);
341}
342
343/*
344 * Select and remove a thread from the run queue.
345 */
346static struct kse_thr_mailbox *
347runq_remove(struct uts_runq *q)
348{
349	struct kse_thr_mailbox *tm;
350
351	simplelock_lock(&q->lock);
352	tm = runq_remove_nolock(q);
353	simplelock_unlock(&q->lock);
354	return tm;
355}
356
357static struct kse_thr_mailbox *
358runq_remove_nolock(struct uts_runq *q)
359{
360	struct kse_thr_mailbox *p, *p1;
361
362	if (q->head == NULL)
363		return (NULL);
364	p1 = NULL;
365	for (p = q->head; p->tm_next != NULL; p = p->tm_next)
366		p1 = p;
367	if (p1 == NULL)
368		q->head = NULL;
369	else
370		p1->tm_next = NULL;
371	return (p);
372}
373
374/*
375 * Userland thread scheduler.
376 */
377static void
378uts(struct kse_mailbox *km)
379{
380	static struct uts_data *prev_data;
381	struct kse_thr_mailbox *tm, *p;
382	struct uts_data *data;
383	int ret, i;
384
385	UPSTR("\n--uts() start--\n");
386	UPFMT("mailbox -> %x\n", km);
387
388	/*
389	 * Insert any processes back from being blocked
390	 * in the kernel into the run queue.
391	 */
392	data = km->km_udata;
393	p = km->km_completed;
394	km->km_completed = NULL;
395	UPFMT("km_completed -> 0x%x", p);
396#ifdef TRACE_KSE
397	if (data != prev_data) {
398		prev_data = data;
399		pfmt("uts data: 0x%x\n", data);
400	}
401#endif
402	while ((tm = p) != NULL) {
403		p = tm->tm_next;
404		UPFMT(" 0x%x", p);
405		runq_insert(data->runq, tm);
406	}
407	UPCHAR('\n');
408
409	simplelock_lock(&data->runq->lock);
410	/*
411	 * Process any signals we've recieved (but only if we have
412	 * somewhere to deliver them to).
413	 */
414	if ((data->runq->head != NULL) && SIGNOTEMPTY(km->km_sigscaught)) {
415		for (i = 0;i < _SIG_MAXSIG;i++)
416			if (SIGISMEMBER(km->km_sigscaught, i)) {
417				signalcontext(&data->runq->head->tm_context,
418				 i, foof);
419				break;
420			}
421		bzero(&km->km_sigscaught, sizeof(sigset_t));
422	}
423
424	/*
425	 * Pull a thread off the run queue.
426	 */
427	p = runq_remove_nolock(data->runq);
428	simplelock_unlock(&data->runq->lock);
429#if 0
430	if ((p == aa) && (progress > 0)) {
431		--progress;
432		signalcontext(&p->tm_context, 1, foof);
433	}
434#endif
435
436	/*
437	 * Either schedule a thread, or idle if none ready to run.
438	 */
439	if (p != NULL) {
440		UPFMT("\n-- uts() scheduling 0x%x--\n", p);
441		UPFMT("eip -> 0x%x progress -> %d\n",
442		    p->tm_context.uc_mcontext.mc_eip, progress);
443		UPSTR("curthread set\n");
444		uts_to_thread(p, &km->km_curthread);
445		UPSTR("\n-- uts_to_thread() failed --\n");
446	}
447	kse_release();
448	pstr("** uts() exiting **\n");
449	exit(EX_SOFTWARE);
450}
451
452/*
453 * Start a thread.
454 */
455static struct kse_thr_mailbox *
456thread_create(const void *func, int arg)
457{
458	struct kse_thr_mailbox *tm;
459	char *p;
460
461	aa = tm = (struct kse_thr_mailbox *)calloc(1, sizeof(struct kse_thr_mailbox));
462	getcontext(&tm->tm_context);
463	p = (char *)malloc(THREAD_STACK_SIZE);
464	tm->tm_context.uc_stack.ss_sp = p;
465	tm->tm_context.uc_stack.ss_size = THREAD_STACK_SIZE;
466	makecontext(&tm->tm_context, func, 2, arg);
467	// setcontext(&tm->tm_context);
468	return tm;
469}
470
471static void
472thread_start(struct uts_data *data, const void *func, int arg)
473{
474	struct kse_thr_mailbox *tm;
475	struct kse_thr_mailbox *tm2;
476
477	tm = thread_create(func, arg);
478	tm2 = thread_create(enter_uts, (int)data);
479	tm->tm_context.uc_link = &tm2->tm_context;
480	runq_insert(data->runq, tm);
481	pfmt("thread_start() : 0x%x %x\n", tm, &tm->tm_context);
482}
483