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