1 |
|
/* |
2 |
|
* timer.cpp - Time Manager emulation |
3 |
|
* |
4 |
< |
* SheepShaver (C) 1997-2005 Christian Bauer and Marc Hellwig |
4 |
> |
* SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig |
5 |
|
* |
6 |
|
* This program is free software; you can redistribute it and/or modify |
7 |
|
* it under the terms of the GNU General Public License as published by |
70 |
|
#endif |
71 |
|
#ifdef PRECISE_TIMING_POSIX |
72 |
|
static pthread_t timer_thread; |
73 |
< |
static volatile bool thread_active = false; |
73 |
> |
static bool timer_thread_active = false; |
74 |
> |
static volatile bool timer_thread_cancel = false; |
75 |
|
static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 }; |
76 |
|
static tm_time_t wakeup_time = wakeup_time_max; |
77 |
< |
static sem_t wakeup_time_sem; |
77 |
> |
static pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER; |
78 |
|
static void *timer_func(void *arg); |
79 |
|
#endif |
80 |
|
#endif |
166 |
|
static int suspend_count = 0; |
167 |
|
static pthread_mutex_t suspend_count_lock = PTHREAD_MUTEX_INITIALIZER; |
168 |
|
static sem_t suspend_ack_sem; |
169 |
+ |
static sigset_t suspend_handler_mask; |
170 |
|
|
171 |
|
// Signal handler for suspended thread |
172 |
|
static void sigsuspend_handler(int sig) |
173 |
|
{ |
174 |
|
sem_post(&suspend_ack_sem); |
175 |
< |
|
174 |
< |
sigset_t mask; |
175 |
< |
sigfillset(&mask); |
176 |
< |
sigdelset(&mask, SIGRESUME); |
177 |
< |
sigsuspend(&mask); |
175 |
> |
sigsuspend(&suspend_handler_mask); |
176 |
|
} |
177 |
|
|
178 |
|
// Signal handler for resumed thread |
185 |
|
static bool timer_thread_init(void) |
186 |
|
{ |
187 |
|
// Install suspend signal handler |
188 |
< |
sigfillset(&sigsuspend_action.sa_mask); |
188 |
> |
sigemptyset(&sigsuspend_action.sa_mask); |
189 |
> |
sigaddset(&sigsuspend_action.sa_mask, SIGRESUME); |
190 |
|
sigsuspend_action.sa_handler = sigsuspend_handler; |
191 |
|
sigsuspend_action.sa_flags = SA_RESTART; |
192 |
|
#ifdef HAVE_SIGNAL_SA_RESTORER |
196 |
|
return false; |
197 |
|
|
198 |
|
// Install resume signal handler |
199 |
< |
sigfillset(&sigresume_action.sa_mask); |
199 |
> |
sigemptyset(&sigresume_action.sa_mask); |
200 |
|
sigresume_action.sa_handler = sigresume_handler; |
201 |
|
sigresume_action.sa_flags = SA_RESTART; |
202 |
|
#ifdef HAVE_SIGNAL_SA_RESTORER |
209 |
|
if (sem_init(&suspend_ack_sem, 0, 0) < 0) |
210 |
|
return false; |
211 |
|
|
212 |
+ |
// Initialize suspend_handler_mask, it excludes SIGRESUME |
213 |
+ |
if (sigfillset(&suspend_handler_mask) != 0) |
214 |
+ |
return false; |
215 |
+ |
if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0) |
216 |
+ |
return false; |
217 |
+ |
|
218 |
|
// Create thread in running state |
219 |
|
suspend_count = 0; |
220 |
|
return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0); |
223 |
|
// Kill timer thread |
224 |
|
static void timer_thread_kill(void) |
225 |
|
{ |
226 |
+ |
timer_thread_cancel = true; |
227 |
|
#ifdef HAVE_PTHREAD_CANCEL |
228 |
|
pthread_cancel(timer_thread); |
229 |
|
#endif |
274 |
|
resume_thread(timer_thread); |
275 |
|
#endif |
276 |
|
#ifdef PRECISE_TIMING_POSIX |
277 |
< |
sem_init(&wakeup_time_sem, 0, 1); |
272 |
< |
thread_active = timer_thread_init(); |
277 |
> |
timer_thread_active = timer_thread_init(); |
278 |
|
#endif |
279 |
|
#endif |
280 |
|
} |
298 |
|
delete_sem(wakeup_time_sem); |
299 |
|
#endif |
300 |
|
#ifdef PRECISE_TIMING_POSIX |
296 |
– |
thread_active = false; |
301 |
|
timer_thread_kill(); |
298 |
– |
sem_destroy(&wakeup_time_sem); |
302 |
|
#endif |
303 |
|
} |
304 |
|
#endif |
357 |
|
suspend_thread(timer_thread); |
358 |
|
#endif |
359 |
|
#if PRECISE_TIMING_POSIX |
357 |
– |
sem_wait(&wakeup_time_sem); |
360 |
|
timer_thread_suspend(); |
361 |
+ |
pthread_mutex_lock(&wakeup_time_lock); |
362 |
|
#endif |
363 |
|
if (ReadMacInt16(tm + qType) & 0x8000) { |
364 |
|
|
392 |
|
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) |
393 |
|
#endif |
394 |
|
#if PRECISE_TIMING_POSIX |
395 |
< |
sem_post(&wakeup_time_sem); |
395 |
> |
pthread_mutex_unlock(&wakeup_time_lock); |
396 |
|
timer_thread_resume(); |
397 |
|
assert(suspend_count == 0); |
398 |
|
#endif |
428 |
|
// Yes, tmWakeUp set? |
429 |
|
if (ReadMacInt32(tm + tmWakeUp)) { |
430 |
|
|
431 |
+ |
// PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled" |
432 |
+ |
// or (b) "continue previous delay" if an expired task was stopped via RmvTime() and |
433 |
+ |
// then re-installed using InsXTime(). This currently only handles (a). |
434 |
+ |
// |
435 |
+ |
// API reference: http://developer.apple.com/documentation/mac/Processes/Processes-68.html |
436 |
+ |
|
437 |
+ |
#if 0 |
438 |
|
//!! PrimeTime(0) means continue previous delay |
439 |
|
// (save wakeup time in RmvTime?) |
440 |
|
if (time == 0) { |
441 |
|
printf("FATAL: Unsupported PrimeTime(0)\n"); |
442 |
|
return 0; |
443 |
|
} |
444 |
+ |
#endif |
445 |
|
|
446 |
|
// Yes, calculate wakeup time relative to last scheduled time |
447 |
|
tm_time_t wakeup; |
473 |
|
suspend_thread(timer_thread); |
474 |
|
#endif |
475 |
|
#if PRECISE_TIMING_POSIX |
465 |
– |
sem_wait(&wakeup_time_sem); |
476 |
|
timer_thread_suspend(); |
477 |
+ |
pthread_mutex_lock(&wakeup_time_lock); |
478 |
|
#endif |
479 |
|
WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000); |
480 |
|
enqueue_tm(tm); |
495 |
|
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) |
496 |
|
#endif |
497 |
|
#ifdef PRECISE_TIMING_POSIX |
498 |
< |
sem_post(&wakeup_time_sem); |
498 |
> |
pthread_mutex_unlock(&wakeup_time_lock); |
499 |
|
timer_thread_resume(); |
500 |
|
assert(suspend_count == 0); |
501 |
|
#endif |
533 |
|
#ifdef PRECISE_TIMING_POSIX |
534 |
|
static void *timer_func(void *arg) |
535 |
|
{ |
536 |
< |
while (thread_active) { |
536 |
> |
while (!timer_thread_cancel) { |
537 |
|
|
538 |
|
// Wait until time specified by wakeup_time |
539 |
|
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL); |
540 |
|
|
530 |
– |
sem_wait(&wakeup_time_sem); |
541 |
|
tm_time_t system_time; |
542 |
|
timer_current_time(system_time); |
543 |
|
if (timer_cmp_time(wakeup_time, system_time) < 0) { |
544 |
|
|
545 |
|
// Timer expired, trigger interrupt |
546 |
+ |
pthread_mutex_lock(&wakeup_time_lock); |
547 |
|
wakeup_time = wakeup_time_max; |
548 |
+ |
pthread_mutex_unlock(&wakeup_time_lock); |
549 |
|
SetInterruptFlag(INTFLAG_TIMER); |
550 |
|
TriggerInterrupt(); |
551 |
|
} |
540 |
– |
sem_post(&wakeup_time_sem); |
552 |
|
} |
553 |
|
return NULL; |
554 |
|
} |
595 |
|
suspend_thread(timer_thread); |
596 |
|
#endif |
597 |
|
#if PRECISE_TIMING_POSIX |
587 |
– |
sem_wait(&wakeup_time_sem); |
598 |
|
timer_thread_suspend(); |
599 |
+ |
pthread_mutex_lock(&wakeup_time_lock); |
600 |
|
#endif |
601 |
|
wakeup_time = wakeup_time_max; |
602 |
|
for (int j=0; j<NUM_DESCS; j++) { |
613 |
|
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?) |
614 |
|
#endif |
615 |
|
#if PRECISE_TIMING_POSIX |
616 |
< |
sem_post(&wakeup_time_sem); |
616 |
> |
pthread_mutex_unlock(&wakeup_time_lock); |
617 |
|
timer_thread_resume(); |
618 |
|
assert(suspend_count == 0); |
619 |
|
#endif |