sleep.c
上传用户:tsgydb
上传日期:2007-04-14
资源大小:10674k
文件大小:11k
- /* ==== 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: %ldn
- current: tv_sec: %ld tv_nsec: %ldn
- argument: tv_sec: %ld tv_usec: %ldn",
- 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);
- }