mirror of
https://github.com/kanjitalk755/macemu.git
synced 2025-02-18 12:30:33 +00:00
Enable high precision timings on POSIX systems supporting clock_nanosleep().
Since pthread_suspend_np() is not available to Linux (but NetBSD 2.0), thread suspend is implemented likewise to boehm-gc.
This commit is contained in:
parent
48213aa5eb
commit
e7d8a54e21
@ -42,6 +42,10 @@
|
|||||||
#undef WORDS_BIGENDIAN
|
#undef WORDS_BIGENDIAN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// High precision timing
|
||||||
|
#define PRECISE_TIMING 1
|
||||||
|
#define PRECISE_TIMING_BEOS 1
|
||||||
|
|
||||||
#define POWERPC_ROM 1
|
#define POWERPC_ROM 1
|
||||||
|
|
||||||
// Time data type for Time Manager emulation
|
// Time data type for Time Manager emulation
|
||||||
|
@ -318,7 +318,7 @@ no:linux*)
|
|||||||
;;
|
;;
|
||||||
*:*)
|
*:*)
|
||||||
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
|
AC_SEARCH_LIBS(clock_gettime, [rt posix4])
|
||||||
AC_CHECK_FUNCS(clock_gettime)
|
AC_CHECK_FUNCS(clock_gettime clock_nanosleep)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
@ -410,6 +410,12 @@ typedef struct timespec tm_time_t;
|
|||||||
typedef struct timeval tm_time_t;
|
typedef struct timeval tm_time_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// High-precision timing
|
||||||
|
#if defined(HAVE_PTHREADS) && defined(HAVE_CLOCK_NANOSLEEP)
|
||||||
|
#define PRECISE_TIMING 1
|
||||||
|
#define PRECISE_TIMING_POSIX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// Timing functions
|
// Timing functions
|
||||||
extern uint64 GetTicks_usec(void);
|
extern uint64 GetTicks_usec(void);
|
||||||
extern void Delay_usec(uint32 usec);
|
extern void Delay_usec(uint32 usec);
|
||||||
|
@ -51,13 +51,6 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
|
||||||
#if __BEOS__
|
|
||||||
#define PRECISE_TIMING 1
|
|
||||||
#else
|
|
||||||
#define PRECISE_TIMING 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// TVector of MakeExecutable
|
// TVector of MakeExecutable
|
||||||
static uint32 MakeExecutableTvec;
|
static uint32 MakeExecutableTvec;
|
||||||
|
|
||||||
|
@ -28,16 +28,15 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "cpu_emulation.h"
|
#include "cpu_emulation.h"
|
||||||
|
|
||||||
|
#ifdef PRECISE_TIMING_POSIX
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DEBUG 0
|
#define DEBUG 0
|
||||||
#include "debug.h"
|
#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)
|
#define TM_QUEUE 0 // Enable TMQueue management (doesn't work)
|
||||||
|
|
||||||
|
|
||||||
@ -61,12 +60,23 @@ const int NUM_DESCS = 64; // Maximum number of descriptors
|
|||||||
static TMDesc desc[NUM_DESCS];
|
static TMDesc desc[NUM_DESCS];
|
||||||
|
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING
|
||||||
|
#ifdef PRECISE_TIMING_BEOS
|
||||||
static thread_id timer_thread = -1;
|
static thread_id timer_thread = -1;
|
||||||
static bool thread_active = true;
|
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 sem_id wakeup_time_sem = -1;
|
||||||
static int32 timer_func(void *arg);
|
static int32 timer_func(void *arg);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef PRECISE_TIMING_POSIX
|
||||||
|
static pthread_t timer_thread;
|
||||||
|
static volatile bool thread_active = 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 void *timer_func(void *arg);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -142,6 +152,104 @@ 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;
|
||||||
|
|
||||||
|
// Signal handler for suspended thread
|
||||||
|
static void sigsuspend_handler(int sig)
|
||||||
|
{
|
||||||
|
sem_post(&suspend_ack_sem);
|
||||||
|
|
||||||
|
sigset_t mask;
|
||||||
|
sigfillset(&mask);
|
||||||
|
sigdelset(&mask, SIGRESUME);
|
||||||
|
sigsuspend(&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
|
||||||
|
sigfillset(&sigsuspend_action.sa_mask);
|
||||||
|
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
|
||||||
|
sigfillset(&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;
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
#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
|
* Initialize Time Manager
|
||||||
*/
|
*/
|
||||||
@ -154,10 +262,16 @@ void TimerInit(void)
|
|||||||
|
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING
|
||||||
// Start timer thread
|
// Start timer thread
|
||||||
|
#ifdef PRECISE_TIMING_BEOS
|
||||||
wakeup_time_sem = create_sem(1, "Wakeup Time");
|
wakeup_time_sem = create_sem(1, "Wakeup Time");
|
||||||
timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
|
timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
|
||||||
resume_thread(timer_thread);
|
resume_thread(timer_thread);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef PRECISE_TIMING_POSIX
|
||||||
|
sem_init(&wakeup_time_sem, 0, 1);
|
||||||
|
thread_active = timer_thread_init();
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -170,12 +284,19 @@ void TimerExit(void)
|
|||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING
|
||||||
// Quit timer thread
|
// Quit timer thread
|
||||||
if (timer_thread > 0) {
|
if (timer_thread > 0) {
|
||||||
|
#ifdef PRECISE_TIMING_BEOS
|
||||||
status_t l;
|
status_t l;
|
||||||
thread_active = false;
|
thread_active = false;
|
||||||
suspend_thread(timer_thread);
|
suspend_thread(timer_thread);
|
||||||
resume_thread(timer_thread);
|
resume_thread(timer_thread);
|
||||||
wait_for_thread(timer_thread, &l);
|
wait_for_thread(timer_thread, &l);
|
||||||
delete_sem(wakeup_time_sem);
|
delete_sem(wakeup_time_sem);
|
||||||
|
#endif
|
||||||
|
#ifdef PRECISE_TIMING_POSIX
|
||||||
|
thread_active = false;
|
||||||
|
timer_thread_kill();
|
||||||
|
sem_destroy(&wakeup_time_sem);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -228,9 +349,13 @@ int16 RmvTime(uint32 tm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Task active?
|
// Task active?
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING_BEOS
|
||||||
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
||||||
suspend_thread(timer_thread);
|
suspend_thread(timer_thread);
|
||||||
|
#endif
|
||||||
|
#if PRECISE_TIMING_POSIX
|
||||||
|
sem_wait(&wakeup_time_sem);
|
||||||
|
timer_thread_suspend();
|
||||||
#endif
|
#endif
|
||||||
if (ReadMacInt16(tm + qType) & 0x8000) {
|
if (ReadMacInt16(tm + qType) & 0x8000) {
|
||||||
|
|
||||||
@ -239,10 +364,10 @@ int16 RmvTime(uint32 tm)
|
|||||||
dequeue_tm(tm);
|
dequeue_tm(tm);
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING
|
||||||
// Look for next task to be called and set wakeup_time
|
// Look for next task to be called and set wakeup_time
|
||||||
wakeup_time = 0x7fffffffffffffff;
|
wakeup_time = wakeup_time_max;
|
||||||
for (int j=0; j<NUM_DESCS; j++) {
|
for (int j=0; j<NUM_DESCS; j++) {
|
||||||
if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
|
if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
|
||||||
if (desc[j].wakeup < wakeup_time)
|
if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
|
||||||
wakeup_time = desc[j].wakeup;
|
wakeup_time = desc[j].wakeup;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -255,7 +380,7 @@ int16 RmvTime(uint32 tm)
|
|||||||
} else
|
} else
|
||||||
WriteMacInt32(tm + tmCount, 0);
|
WriteMacInt32(tm + tmCount, 0);
|
||||||
D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
|
D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING_BEOS
|
||||||
release_sem(wakeup_time_sem);
|
release_sem(wakeup_time_sem);
|
||||||
thread_info info;
|
thread_info info;
|
||||||
do {
|
do {
|
||||||
@ -263,6 +388,11 @@ int16 RmvTime(uint32 tm)
|
|||||||
get_thread_info(timer_thread, &info);
|
get_thread_info(timer_thread, &info);
|
||||||
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
||||||
#endif
|
#endif
|
||||||
|
#if PRECISE_TIMING_POSIX
|
||||||
|
sem_post(&wakeup_time_sem);
|
||||||
|
timer_thread_resume();
|
||||||
|
assert(suspend_count == 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Free descriptor
|
// Free descriptor
|
||||||
free_desc(i);
|
free_desc(i);
|
||||||
@ -327,36 +457,47 @@ int16 PrimeTime(uint32 tm, int32 time)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make task active and enqueue it in the Time Manager queue
|
// Make task active and enqueue it in the Time Manager queue
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING_BEOS
|
||||||
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
||||||
suspend_thread(timer_thread);
|
suspend_thread(timer_thread);
|
||||||
|
#endif
|
||||||
|
#if PRECISE_TIMING_POSIX
|
||||||
|
sem_wait(&wakeup_time_sem);
|
||||||
|
timer_thread_suspend();
|
||||||
#endif
|
#endif
|
||||||
WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
|
WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
|
||||||
enqueue_tm(tm);
|
enqueue_tm(tm);
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING
|
||||||
// Look for next task to be called and set wakeup_time
|
// Look for next task to be called and set wakeup_time
|
||||||
wakeup_time = 0x7fffffffffffffff;
|
wakeup_time = wakeup_time_max;
|
||||||
for (int j=0; j<NUM_DESCS; j++) {
|
for (int j=0; j<NUM_DESCS; j++) {
|
||||||
if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
|
if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
|
||||||
if (desc[j].wakeup < wakeup_time)
|
if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
|
||||||
wakeup_time = desc[j].wakeup;
|
wakeup_time = desc[j].wakeup;
|
||||||
}
|
}
|
||||||
|
#ifdef PRECISE_TIMING_BEOS
|
||||||
release_sem(wakeup_time_sem);
|
release_sem(wakeup_time_sem);
|
||||||
thread_info info;
|
thread_info info;
|
||||||
do {
|
do {
|
||||||
resume_thread(timer_thread); // This will unblock the thread
|
resume_thread(timer_thread); // This will unblock the thread
|
||||||
get_thread_info(timer_thread, &info);
|
get_thread_info(timer_thread, &info);
|
||||||
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
||||||
|
#endif
|
||||||
|
#ifdef PRECISE_TIMING_POSIX
|
||||||
|
sem_post(&wakeup_time_sem);
|
||||||
|
timer_thread_resume();
|
||||||
|
assert(suspend_count == 0);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if PRECISE_TIMING
|
|
||||||
/*
|
/*
|
||||||
* Time Manager thread
|
* Time Manager thread
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef PRECISE_TIMING_BEOS
|
||||||
static int32 timer_func(void *arg)
|
static int32 timer_func(void *arg)
|
||||||
{
|
{
|
||||||
while (thread_active) {
|
while (thread_active) {
|
||||||
@ -378,6 +519,30 @@ static int32 timer_func(void *arg)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef PRECISE_TIMING_POSIX
|
||||||
|
static void *timer_func(void *arg)
|
||||||
|
{
|
||||||
|
while (thread_active) {
|
||||||
|
|
||||||
|
// 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
|
||||||
|
wakeup_time = wakeup_time_max;
|
||||||
|
SetInterruptFlag(INTFLAG_TIMER);
|
||||||
|
TriggerInterrupt();
|
||||||
|
}
|
||||||
|
sem_post(&wakeup_time_sem);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Timer interrupt function (executed as part of 60Hz interrupt)
|
* Timer interrupt function (executed as part of 60Hz interrupt)
|
||||||
@ -414,14 +579,21 @@ void TimerInterrupt(void)
|
|||||||
|
|
||||||
#if PRECISE_TIMING
|
#if PRECISE_TIMING
|
||||||
// Look for next task to be called and set wakeup_time
|
// Look for next task to be called and set wakeup_time
|
||||||
|
#if PRECISE_TIMING_BEOS
|
||||||
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
||||||
suspend_thread(timer_thread);
|
suspend_thread(timer_thread);
|
||||||
wakeup_time = 0x7fffffffffffffff;
|
#endif
|
||||||
|
#if PRECISE_TIMING_POSIX
|
||||||
|
sem_wait(&wakeup_time_sem);
|
||||||
|
timer_thread_suspend();
|
||||||
|
#endif
|
||||||
|
wakeup_time = wakeup_time_max;
|
||||||
for (int j=0; j<NUM_DESCS; j++) {
|
for (int j=0; j<NUM_DESCS; j++) {
|
||||||
if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
|
if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
|
||||||
if (desc[j].wakeup < wakeup_time)
|
if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
|
||||||
wakeup_time = desc[j].wakeup;
|
wakeup_time = desc[j].wakeup;
|
||||||
}
|
}
|
||||||
|
#if PRECISE_TIMING_BEOS
|
||||||
release_sem(wakeup_time_sem);
|
release_sem(wakeup_time_sem);
|
||||||
thread_info info;
|
thread_info info;
|
||||||
do {
|
do {
|
||||||
@ -429,4 +601,10 @@ void TimerInterrupt(void)
|
|||||||
get_thread_info(timer_thread, &info);
|
get_thread_info(timer_thread, &info);
|
||||||
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
||||||
#endif
|
#endif
|
||||||
|
#if PRECISE_TIMING_POSIX
|
||||||
|
sem_post(&wakeup_time_sem);
|
||||||
|
timer_thread_resume();
|
||||||
|
assert(suspend_count == 0);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user