diff options
Diffstat (limited to 'mit-pthreads/pthreads/sleep.c')
-rw-r--r-- | mit-pthreads/pthreads/sleep.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/mit-pthreads/pthreads/sleep.c b/mit-pthreads/pthreads/sleep.c new file mode 100644 index 00000000000..1c13dd2eb1d --- /dev/null +++ b/mit-pthreads/pthreads/sleep.c @@ -0,0 +1,367 @@ +/* ==== sleep.c ============================================================ + * Copyright (c) 1993, 1994 by Chris Provenzano, proven@mit.edu + * 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 Chris Provenzano. + * 4. The name of Chris Provenzano may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``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 CHRIS PROVENZANO 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. + * + * Description : All the appropriate sleep routines. + * + * 1.00 93/12/28 proven + * -Started coding this file. + * + * 1.36 94/06/04 proven + * -Use new timer structure pthread_timer, that uses seconds + * -nano seconds. Rewrite all routines completely. + * + * 1.38 94/06/13 proven + * -switch pthread_timer to timespec + */ + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +#include <pthread.h> +#include <stdio.h> +#include <errno.h> +#include <sys/time.h> +#include <signal.h> +#include <unistd.h> +#include <sys/compat.h> + +struct pthread * pthread_sleep = NULL; + +/* ========================================================================== + * sleep_compare_time() + */ +/* static inline int sleep_compare_time(struct timespec * time1, + struct timespec * time2) */ +static int sleep_compare_time(struct timespec * time1, struct timespec * time2) +{ + if ((time1->tv_sec < time2->tv_sec) || + ((time1->tv_sec == time2->tv_sec) && (time1->tv_nsec < time2->tv_nsec))) { + return(-1); + } + if ((time1->tv_sec == time2->tv_sec) && (time1->tv_nsec == time2->tv_nsec)){ + return(0); + } + return(1); +} + +/* ========================================================================== + * machdep_stop_timer() + * + * Returns the time left on the timer. + */ +static struct itimerval timestop = { { 0, 0 }, { 0, 0 } }; + +void machdep_stop_timer(struct timespec *current) +{ + struct itimerval timenow; + + setitimer(ITIMER_REAL, & timestop, & timenow); + __pthread_signal_delete(SIGALRM); + if (current) { + current->tv_nsec = timenow.it_value.tv_usec * 1000; + current->tv_sec = timenow.it_value.tv_sec; + } +} + +/* ========================================================================== + * machdep_start_timer() + */ +int machdep_start_timer(struct timespec *current, struct timespec *wakeup) +{ + struct itimerval timeout; + + timeout.it_value.tv_usec = (wakeup->tv_nsec - current->tv_nsec) / 1000; + timeout.it_value.tv_sec = wakeup->tv_sec - current->tv_sec; + timeout.it_interval.tv_usec = 0; + timeout.it_interval.tv_sec = 0; + if (timeout.it_value.tv_usec < 0) { + timeout.it_value.tv_usec += 1000000; + timeout.it_value.tv_sec--; + } + + if (((long) timeout.it_value.tv_sec >= 0) && + ((timeout.it_value.tv_usec) || (timeout.it_value.tv_sec))) { + if (setitimer(ITIMER_REAL, & timeout, NULL) < 0) + { + fprintf(stderr,"Got error %d from setitimer with:\n\ + wakeup: tv_sec: %ld tv_nsec: %ld\n\ + current: tv_sec: %ld tv_nsec: %ld\n\ + argument: tv_sec: %ld tv_usec: %ld\n", + errno, + wakeup->tv_sec, wakeup->tv_nsec, + current->tv_sec, current->tv_nsec, + timeout.it_value.tv_sec, timeout.it_value.tv_usec); + PANIC(); + } + } else { + /* + * There is no time on the timer. + * This shouldn't happen, + * but isn't fatal. + */ + sig_handler_fake(SIGALRM); + } + return(OK); +} + +/* ========================================================================== + * sleep_schedule() + * + * Assumes that the current thread is the thread to be scheduled + * and that the kthread is already locked. + */ +void sleep_schedule(struct timespec *current_time, struct timespec *new_time) +{ + struct pthread * pthread_sleep_current, * pthread_sleep_prev; + + /* Record the new time as the current thread's wakeup time. */ + pthread_run->wakeup_time = *new_time; + + /* any threads? */ + if (pthread_sleep_current = pthread_sleep) { + if (sleep_compare_time(&(pthread_sleep_current->wakeup_time), + new_time) <= 0) { + /* Don't need to restart timer */ + while (pthread_sleep_current->sll) { + + pthread_sleep_prev = pthread_sleep_current; + pthread_sleep_current = pthread_sleep_current->sll; + + if (sleep_compare_time(&(pthread_sleep_current->wakeup_time), + new_time) > 0) { + pthread_run->sll = pthread_sleep_current; + pthread_sleep_prev->sll = pthread_run; + return; + } + } + + /* No more threads in queue, attach pthread_run to end of list */ + pthread_sleep_current->sll = pthread_run; + pthread_run->sll = NULL; + + } else { + /* Start timer and enqueue thread */ + machdep_start_timer(current_time, new_time); + pthread_run->sll = pthread_sleep_current; + pthread_sleep = pthread_run; + } + } else { + /* Start timer and enqueue thread */ + machdep_start_timer(current_time, new_time); + pthread_sleep = pthread_run; + pthread_run->sll = NULL; + } +} + +/* ========================================================================== + * sleep_wakeup() + * + * This routine is called by the interrupt handler, which has already + * locked the current kthread. Since all threads on this list are owned + * by the current kthread, rescheduling won't be a problem. + */ +int sleep_spurious_wakeup = 0; +int sleep_wakeup() +{ + struct pthread *pthread_sleep_next; + struct timespec current_time; + int ret = 0; + + if (pthread_sleep == NULL) { + return(NOTOK); + } + + machdep_gettimeofday(¤t_time); + if (sleep_compare_time(&(pthread_sleep->wakeup_time), ¤t_time) > 0) { + machdep_start_timer(¤t_time, &(pthread_sleep->wakeup_time)); + sleep_spurious_wakeup++; + return(OK); + } + + do { + if (pthread_sleep->pthread_priority > ret) { + ret = pthread_sleep->pthread_priority; + } + + /* + * Clean up removed thread and start it running again. + * + * Note: It is VERY important to remove the thread form the + * current queue before putting it on the run queue. + * Both queues use pthread_sleep->next, and the thread that points + * to pthread_sleep should point to pthread_sleep->next then + * pthread_sleep should be put on the run queue. + */ + if ((SET_PF_DONE_EVENT(pthread_sleep)) == OK) { + if (pthread_sleep->queue) + pthread_queue_remove(pthread_sleep->queue, pthread_sleep); + pthread_prio_queue_enq(pthread_current_prio_queue, pthread_sleep); + pthread_sleep->state = PS_RUNNING; + } + + pthread_sleep_next = pthread_sleep->sll; + pthread_sleep->sll = NULL; + + if ((pthread_sleep = pthread_sleep_next) == NULL) { + /* No more threads on sleep queue */ + return(ret); + } + } while (sleep_compare_time(&(pthread_sleep->wakeup_time), &(current_time)) <= 0); + + /* Start timer for next time interval */ + machdep_start_timer(¤t_time, &(pthread_sleep->wakeup_time)); + return(ret); +} + + +/* ========================================================================== + * __sleep() + */ +void __sleep(struct timespec * time_to_sleep) +{ + struct pthread *pthread_sleep_prev; + struct timespec current_time, wakeup_time; + + pthread_sched_prevent(); + + /* Get real time */ + machdep_gettimeofday(¤t_time); + wakeup_time.tv_sec = current_time.tv_sec + time_to_sleep->tv_sec; + wakeup_time.tv_nsec = current_time.tv_nsec + time_to_sleep->tv_nsec; + + sleep_schedule(¤t_time, &wakeup_time); + + /* Reschedule thread */ + SET_PF_WAIT_EVENT(pthread_run); + SET_PF_AT_CANCEL_POINT(pthread_run); /* This is a cancel point */ + pthread_resched_resume(PS_SLEEP_WAIT); + CLEAR_PF_AT_CANCEL_POINT(pthread_run); /* No longer at cancel point */ + CLEAR_PF_DONE_EVENT(pthread_run); + + /* Return actual time slept */ + time_to_sleep->tv_sec = pthread_run->wakeup_time.tv_sec; + time_to_sleep->tv_nsec = pthread_run->wakeup_time.tv_nsec; +} + +/* ========================================================================== + * pthread_nanosleep() + */ +unsigned int pthread_nanosleep(unsigned int nseconds) +{ + struct timespec time_to_sleep; + + if (nseconds) { + time_to_sleep.tv_nsec = nseconds; + time_to_sleep.tv_sec = 0; + __sleep(&time_to_sleep); + nseconds = time_to_sleep.tv_nsec; + } + return(nseconds); +} + +/* ========================================================================== + * usleep() + */ +void usleep(unsigned int useconds) +{ + struct timespec time_to_sleep; + + if (useconds) { + time_to_sleep.tv_nsec = (useconds % 1000000) * 1000; + time_to_sleep.tv_sec = useconds / 1000000; + __sleep(&time_to_sleep); + } +} + +/* ========================================================================== + * sleep() + */ +unsigned int sleep(unsigned int seconds) +{ + struct timespec time_to_sleep; + + if (seconds) { + time_to_sleep.tv_sec = seconds; + time_to_sleep.tv_nsec = 0; + __sleep(&time_to_sleep); + seconds = time_to_sleep.tv_sec; + } + return(seconds); +} + +/* ========================================================================== + * sleep_cancel() + * + * Cannot be called while kernel is locked. + * Does not wake sleeping thread up, just remove it from the sleep queue. + */ +int sleep_cancel(struct pthread * pthread) +{ + struct timespec current_time, delta_time; + struct pthread * pthread_last; + int rval = NOTOK; + + /* Lock sleep queue, Note this may be on a different kthread queue */ + pthread_sched_prevent(); + + if (pthread_sleep) { + if (pthread == pthread_sleep) { + rval = OK; + machdep_stop_timer(&delta_time); + if (pthread_sleep = pthread_sleep->sll) { + current_time.tv_sec = delta_time.tv_sec; + current_time.tv_nsec = delta_time.tv_nsec; + current_time.tv_sec += pthread_sleep->wakeup_time.tv_sec; + current_time.tv_nsec += pthread_sleep->wakeup_time.tv_nsec; + while (current_time.tv_nsec > 1000000000) { + current_time.tv_nsec -= 1000000000; + current_time.tv_sec++; + } + machdep_start_timer(&(current_time), + &(pthread_sleep->wakeup_time)); + } + } else { + for (pthread_last = pthread_sleep; pthread_last; + pthread_last = pthread_last->sll) { + if (pthread_last->sll == pthread) { + pthread_last->sll = pthread->sll; + rval = OK; + break; + } + } + } + } + + pthread_sched_resume(); + pthread->sll = NULL; + return(rval); +} |