--- SheepShaver/src/timer.cpp 2005/07/01 23:15:11 1.7 +++ 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" @@ -77,6 +77,15 @@ static tm_time_t wakeup_time = wakeup_ti 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 @@ -272,6 +281,13 @@ 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 timer_thread_active = timer_thread_init(); @@ -297,6 +313,10 @@ 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(); #endif @@ -356,6 +376,10 @@ 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 timer_thread_suspend(); pthread_mutex_lock(&wakeup_time_lock); @@ -391,6 +415,11 @@ 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 pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume(); @@ -428,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 @@ -464,6 +494,10 @@ 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 timer_thread_suspend(); pthread_mutex_lock(&wakeup_time_lock); @@ -486,6 +520,11 @@ 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 pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume(); @@ -522,11 +561,33 @@ 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); @@ -586,6 +647,10 @@ 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 timer_thread_suspend(); pthread_mutex_lock(&wakeup_time_lock); @@ -604,6 +669,11 @@ void TimerInterrupt(void) get_thread_info(timer_thread, &info); } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) #endif +#if PRECISE_TIMING_MACH + semaphore_signal(wakeup_time_sem); + thread_abort(timer_thread); + thread_resume(timer_thread); +#endif #if PRECISE_TIMING_POSIX pthread_mutex_unlock(&wakeup_time_lock); timer_thread_resume();