--- SheepShaver/src/timer.cpp 2005/03/15 20:46:50 1.5 +++ SheepShaver/src/timer.cpp 2009/08/17 20:44:30 1.12 @@ -1,7 +1,7 @@ /* * timer.cpp - Time Manager emulation * - * SheepShaver (C) 1997-2005 Christian Bauer and Marc Hellwig + * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * TODO: Prime(0) - */ - #include "sysdeps.h" #include "timer.h" #include "macos_util.h" @@ -33,6 +29,10 @@ #include #endif +#ifdef PRECISE_TIMING_MACH +#include +#endif + #define DEBUG 0 #include "debug.h" @@ -74,7 +74,16 @@ static bool timer_thread_active = false; static volatile bool timer_thread_cancel = false; static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 }; static tm_time_t wakeup_time = wakeup_time_max; -static sem_t wakeup_time_sem; +static pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER; +static void *timer_func(void *arg); +#endif +#ifdef PRECISE_TIMING_MACH +static clock_serv_t system_clock; +static thread_act_t timer_thread; +static bool timer_thread_active = false; +static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 }; +static tm_time_t wakeup_time = wakeup_time_max; +static semaphore_t wakeup_time_sem; static void *timer_func(void *arg); #endif #endif @@ -185,7 +194,8 @@ static void sigresume_handler(int sig) static bool timer_thread_init(void) { // Install suspend signal handler - sigfillset(&sigsuspend_action.sa_mask); + sigemptyset(&sigsuspend_action.sa_mask); + sigaddset(&sigsuspend_action.sa_mask, SIGRESUME); sigsuspend_action.sa_handler = sigsuspend_handler; sigsuspend_action.sa_flags = SA_RESTART; #ifdef HAVE_SIGNAL_SA_RESTORER @@ -195,7 +205,7 @@ static bool timer_thread_init(void) return false; // Install resume signal handler - sigfillset(&sigresume_action.sa_mask); + sigemptyset(&sigresume_action.sa_mask); sigresume_action.sa_handler = sigresume_handler; sigresume_action.sa_flags = SA_RESTART; #ifdef HAVE_SIGNAL_SA_RESTORER @@ -271,9 +281,15 @@ void TimerInit(void) wakeup_time_sem = create_sem(1, "Wakeup Time"); timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL); resume_thread(timer_thread); +#elif PRECISE_TIMING_MACH + pthread_t pthread; + + host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &system_clock); + semaphore_create(mach_task_self(), &wakeup_time_sem, SYNC_POLICY_FIFO, 1); + + pthread_create(&pthread, NULL, &timer_func, NULL); #endif #ifdef PRECISE_TIMING_POSIX - sem_init(&wakeup_time_sem, 0, 1); timer_thread_active = timer_thread_init(); #endif #endif @@ -297,9 +313,12 @@ void TimerExit(void) wait_for_thread(timer_thread, &l); delete_sem(wakeup_time_sem); #endif +#ifdef PRECISE_TIMING_MACH + timer_thread_active = false; + semaphore_destroy(mach_task_self(), wakeup_time_sem); +#endif #ifdef PRECISE_TIMING_POSIX timer_thread_kill(); - sem_destroy(&wakeup_time_sem); #endif } #endif @@ -357,9 +376,13 @@ int16 RmvTime(uint32 tm) while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#ifdef PRECISE_TIMING_MACH + semaphore_wait(wakeup_time_sem); + thread_suspend(timer_thread); +#endif #if PRECISE_TIMING_POSIX - sem_wait(&wakeup_time_sem); timer_thread_suspend(); + pthread_mutex_lock(&wakeup_time_lock); #endif if (ReadMacInt16(tm + qType) & 0x8000) { @@ -392,8 +415,13 @@ int16 RmvTime(uint32 tm) get_thread_info(timer_thread, &info); } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) #endif +#ifdef PRECISE_TIMING_MACH + semaphore_signal(wakeup_time_sem); + thread_abort(timer_thread); + thread_resume(timer_thread); +#endif #if PRECISE_TIMING_POSIX - sem_post(&wakeup_time_sem); + pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume(); assert(suspend_count == 0); #endif @@ -429,11 +457,12 @@ int16 PrimeTime(uint32 tm, int32 time) // Yes, tmWakeUp set? if (ReadMacInt32(tm + tmWakeUp)) { - //!! PrimeTime(0) means continue previous delay - // (save wakeup time in RmvTime?) + // PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled" + // or (b) "continue previous delay" if an expired task was stopped via RmvTime() and + // then re-installed using InsXTime(). Since tmWakeUp was set, this is case (b). + // The remaining time was saved in tmCount by RmvTime(). if (time == 0) { - printf("FATAL: Unsupported PrimeTime(0)\n"); - return 0; + timer_mac2host_time(delay, ReadMacInt16(tm + tmCount)); } // Yes, calculate wakeup time relative to last scheduled time @@ -465,9 +494,13 @@ int16 PrimeTime(uint32 tm, int32 time) while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#ifdef PRECISE_TIMING_MACH + semaphore_wait(wakeup_time_sem); + thread_suspend(timer_thread); +#endif #if PRECISE_TIMING_POSIX - sem_wait(&wakeup_time_sem); timer_thread_suspend(); + pthread_mutex_lock(&wakeup_time_lock); #endif WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000); enqueue_tm(tm); @@ -487,8 +520,13 @@ int16 PrimeTime(uint32 tm, int32 time) get_thread_info(timer_thread, &info); } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) #endif +#ifdef PRECISE_TIMING_MACH + semaphore_signal(wakeup_time_sem); + thread_abort(timer_thread); + thread_resume(timer_thread); +#endif #ifdef PRECISE_TIMING_POSIX - sem_post(&wakeup_time_sem); + pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume(); assert(suspend_count == 0); #endif @@ -523,25 +561,47 @@ static int32 timer_func(void *arg) } #endif +#ifdef PRECISE_TIMING_MACH +static void *timer_func(void *arg) +{ + timer_thread = mach_thread_self(); + timer_thread_active = true; + + while (timer_thread_active) { + clock_sleep(system_clock, TIME_ABSOLUTE, wakeup_time, NULL); + semaphore_wait(wakeup_time_sem); + + tm_time_t system_time; + + timer_current_time(system_time); + if (timer_cmp_time(wakeup_time, system_time) < 0) { + wakeup_time = wakeup_time_max; + SetInterruptFlag(INTFLAG_TIMER); + TriggerInterrupt(); + } + semaphore_signal(wakeup_time_sem); + } +} +#endif + #ifdef PRECISE_TIMING_POSIX static void *timer_func(void *arg) { while (!timer_thread_cancel) { - // Wait until time specified by wakeup_time clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL); - sem_wait(&wakeup_time_sem); tm_time_t system_time; timer_current_time(system_time); if (timer_cmp_time(wakeup_time, system_time) < 0) { // Timer expired, trigger interrupt + pthread_mutex_lock(&wakeup_time_lock); wakeup_time = wakeup_time_max; + pthread_mutex_unlock(&wakeup_time_lock); SetInterruptFlag(INTFLAG_TIMER); TriggerInterrupt(); } - sem_post(&wakeup_time_sem); } return NULL; } @@ -587,9 +647,13 @@ void TimerInterrupt(void) while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#if PRECISE_TIMING_MACH + semaphore_wait(wakeup_time_sem); + thread_suspend(timer_thread); +#endif #if PRECISE_TIMING_POSIX - sem_wait(&wakeup_time_sem); timer_thread_suspend(); + pthread_mutex_lock(&wakeup_time_lock); #endif wakeup_time = wakeup_time_max; for (int j=0; j