/* $OpenBSD: uthread_kern.c,v 1.16 2001/09/04 22:17:45 fgsch Exp $ */ /* * Copyright (c) 1995-1998 John Birrell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by John Birrell. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: uthread_kern.c,v 1.23 1999/09/29 15:18:39 marcel Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _THREAD_SAFE #include #include "pthread_private.h" /* Static function prototype definitions: */ static void _thread_kern_poll(int wait_reqd); static void dequeue_signals(void); static inline void thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in); static void _thread_check_cancel() { if (!(_thread_run->flags & PTHREAD_FLAGS_CANCELPT) && (_thread_run->canceltype == PTHREAD_CANCEL_ASYNCHRONOUS)) /* * Check if an async-cancellable thread * has been cancelled. */ _thread_cancellation_point(); } void _thread_kern_sched(struct sigcontext * scp) { struct pthread *curthread = _get_curthread(); pthread_t pthread, pthread_h = NULL; pthread_t old_thread_run; struct itimerval itimer; struct timespec ts, ts1; struct timeval tv, tv1; int set_timer = 0; /* * Flag the pthread kernel as executing scheduler code * to avoid a scheduler signal from interrupting this * execution and calling the scheduler again. */ _thread_kern_in_sched = 1; /* Check if this function was called from the signal handler: */ if (scp != NULL) { /* * Copy the signal context to the current thread's jump * buffer: */ memcpy(&_thread_run->saved_sigcontext, scp, sizeof(_thread_run->saved_sigcontext)); /* * Save floating point state. */ _thread_machdep_save_float_state(&_thread_run->_machdep); /* Flag the signal context as the last state saved: */ _thread_run->sig_saved = 1; } else /* Flag the jump buffer was the last state saved: */ _thread_run->sig_saved = 0; /* If the currently running thread is a user thread, save it: */ if ((_thread_run->flags & PTHREAD_FLAGS_PRIVATE) == 0) _last_user_thread = _thread_run; /* Save errno. */ _thread_run->error = errno; /* Save the current thread to switch from */ old_thread_run = _thread_run; /* * Enter a scheduling loop that finds the next thread that is * ready to run. This loop completes when there are no more threads * in the global list or when a thread has its state restored by * either a sigreturn (if the state was saved as a sigcontext) or a * switch. */ while (!(TAILQ_EMPTY(&_thread_list))) { /* Get the current time of day: */ gettimeofday(&tv, NULL); TIMEVAL_TO_TIMESPEC(&tv, &ts); /* * Protect the scheduling queues from access by the signal * handler. */ _queue_signals = 1; if (_thread_run != &_thread_kern_thread) { /* * This thread no longer needs to yield the CPU. */ _thread_run->yield_on_sig_undefer = 0; /* * Save the current time as the time that the thread * became inactive: */ _thread_run->last_inactive.tv_sec = tv.tv_sec; _thread_run->last_inactive.tv_usec = tv.tv_usec; /* * Place the currently running thread into the * appropriate queue(s). */ switch (_thread_run->state) { case PS_DEAD: /* * Dead threads are not placed in any queue: */ break; case PS_RUNNING: /* * Runnable threads can't be placed in the * priority queue until after waiting threads * are polled (to preserve round-robin * scheduling). */ if ((_thread_run->slice_usec != -1) && (_thread_run->attr.sched_policy != SCHED_FIFO)) { /* * Accumulate the number of microseconds that * this thread has run for: */ _thread_run->slice_usec += (_thread_run->last_inactive.tv_sec - _thread_run->last_active.tv_sec) * 1000000 + _thread_run->last_inactive.tv_usec - _thread_run->last_active.tv_usec; /* Check for time quantum exceeded: */ if (_thread_run->slice_usec > TIMESLICE_USEC) _thread_run->slice_usec = -1; } break; /* * States which do not depend on file descriptor I/O * operations or timeouts: */ case PS_DEADLOCK: case PS_FDLR_WAIT: case PS_FDLW_WAIT: case PS_FILE_WAIT: case PS_JOIN: case PS_MUTEX_WAIT: case PS_SIGSUSPEND: case PS_SIGTHREAD: case PS_SIGWAIT: case PS_SUSPENDED: case PS_WAIT_WAIT: /* No timeouts for these states: */ _thread_run->wakeup_time.tv_sec = -1; _thread_run->wakeup_time.tv_nsec = -1; /* Restart the time slice: */ _thread_run->slice_usec = -1; /* Insert into the waiting queue: */ PTHREAD_WAITQ_INSERT(_thread_run); break; /* States which can timeout: */ case PS_COND_WAIT: case PS_SLEEP_WAIT: /* Restart the time slice: */ _thread_run->slice_usec = -1; /* Insert into the waiting queue: */ PTHREAD_WAITQ_INSERT(_thread_run); break; /* States that require periodic work: */ case PS_SPINBLOCK: /* No timeouts for this state: */ _thread_run->wakeup_time.tv_sec = -1; _thread_run->wakeup_time.tv_nsec = -1; /* Increment spinblock count: */ _spinblock_count++; /* fall through */ case PS_FDR_WAIT: case PS_FDW_WAIT: case PS_POLL_WAIT: case PS_SELECT_WAIT: /* Restart the time slice: */ _thread_run->slice_usec = -1; /* Insert into the waiting queue: */ PTHREAD_WAITQ_INSERT(_thread_run); /* Insert into the work queue: */ PTHREAD_WORKQ_INSERT(_thread_run); } } /* Unprotect the scheduling queues: */ _queue_signals = 0; /* * Poll file descriptors to update the state of threads * waiting on file I/O where data may be available: */ _thread_kern_poll(0); /* Protect the scheduling queues: */ _queue_signals = 1; /* * Wake up threads that have timedout. This has to be * done after polling in case a thread does a poll or * select with zero time. */ PTHREAD_WAITQ_SETACTIVE(); while (((pthread = TAILQ_FIRST(&_waitingq)) != NULL) && (pthread->wakeup_time.tv_sec != -1) && (((pthread->wakeup_time.tv_sec == 0) && (pthread->wakeup_time.tv_nsec == 0)) || (pthread->wakeup_time.tv_sec < ts.tv_sec) || ((pthread->wakeup_time.tv_sec == ts.tv_sec) && (pthread->wakeup_time.tv_nsec <= ts.tv_nsec)))) { switch (pthread->state) { case PS_POLL_WAIT: case PS_SELECT_WAIT: /* Return zero file descriptors ready: */ pthread->data.poll_data->nfds = 0; /* fall through */ default: /* * Remove this thread from the waiting queue * (and work queue if necessary) and place it * in the ready queue. */ PTHREAD_WAITQ_CLEARACTIVE(); if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread, PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); break; } /* * Flag the timeout in the thread structure: */ pthread->timeout = 1; } PTHREAD_WAITQ_CLEARACTIVE(); /* * Check if there is a current runnable thread that isn't * already in the ready queue: */ if ((_thread_run != &_thread_kern_thread) && (_thread_run->state == PS_RUNNING) && ((_thread_run->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0)) { if (_thread_run->slice_usec == -1) { /* * The thread exceeded its time * quantum or it yielded the CPU; * place it at the tail of the * queue for its priority. */ PTHREAD_PRIOQ_INSERT_TAIL(_thread_run); } else { /* * The thread hasn't exceeded its * interval. Place it at the head * of the queue for its priority. */ PTHREAD_PRIOQ_INSERT_HEAD(_thread_run); } } /* * Get the highest priority thread in the ready queue. */ pthread_h = PTHREAD_PRIOQ_FIRST(); /* Check if there are no threads ready to run: */ if (pthread_h == NULL) { /* * Lock the pthread kernel by changing the pointer to * the running thread to point to the global kernel * thread structure: */ _set_curthread(&_thread_kern_thread); curthread = &_thread_kern_thread; /* Unprotect the scheduling queues: */ _queue_signals = 0; /* * There are no threads ready to run, so wait until * something happens that changes this condition: */ _thread_kern_poll(1); } else { /* Remove the thread from the ready queue: */ PTHREAD_PRIOQ_REMOVE(pthread_h); /* Get first thread on the waiting list: */ pthread = TAILQ_FIRST(&_waitingq); /* Check to see if there is more than one thread: */ if (pthread_h != TAILQ_FIRST(&_thread_list) || TAILQ_NEXT(pthread_h, tle) != NULL) set_timer = 1; else set_timer = 0; /* Unprotect the scheduling queues: */ _queue_signals = 0; /* * Check for signals queued while the scheduling * queues were protected: */ while (_sigq_check_reqd != 0) { /* Clear before handling queued signals: */ _sigq_check_reqd = 0; /* Protect the scheduling queues again: */ _queue_signals = 1; dequeue_signals(); /* * Check for a higher priority thread that * became runnable due to signal handling. */ if (((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) && (pthread->active_priority > pthread_h->active_priority)) { /* * Insert the lower priority thread * at the head of its priority list: */ PTHREAD_PRIOQ_INSERT_HEAD(pthread_h); /* Remove the thread from the ready queue: */ PTHREAD_PRIOQ_REMOVE(pthread); /* There's a new thread in town: */ pthread_h = pthread; } /* Get first thread on the waiting list: */ pthread = TAILQ_FIRST(&_waitingq); /* * Check to see if there is more than one * thread: */ if (pthread_h != TAILQ_FIRST(&_thread_list) || TAILQ_NEXT(pthread_h, tle) != NULL) set_timer = 1; else set_timer = 0; /* Unprotect the scheduling queues: */ _queue_signals = 0; } /* Make the selected thread the current thread: */ _set_curthread(pthread_h); curthread = pthread_h; /* * Save the current time as the time that the thread * became active: */ curthread->last_active.tv_sec = tv.tv_sec; curthread->last_active.tv_usec = tv.tv_usec; /* * Define the maximum time before a scheduling signal * is required: */ itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = TIMESLICE_USEC; /* * The interval timer is not reloaded when it * times out. The interval time needs to be * calculated every time. */ itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; /* Get first thread on the waiting list: */ if ((pthread != NULL) && (pthread->wakeup_time.tv_sec != -1)) { /* * Calculate the time until this thread * is ready, allowing for the clock * resolution: */ ts1.tv_sec = pthread->wakeup_time.tv_sec - ts.tv_sec; ts1.tv_nsec = pthread->wakeup_time.tv_nsec - ts.tv_nsec + _clock_res_nsec; /* * Check for underflow of the nanosecond field: */ while (ts1.tv_nsec < 0) { /* * Allow for the underflow of the * nanosecond field: */ ts1.tv_sec--; ts1.tv_nsec += 1000000000; } /* * Check for overflow of the nanosecond field: */ while (ts1.tv_nsec >= 1000000000) { /* * Allow for the overflow of the * nanosecond field: */ ts1.tv_sec++; ts1.tv_nsec -= 1000000000; } /* * Convert the timespec structure to a * timeval structure: */ TIMESPEC_TO_TIMEVAL(&tv1, &ts1); /* * Check if the thread will be ready * sooner than the earliest ones found * so far: */ if (timercmp(&tv1, &itimer.it_value, <)) { /* * Update the time value: */ itimer.it_value.tv_sec = tv1.tv_sec; itimer.it_value.tv_usec = tv1.tv_usec; } } /* * Check if this thread is running for the first time * or running again after using its full time slice * allocation: */ if (_thread_run->slice_usec == -1) { /* Reset the accumulated time slice period: */ _thread_run->slice_usec = 0; } /* Check if there is more than one thread: */ if (set_timer != 0) { /* * Start the interval timer for the * calculated time interval: */ if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0) { /* * Cannot initialise the timer, so * abort this process: */ PANIC("Cannot set scheduling timer"); } } /* Restore errno. */ errno = _thread_run->error; /* * Restore the new thread, saving current. */ _thread_machdep_switch(&_thread_run->_machdep, &old_thread_run->_machdep); /* Check if a signal context was saved: */ if (_thread_run->sig_saved == 1) { /* * Restore floating point state. */ _thread_machdep_restore_float_state(&_thread_run->_machdep); /* * Do a sigreturn to restart the thread that * was interrupted by a signal: */ _thread_kern_in_sched = 0; /* * If we had a context switch, run any * installed switch hooks. */ if ((_sched_switch_hook != NULL) && (_last_user_thread != _thread_run)) { thread_run_switch_hook(_last_user_thread, _thread_run); } _thread_check_cancel(); _thread_sys_sigreturn(&_thread_run->saved_sigcontext); } else { /* * This is the normal way out of the scheduler. */ _thread_kern_in_sched = 0; if (_sched_switch_hook != NULL) { /* Run the installed switch hook: */ thread_run_switch_hook(_last_user_thread, _thread_run); } _thread_check_cancel(); return; } /* This point should not be reached. */ PANIC("Thread has returned from sigreturn or switch"); } } /* There are no more threads, so exit this process: */ exit(0); } void _thread_kern_sched_state(enum pthread_state state, char *fname, int lineno) { struct pthread *curthread = _get_curthread(); /* * Flag the pthread kernel as executing scheduler code * to avoid a scheduler signal from interrupting this * execution and calling the scheduler again. */ _thread_kern_in_sched = 1; /* * Prevent the signal handler from fiddling with this thread * before its state is set and is placed into the proper queue. */ _queue_signals = 1; /* Change the state of the current thread: */ curthread->state = state; curthread->fname = fname; curthread->lineno = lineno; /* Schedule the next thread that is ready: */ _thread_kern_sched(NULL); } void _thread_kern_sched_state_unlock(enum pthread_state state, spinlock_t *lock, char *fname, int lineno) { struct pthread *curthread = _get_curthread(); /* * Flag the pthread kernel as executing scheduler code * to avoid a scheduler signal from interrupting this * execution and calling the scheduler again. */ _thread_kern_in_sched = 1; /* * Prevent the signal handler from fiddling with this thread * before its state is set and it is placed into the proper * queue(s). */ _queue_signals = 1; /* Change the state of the current thread: */ curthread->state = state; curthread->fname = fname; curthread->lineno = lineno; _SPINUNLOCK(lock); /* Schedule the next thread that is ready: */ _thread_kern_sched(NULL); } static void _thread_kern_poll(int wait_reqd) { int count = 0; int i, found; int kern_pipe_added = 0; int nfds = 0; int timeout_ms = 0; struct pthread *pthread; struct timespec ts; struct timeval tv; /* Check if the caller wants to wait: */ if (wait_reqd == 0) { timeout_ms = 0; } else { /* Get the current time of day: */ gettimeofday(&tv, NULL); TIMEVAL_TO_TIMESPEC(&tv, &ts); _queue_signals = 1; pthread = TAILQ_FIRST(&_waitingq); _queue_signals = 0; if ((pthread == NULL) || (pthread->wakeup_time.tv_sec == -1)) { /* * Either there are no threads in the waiting queue, * or there are no threads that can timeout. */ timeout_ms = -1; } else { /* * Calculate the time left for the next thread to * timeout allowing for the clock resolution: */ timeout_ms = ((pthread->wakeup_time.tv_sec - ts.tv_sec) * 1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec + _clock_res_nsec) / 1000000); /* * Don't allow negative timeouts: */ if (timeout_ms < 0) timeout_ms = 0; } } /* Protect the scheduling queues: */ _queue_signals = 1; /* * Check to see if the signal queue needs to be walked to look * for threads awoken by a signal while in the scheduler. */ if (_sigq_check_reqd != 0) { /* Reset flag before handling queued signals: */ _sigq_check_reqd = 0; dequeue_signals(); } /* * Check for a thread that became runnable due to a signal: */ if (PTHREAD_PRIOQ_FIRST() != NULL) { /* * Since there is at least one runnable thread, * disable the wait. */ timeout_ms = 0; } /* * Form the poll table: */ nfds = 0; if (timeout_ms != 0) { /* Add the kernel pipe to the poll table: */ _thread_pfd_table[nfds].fd = _thread_kern_pipe[0]; _thread_pfd_table[nfds].events = POLLRDNORM; _thread_pfd_table[nfds].revents = 0; nfds++; kern_pipe_added = 1; } PTHREAD_WAITQ_SETACTIVE(); TAILQ_FOREACH(pthread, &_workq, qe) { switch (pthread->state) { case PS_SPINBLOCK: /* * If the lock is available, let the thread run. */ if (pthread->data.spinlock->access_lock == 0) { PTHREAD_WAITQ_CLEARACTIVE(); PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread,PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); /* One less thread in a spinblock state: */ _spinblock_count--; /* * Since there is at least one runnable * thread, disable the wait. */ timeout_ms = 0; } break; /* File descriptor read wait: */ case PS_FDR_WAIT: /* Limit number of polled files to table size: */ if (nfds < _thread_dtablesize) { _thread_pfd_table[nfds].events = POLLRDNORM; _thread_pfd_table[nfds].fd = pthread->data.fd.fd; nfds++; } break; /* File descriptor write wait: */ case PS_FDW_WAIT: /* Limit number of polled files to table size: */ if (nfds < _thread_dtablesize) { _thread_pfd_table[nfds].events = POLLWRNORM; _thread_pfd_table[nfds].fd = pthread->data.fd.fd; nfds++; } break; /* File descriptor poll or select wait: */ case PS_POLL_WAIT: case PS_SELECT_WAIT: /* Limit number of polled files to table size: */ if (pthread->data.poll_data->nfds + nfds < _thread_dtablesize) { for (i = 0; i < pthread->data.poll_data->nfds; i++) { _thread_pfd_table[nfds + i].fd = pthread->data.poll_data->fds[i].fd; _thread_pfd_table[nfds + i].events = pthread->data.poll_data->fds[i].events; } nfds += pthread->data.poll_data->nfds; } break; /* Other states do not depend on file I/O. */ default: break; } } PTHREAD_WAITQ_CLEARACTIVE(); /* * Wait for a file descriptor to be ready for read, write, or * an exception, or a timeout to occur: */ count = _thread_sys_poll(_thread_pfd_table, nfds, timeout_ms); if (kern_pipe_added != 0) /* * Remove the pthread kernel pipe file descriptor * from the pollfd table: */ nfds = 1; else nfds = 0; /* * Check if it is possible that there are bytes in the kernel * read pipe waiting to be read: */ if (count < 0 || ((kern_pipe_added != 0) && (_thread_pfd_table[0].revents & POLLRDNORM))) { /* * If the kernel read pipe was included in the * count: */ if (count > 0) { /* Decrement the count of file descriptors: */ count--; } if (_sigq_check_reqd != 0) { /* Reset flag before handling signals: */ _sigq_check_reqd = 0; dequeue_signals(); } } /* * Check if any file descriptors are ready: */ if (count > 0) { /* * Enter a loop to look for threads waiting on file * descriptors that are flagged as available by the * _poll syscall: */ PTHREAD_WAITQ_SETACTIVE(); TAILQ_FOREACH(pthread, &_workq, qe) { switch (pthread->state) { case PS_SPINBLOCK: /* * If the lock is available, let the thread run. */ if (pthread->data.spinlock->access_lock == 0) { PTHREAD_WAITQ_CLEARACTIVE(); PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread,PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); /* * One less thread in a spinblock state: */ _spinblock_count--; } break; /* File descriptor read wait: */ case PS_FDR_WAIT: if ((nfds < _thread_dtablesize) && (_thread_pfd_table[nfds].revents & POLLRDNORM)) { PTHREAD_WAITQ_CLEARACTIVE(); PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread,PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); } nfds++; break; /* File descriptor write wait: */ case PS_FDW_WAIT: if ((nfds < _thread_dtablesize) && (_thread_pfd_table[nfds].revents & POLLWRNORM)) { PTHREAD_WAITQ_CLEARACTIVE(); PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread,PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); } nfds++; break; /* File descriptor poll or select wait: */ case PS_POLL_WAIT: case PS_SELECT_WAIT: if (pthread->data.poll_data->nfds + nfds < _thread_dtablesize) { /* * Enter a loop looking for I/O * readiness: */ found = 0; for (i = 0; i < pthread->data.poll_data->nfds; i++) { if (_thread_pfd_table[nfds + i].revents != 0) { pthread->data.poll_data->fds[i].revents = _thread_pfd_table[nfds + i].revents; found++; } } /* Increment before destroying: */ nfds += pthread->data.poll_data->nfds; if (found != 0) { pthread->data.poll_data->nfds = found; PTHREAD_WAITQ_CLEARACTIVE(); PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread,PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); } } else nfds += pthread->data.poll_data->nfds; break; /* Other states do not depend on file I/O. */ default: break; } } PTHREAD_WAITQ_CLEARACTIVE(); } else if (_spinblock_count != 0) { /* * Enter a loop to look for threads waiting on a spinlock * that is now available. */ PTHREAD_WAITQ_SETACTIVE(); TAILQ_FOREACH(pthread, &_workq, qe) { if (pthread->state == PS_SPINBLOCK) { /* * If the lock is available, let the thread run. */ if (pthread->data.spinlock->access_lock == 0) { PTHREAD_WAITQ_CLEARACTIVE(); PTHREAD_WORKQ_REMOVE(pthread); PTHREAD_NEW_STATE(pthread,PS_RUNNING); PTHREAD_WAITQ_SETACTIVE(); /* * One less thread in a spinblock state: */ _spinblock_count--; } } } PTHREAD_WAITQ_CLEARACTIVE(); } /* Unprotect the scheduling queues: */ _queue_signals = 0; while (_sigq_check_reqd != 0) { /* Handle queued signals: */ _sigq_check_reqd = 0; /* Protect the scheduling queues: */ _queue_signals = 1; dequeue_signals(); /* Unprotect the scheduling queues: */ _queue_signals = 0; } /* Nothing to return. */ return; } void _thread_kern_set_timeout(const struct timespec * timeout) { struct pthread *curthread = _get_curthread(); struct timespec current_time; struct timeval tv; /* Reset the timeout flag for the running thread: */ curthread->timeout = 0; /* Check if the thread is to wait forever: */ if (timeout == NULL) { /* * Set the wakeup time to something that can be recognised as * different to an actual time of day: */ curthread->wakeup_time.tv_sec = -1; curthread->wakeup_time.tv_nsec = -1; } /* Check if no waiting is required: */ else if (timeout->tv_sec == 0 && timeout->tv_nsec == 0) { /* Set the wake up time to 'immediately': */ curthread->wakeup_time.tv_sec = 0; curthread->wakeup_time.tv_nsec = 0; } else { /* Get the current time: */ gettimeofday(&tv, NULL); TIMEVAL_TO_TIMESPEC(&tv, ¤t_time); /* Calculate the time for the current thread to wake up: */ curthread->wakeup_time.tv_sec = current_time.tv_sec + timeout->tv_sec; curthread->wakeup_time.tv_nsec = current_time.tv_nsec + timeout->tv_nsec; /* Check if the nanosecond field needs to wrap: */ if (curthread->wakeup_time.tv_nsec >= 1000000000) { /* Wrap the nanosecond field: */ curthread->wakeup_time.tv_sec += 1; curthread->wakeup_time.tv_nsec -= 1000000000; } } return; } void _thread_kern_sig_defer(void) { struct pthread *curthread = _get_curthread(); /* Allow signal deferral to be recursive. */ curthread->sig_defer_count++; } void _thread_kern_sig_undefer(void) { struct pthread *curthread = _get_curthread(); pthread_t pthread; int need_resched = 0; /* * Perform checks to yield only if we are about to undefer * signals. */ if (curthread->sig_defer_count > 1) { /* Decrement the signal deferral count. */ curthread->sig_defer_count--; } else if (curthread->sig_defer_count == 1) { /* Reenable signals: */ curthread->sig_defer_count = 0; /* * Check if there are queued signals: */ while (_sigq_check_reqd != 0) { /* Defer scheduling while we process queued signals: */ curthread->sig_defer_count = 1; /* Clear the flag before checking the signal queue: */ _sigq_check_reqd = 0; /* Dequeue and handle signals: */ dequeue_signals(); /* * Avoiding an unnecessary check to reschedule, check * to see if signal handling caused a higher priority * thread to become ready. */ if ((need_resched == 0) && (((pthread = PTHREAD_PRIOQ_FIRST()) != NULL) && (pthread->active_priority > curthread->active_priority))) { need_resched = 1; } /* Reenable signals: */ curthread->sig_defer_count = 0; } /* Yield the CPU if necessary: */ if (need_resched || curthread->yield_on_sig_undefer != 0) { curthread->yield_on_sig_undefer = 0; _thread_kern_sched(NULL); } } } static void dequeue_signals(void) { char bufr[128]; int i, num; /* * Enter a loop to read and handle queued signals from the * pthread kernel pipe: */ while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr, sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) { /* * The buffer read contains one byte per signal and * each byte is the signal number. */ for (i = 0; i < num; i++) { if ((int) bufr[i] == _SCHED_SIGNAL) { /* * Scheduling signals shouldn't ever be * queued; just ignore it for now. */ } else { /* Handle this signal: */ _thread_sig_handle((int) bufr[i], NULL); } } } if ((num < 0) && (errno != EAGAIN)) { /* * The only error we should expect is if there is * no data to read. */ PANIC("Unable to read from thread kernel pipe"); } } static inline void thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in) { pthread_t tid_out = thread_out; pthread_t tid_in = thread_in; if ((tid_out != NULL) && ((tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0)) tid_out = NULL; if ((tid_in != NULL) && ((tid_in->flags & PTHREAD_FLAGS_PRIVATE) != 0)) tid_in = NULL; if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) { /* Run the scheduler switch hook: */ _sched_switch_hook(tid_out, tid_in); } } struct pthread * _get_curthread(void) { if (_thread_initial == NULL) _thread_init(); return (_thread_run); } void _set_curthread(struct pthread *newthread) { _thread_run = newthread; } #endif