thr_kern.c revision 103419
1/* 2 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> 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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by John Birrell. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD: head/lib/libkse/thread/thr_kern.c 103419 2002-09-16 19:52:52Z mini $ 33 * 34 */ 35#include <errno.h> 36#include <poll.h> 37#include <stdlib.h> 38#include <stdarg.h> 39#include <string.h> 40#include <unistd.h> 41#include <sys/param.h> 42#include <sys/types.h> 43#include <sys/signalvar.h> 44#include <sys/stat.h> 45#include <sys/time.h> 46#include <sys/socket.h> 47#include <sys/uio.h> 48#include <sys/syscall.h> 49#include <fcntl.h> 50#include <pthread.h> 51#include "thr_private.h" 52 53/* #define DEBUG_THREAD_KERN */ 54#ifdef DEBUG_THREAD_KERN 55#define DBG_MSG stdout_debug 56#else 57#define DBG_MSG(x...) 58#endif 59 60/* Static function prototype definitions: */ 61static void 62thread_kern_idle(void); 63 64static void 65dequeue_signals(void); 66 67static inline void 68thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in); 69 70/* Static variables: */ 71static int last_tick = 0; 72 73void 74_thread_kern_sched(void) 75{ 76 struct pthread *curthread = _get_curthread(); 77 78 /* 79 * Flag the pthread kernel as executing scheduler code 80 * to avoid a scheduler signal from interrupting this 81 * execution and calling the scheduler again. 82 */ 83 _thread_kern_in_sched = 1; 84 85 /* Switch into the scheduler's context. */ 86 swapcontext(&curthread->ctx, &_thread_kern_sched_ctx); 87 DBG_MSG("Returned from swapcontext, thread %p\n", curthread); 88 89 /* 90 * This point is reached when swapcontext() is called 91 * to restore the state of a thread. 92 * 93 * This is the normal way out of the scheduler. 94 */ 95 _thread_kern_in_sched = 0; 96 97 if (curthread->sig_defer_count == 0) { 98 if (((curthread->cancelflags & 99 PTHREAD_AT_CANCEL_POINT) == 0) && 100 ((curthread->cancelflags & 101 PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) 102 /* 103 * Stick a cancellation point at the 104 * start of each async-cancellable 105 * thread's resumption. 106 * 107 * We allow threads woken at cancel 108 * points to do their own checks. 109 */ 110 pthread_testcancel(); 111 } 112 113 if (_sched_switch_hook != NULL) { 114 /* Run the installed switch hook: */ 115 thread_run_switch_hook(_last_user_thread, curthread); 116 } 117} 118 119void 120_thread_kern_scheduler(void) 121{ 122 struct timespec ts; 123 struct timeval tv; 124 struct pthread *curthread = _get_curthread(); 125 pthread_t pthread, pthread_h; 126 unsigned int current_tick; 127 int add_to_prioq; 128 129 /* 130 * Enter a scheduling loop that finds the next thread that is 131 * ready to run. This loop completes when there are no more threads 132 * in the global list. It is interrupted each time a thread is 133 * scheduled, but will continue when we return. 134 */ 135 while (!(TAILQ_EMPTY(&_thread_list))) { 136 137 /* If the currently running thread is a user thread, save it: */ 138 if ((curthread->flags & PTHREAD_FLAGS_PRIVATE) == 0) 139 _last_user_thread = curthread; 140 141 /* Get the current time of day: */ 142 GET_CURRENT_TOD(tv); 143 TIMEVAL_TO_TIMESPEC(&tv, &ts); 144 current_tick = _sched_ticks; 145 146 add_to_prioq = 0; 147 if (curthread != &_thread_kern_thread) { 148 /* 149 * This thread no longer needs to yield the CPU. 150 */ 151 if (curthread->state != PS_RUNNING) { 152 /* 153 * Save the current time as the time that the 154 * thread became inactive: 155 */ 156 curthread->last_inactive = (long)current_tick; 157 if (curthread->last_inactive < 158 curthread->last_active) { 159 /* Account for a rollover: */ 160 curthread->last_inactive =+ 161 UINT_MAX + 1; 162 } 163 } 164 165 /* 166 * Place the currently running thread into the 167 * appropriate queue(s). 168 */ 169 switch (curthread->state) { 170 case PS_DEAD: 171 case PS_STATE_MAX: /* to silence -Wall */ 172 case PS_SUSPENDED: 173 /* 174 * Dead and suspended threads are not placed 175 * in any queue: 176 */ 177 break; 178 179 case PS_RUNNING: 180 /* 181 * Runnable threads can't be placed in the 182 * priority queue until after waiting threads 183 * are polled (to preserve round-robin 184 * scheduling). 185 */ 186 add_to_prioq = 1; 187 break; 188 189 /* 190 * States which do not depend on file descriptor I/O 191 * operations or timeouts: 192 */ 193 case PS_DEADLOCK: 194 case PS_JOIN: 195 case PS_MUTEX_WAIT: 196 case PS_WAIT_WAIT: 197 /* No timeouts for these states: */ 198 curthread->wakeup_time.tv_sec = -1; 199 curthread->wakeup_time.tv_nsec = -1; 200 201 /* Restart the time slice: */ 202 curthread->slice_usec = -1; 203 204 /* Insert into the waiting queue: */ 205 PTHREAD_WAITQ_INSERT(curthread); 206 break; 207 208 /* States which can timeout: */ 209 case PS_COND_WAIT: 210 case PS_SLEEP_WAIT: 211 /* Restart the time slice: */ 212 curthread->slice_usec = -1; 213 214 /* Insert into the waiting queue: */ 215 PTHREAD_WAITQ_INSERT(curthread); 216 break; 217 218 /* States that require periodic work: */ 219 case PS_SPINBLOCK: 220 /* No timeouts for this state: */ 221 curthread->wakeup_time.tv_sec = -1; 222 curthread->wakeup_time.tv_nsec = -1; 223 224 /* Increment spinblock count: */ 225 _spinblock_count++; 226 227 /* FALLTHROUGH */ 228 } 229 } 230 231 last_tick = current_tick; 232 233 /* 234 * Wake up threads that have timedout. This has to be 235 * done after polling in case a thread does a poll or 236 * select with zero time. 237 */ 238 PTHREAD_WAITQ_SETACTIVE(); 239 while (((pthread = TAILQ_FIRST(&_waitingq)) != NULL) && 240 (pthread->wakeup_time.tv_sec != -1) && 241 (((pthread->wakeup_time.tv_sec == 0) && 242 (pthread->wakeup_time.tv_nsec == 0)) || 243 (pthread->wakeup_time.tv_sec < ts.tv_sec) || 244 ((pthread->wakeup_time.tv_sec == ts.tv_sec) && 245 (pthread->wakeup_time.tv_nsec <= ts.tv_nsec)))) { 246 /* 247 * Remove this thread from the waiting queue 248 * (and work queue if necessary) and place it 249 * in the ready queue. 250 */ 251 PTHREAD_WAITQ_CLEARACTIVE(); 252 if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) 253 PTHREAD_WORKQ_REMOVE(pthread); 254 PTHREAD_NEW_STATE(pthread, PS_RUNNING); 255 PTHREAD_WAITQ_SETACTIVE(); 256 /* 257 * Flag the timeout in the thread structure: 258 */ 259 pthread->timeout = 1; 260 } 261 PTHREAD_WAITQ_CLEARACTIVE(); 262 263 /* 264 * Check to see if the current thread needs to be added 265 * to the priority queue: 266 */ 267 if (add_to_prioq != 0) { 268 /* 269 * Save the current time as the time that the 270 * thread became inactive: 271 */ 272 current_tick = _sched_ticks; 273 curthread->last_inactive = (long)current_tick; 274 if (curthread->last_inactive < 275 curthread->last_active) { 276 /* Account for a rollover: */ 277 curthread->last_inactive =+ UINT_MAX + 1; 278 } 279 280 if ((curthread->slice_usec != -1) && 281 (curthread->attr.sched_policy != SCHED_FIFO)) { 282 /* 283 * Accumulate the number of microseconds for 284 * which the current thread has run: 285 */ 286 curthread->slice_usec += 287 (curthread->last_inactive - 288 curthread->last_active) * 289 (long)_clock_res_usec; 290 /* Check for time quantum exceeded: */ 291 if (curthread->slice_usec > TIMESLICE_USEC) 292 curthread->slice_usec = -1; 293 } 294 295 if (curthread->slice_usec == -1) { 296 /* 297 * The thread exceeded its time 298 * quantum or it yielded the CPU; 299 * place it at the tail of the 300 * queue for its priority. 301 */ 302 PTHREAD_PRIOQ_INSERT_TAIL(curthread); 303 } else { 304 /* 305 * The thread hasn't exceeded its 306 * interval. Place it at the head 307 * of the queue for its priority. 308 */ 309 PTHREAD_PRIOQ_INSERT_HEAD(curthread); 310 } 311 } 312 313 /* 314 * Get the highest priority thread in the ready queue. 315 */ 316 pthread_h = PTHREAD_PRIOQ_FIRST(); 317 318 /* Check if there are no threads ready to run: */ 319 if (pthread_h == NULL) { 320 /* 321 * Lock the pthread kernel by changing the pointer to 322 * the running thread to point to the global kernel 323 * thread structure: 324 */ 325 _set_curthread(&_thread_kern_thread); 326 curthread = &_thread_kern_thread; 327 328 DBG_MSG("No runnable threads, using kernel thread %p\n", 329 curthread); 330 331 /* 332 * There are no threads ready to run, so wait until 333 * something happens that changes this condition: 334 */ 335 thread_kern_idle(); 336 337 /* 338 * This process' usage will likely be very small 339 * while waiting in a poll. Since the scheduling 340 * clock is based on the profiling timer, it is 341 * unlikely that the profiling timer will fire 342 * and update the time of day. To account for this, 343 * get the time of day after polling with a timeout. 344 */ 345 gettimeofday((struct timeval *) &_sched_tod, NULL); 346 347 /* Check once more for a runnable thread: */ 348 pthread_h = PTHREAD_PRIOQ_FIRST(); 349 } 350 351 if (pthread_h != NULL) { 352 /* Remove the thread from the ready queue: */ 353 PTHREAD_PRIOQ_REMOVE(pthread_h); 354 355 /* Make the selected thread the current thread: */ 356 _set_curthread(pthread_h); 357 curthread = pthread_h; 358 359 /* 360 * Save the current time as the time that the thread 361 * became active: 362 */ 363 current_tick = _sched_ticks; 364 curthread->last_active = (long) current_tick; 365 366 /* 367 * Check if this thread is running for the first time 368 * or running again after using its full time slice 369 * allocation: 370 */ 371 if (curthread->slice_usec == -1) { 372 /* Reset the accumulated time slice period: */ 373 curthread->slice_usec = 0; 374 } 375 376 /* 377 * If we had a context switch, run any 378 * installed switch hooks. 379 */ 380 if ((_sched_switch_hook != NULL) && 381 (_last_user_thread != curthread)) { 382 thread_run_switch_hook(_last_user_thread, 383 curthread); 384 } 385 /* 386 * Continue the thread at its current frame: 387 */ 388 swapcontext(&_thread_kern_sched_ctx, &curthread->ctx); 389 } 390 } 391 392 /* There are no more threads, so exit this process: */ 393 exit(0); 394} 395 396void 397_thread_kern_sched_state(enum pthread_state state, char *fname, int lineno) 398{ 399 struct pthread *curthread = _get_curthread(); 400 401 /* 402 * Flag the pthread kernel as executing scheduler code 403 * to avoid a scheduler signal from interrupting this 404 * execution and calling the scheduler again. 405 */ 406 _thread_kern_in_sched = 1; 407 408 /* Change the state of the current thread: */ 409 curthread->state = state; 410 curthread->fname = fname; 411 curthread->lineno = lineno; 412 413 /* Schedule the next thread that is ready: */ 414 _thread_kern_sched(); 415} 416 417void 418_thread_kern_sched_state_unlock(enum pthread_state state, 419 spinlock_t *lock, char *fname, int lineno) 420{ 421 struct pthread *curthread = _get_curthread(); 422 423 /* 424 * Flag the pthread kernel as executing scheduler code 425 * to avoid a scheduler signal from interrupting this 426 * execution and calling the scheduler again. 427 */ 428 _thread_kern_in_sched = 1; 429 430 /* Change the state of the current thread: */ 431 curthread->state = state; 432 curthread->fname = fname; 433 curthread->lineno = lineno; 434 435 _SPINUNLOCK(lock); 436 437 /* Schedule the next thread that is ready: */ 438 _thread_kern_sched(); 439} 440 441static void 442thread_kern_idle() 443{ 444 int i, found; 445 int kern_pipe_added = 0; 446 int nfds = 0; 447 int timeout_ms = 0; 448 struct pthread *pthread; 449 struct timespec ts; 450 struct timeval tv; 451 452 /* Get the current time of day: */ 453 GET_CURRENT_TOD(tv); 454 TIMEVAL_TO_TIMESPEC(&tv, &ts); 455 456 pthread = TAILQ_FIRST(&_waitingq); 457 458 if ((pthread == NULL) || (pthread->wakeup_time.tv_sec == -1)) { 459 /* 460 * Either there are no threads in the waiting queue, 461 * or there are no threads that can timeout. 462 */ 463 PANIC("Would idle forever"); 464 } 465 else if (pthread->wakeup_time.tv_sec - ts.tv_sec > 60000) 466 /* Limit maximum timeout to prevent rollover. */ 467 timeout_ms = 60000; 468 else { 469 /* 470 * Calculate the time left for the next thread to 471 * timeout: 472 */ 473 timeout_ms = ((pthread->wakeup_time.tv_sec - ts.tv_sec) * 474 1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec) / 475 1000000); 476 /* 477 * Only idle if we would be. 478 */ 479 if (timeout_ms <= 0) 480 return; 481 } 482 483 /* 484 * Check for a thread that became runnable due to a signal: 485 */ 486 if (PTHREAD_PRIOQ_FIRST() != NULL) { 487 /* 488 * Since there is at least one runnable thread, 489 * disable the wait. 490 */ 491 timeout_ms = 0; 492 } 493 494 /* 495 * Idle. 496 */ 497 __sys_poll(NULL, 0, timeout_ms); 498 499 if (_spinblock_count != 0) { 500 /* 501 * Enter a loop to look for threads waiting on a spinlock 502 * that is now available. 503 */ 504 PTHREAD_WAITQ_SETACTIVE(); 505 TAILQ_FOREACH(pthread, &_workq, qe) { 506 if (pthread->state == PS_SPINBLOCK) { 507 /* 508 * If the lock is available, let the thread run. 509 */ 510 if (pthread->data.spinlock->access_lock == 0) { 511 PTHREAD_WAITQ_CLEARACTIVE(); 512 PTHREAD_WORKQ_REMOVE(pthread); 513 PTHREAD_NEW_STATE(pthread,PS_RUNNING); 514 PTHREAD_WAITQ_SETACTIVE(); 515 516 /* 517 * One less thread in a spinblock state: 518 */ 519 _spinblock_count--; 520 } 521 } 522 } 523 PTHREAD_WAITQ_CLEARACTIVE(); 524 } 525} 526 527void 528_thread_kern_set_timeout(const struct timespec * timeout) 529{ 530 struct pthread *curthread = _get_curthread(); 531 struct timespec current_time; 532 struct timeval tv; 533 534 /* Reset the timeout flag for the running thread: */ 535 curthread->timeout = 0; 536 537 /* Check if the thread is to wait forever: */ 538 if (timeout == NULL) { 539 /* 540 * Set the wakeup time to something that can be recognised as 541 * different to an actual time of day: 542 */ 543 curthread->wakeup_time.tv_sec = -1; 544 curthread->wakeup_time.tv_nsec = -1; 545 } 546 /* Check if no waiting is required: */ 547 else if (timeout->tv_sec == 0 && timeout->tv_nsec == 0) { 548 /* Set the wake up time to 'immediately': */ 549 curthread->wakeup_time.tv_sec = 0; 550 curthread->wakeup_time.tv_nsec = 0; 551 } else { 552 /* Get the current time: */ 553 GET_CURRENT_TOD(tv); 554 TIMEVAL_TO_TIMESPEC(&tv, ¤t_time); 555 556 /* Calculate the time for the current thread to wake up: */ 557 curthread->wakeup_time.tv_sec = current_time.tv_sec + timeout->tv_sec; 558 curthread->wakeup_time.tv_nsec = current_time.tv_nsec + timeout->tv_nsec; 559 560 /* Check if the nanosecond field needs to wrap: */ 561 if (curthread->wakeup_time.tv_nsec >= 1000000000) { 562 /* Wrap the nanosecond field: */ 563 curthread->wakeup_time.tv_sec += 1; 564 curthread->wakeup_time.tv_nsec -= 1000000000; 565 } 566 } 567} 568 569void 570_thread_kern_sig_defer(void) 571{ 572 struct pthread *curthread = _get_curthread(); 573 574 /* Allow signal deferral to be recursive. */ 575 curthread->sig_defer_count++; 576} 577 578void 579_thread_kern_sig_undefer(void) 580{ 581 struct pthread *curthread = _get_curthread(); 582 583 /* 584 * Perform checks to yield only if we are about to undefer 585 * signals. 586 */ 587 if (curthread->sig_defer_count > 1) { 588 /* Decrement the signal deferral count. */ 589 curthread->sig_defer_count--; 590 } 591 else if (curthread->sig_defer_count == 1) { 592 /* Reenable signals: */ 593 curthread->sig_defer_count = 0; 594 595 /* 596 * Check for asynchronous cancellation before delivering any 597 * pending signals: 598 */ 599 if (((curthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) && 600 ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) 601 pthread_testcancel(); 602 } 603} 604 605static inline void 606thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in) 607{ 608 pthread_t tid_out = thread_out; 609 pthread_t tid_in = thread_in; 610 611 if ((tid_out != NULL) && 612 (tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0) 613 tid_out = NULL; 614 if ((tid_in != NULL) && 615 (tid_in->flags & PTHREAD_FLAGS_PRIVATE) != 0) 616 tid_in = NULL; 617 618 if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) { 619 /* Run the scheduler switch hook: */ 620 _sched_switch_hook(tid_out, tid_in); 621 } 622} 623 624struct pthread * 625_get_curthread(void) 626{ 627 if (_thread_initial == NULL) 628 _thread_init(); 629 630 return (_thread_run); 631} 632 633void 634_set_curthread(struct pthread *newthread) 635{ 636 _thread_run = newthread; 637} 638