--- SheepShaver/src/timer.cpp 2004/01/12 15:37:19 1.2 +++ SheepShaver/src/timer.cpp 2005/07/01 23:15:11 1.7 @@ -1,7 +1,7 @@ /* * timer.cpp - Time Manager emulation * - * SheepShaver (C) 1997-2004 Christian Bauer and Marc Hellwig + * SheepShaver (C) 1997-2005 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 @@ -28,16 +28,15 @@ #include "main.h" #include "cpu_emulation.h" +#ifdef PRECISE_TIMING_POSIX +#include +#include +#endif + #define DEBUG 0 #include "debug.h" -#if __BEOS__ -#define PRECISE_TIMING 1 -#else -#define PRECISE_TIMING 0 -#endif - #define TM_QUEUE 0 // Enable TMQueue management (doesn't work) @@ -61,12 +60,24 @@ const int NUM_DESCS = 64; // Maximum nu static TMDesc desc[NUM_DESCS]; #if PRECISE_TIMING +#ifdef PRECISE_TIMING_BEOS static thread_id timer_thread = -1; static bool thread_active = true; -static volatile tm_time_t wakeup_time = 0x7fffffffffffffff; +static const tm_time_t wakeup_time_max = 0x7fffffffffffffff; +static volatile tm_time_t wakeup_time = wakeup_time_max; static sem_id wakeup_time_sem = -1; static int32 timer_func(void *arg); #endif +#ifdef PRECISE_TIMING_POSIX +static pthread_t timer_thread; +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 pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER; +static void *timer_func(void *arg); +#endif +#endif /* @@ -143,6 +154,109 @@ static void dequeue_tm(uint32 tm) /* + * Timer thread operations + */ + +#ifdef PRECISE_TIMING_POSIX +const int SIGSUSPEND = SIGRTMIN + 6; +const int SIGRESUME = SIGRTMIN + 7; +static struct sigaction sigsuspend_action; +static struct sigaction sigresume_action; + +static int suspend_count = 0; +static pthread_mutex_t suspend_count_lock = PTHREAD_MUTEX_INITIALIZER; +static sem_t suspend_ack_sem; +static sigset_t suspend_handler_mask; + +// Signal handler for suspended thread +static void sigsuspend_handler(int sig) +{ + sem_post(&suspend_ack_sem); + sigsuspend(&suspend_handler_mask); +} + +// Signal handler for resumed thread +static void sigresume_handler(int sig) +{ + /* simply trigger a signal to stop clock_nanosleep() */ +} + +// Initialize timer thread +static bool timer_thread_init(void) +{ + // Install suspend signal handler + 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 + sigsuspend_action.sa_restorer = NULL; +#endif + if (sigaction(SIGSUSPEND, &sigsuspend_action, NULL) < 0) + return false; + + // Install resume signal handler + sigemptyset(&sigresume_action.sa_mask); + sigresume_action.sa_handler = sigresume_handler; + sigresume_action.sa_flags = SA_RESTART; +#ifdef HAVE_SIGNAL_SA_RESTORER + sigresume_action.sa_restorer = NULL; +#endif + if (sigaction(SIGRESUME, &sigresume_action, NULL) < 0) + return false; + + // Initialize semaphore + if (sem_init(&suspend_ack_sem, 0, 0) < 0) + return false; + + // Initialize suspend_handler_mask, it excludes SIGRESUME + if (sigfillset(&suspend_handler_mask) != 0) + return false; + if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0) + return false; + + // Create thread in running state + suspend_count = 0; + return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0); +} + +// Kill timer thread +static void timer_thread_kill(void) +{ + timer_thread_cancel = true; +#ifdef HAVE_PTHREAD_CANCEL + pthread_cancel(timer_thread); +#endif + pthread_join(timer_thread, NULL); +} + +// Suspend timer thread +static void timer_thread_suspend(void) +{ + pthread_mutex_lock(&suspend_count_lock); + if (suspend_count == 0) { + suspend_count ++; + if (pthread_kill(timer_thread, SIGSUSPEND) == 0) + sem_wait(&suspend_ack_sem); + } + pthread_mutex_unlock(&suspend_count_lock); +} + +// Resume timer thread +static void timer_thread_resume(void) +{ + pthread_mutex_lock(&suspend_count_lock); + assert(suspend_count > 0); + if (suspend_count == 1) { + suspend_count = 0; + pthread_kill(timer_thread, SIGRESUME); + } + pthread_mutex_unlock(&suspend_count_lock); +} +#endif + + +/* * Initialize Time Manager */ @@ -154,10 +268,15 @@ void TimerInit(void) #if PRECISE_TIMING // Start timer thread +#ifdef PRECISE_TIMING_BEOS 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); #endif +#ifdef PRECISE_TIMING_POSIX + timer_thread_active = timer_thread_init(); +#endif +#endif } @@ -170,12 +289,17 @@ void TimerExit(void) #if PRECISE_TIMING // Quit timer thread if (timer_thread > 0) { +#ifdef PRECISE_TIMING_BEOS status_t l; thread_active = false; suspend_thread(timer_thread); resume_thread(timer_thread); wait_for_thread(timer_thread, &l); delete_sem(wakeup_time_sem); +#endif +#ifdef PRECISE_TIMING_POSIX + timer_thread_kill(); +#endif } #endif } @@ -228,10 +352,14 @@ int16 RmvTime(uint32 tm) } // Task active? -#if PRECISE_TIMING +#if PRECISE_TIMING_BEOS while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ; suspend_thread(timer_thread); #endif +#if PRECISE_TIMING_POSIX + timer_thread_suspend(); + pthread_mutex_lock(&wakeup_time_lock); +#endif if (ReadMacInt16(tm + qType) & 0x8000) { // Yes, make task inactive and remove it from the Time Manager queue @@ -239,10 +367,10 @@ int16 RmvTime(uint32 tm) dequeue_tm(tm); #if PRECISE_TIMING // Look for next task to be called and set wakeup_time - wakeup_time = 0x7fffffffffffffff; + wakeup_time = wakeup_time_max; for (int j=0; j