ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/timer.cpp
Revision: 1.12
Committed: 2009-08-17T20:44:30Z (14 years, 8 months ago) by asvitkine
Branch: MAIN
Changes since 1.11: +74 -1 lines
Log Message:
[Charles Srstka]
Attached is a set of patches to port the precise timer that is currently used in the Linux and BeOS builds of SheepShaver to Mac OS X (and any other Mach-based operating systems).

Currently, the Linux build uses the clock_gettime() function to get nanosecond-precision time, and falls back on gettimeofday() if it is not present. Unfortunately, Mac OS X does not currently support clock_gettime(), and gettimeofday() has only microsecond granularity. The Mach kernel, however, has a clock_get_time() function that does very nearly the same thing as clock_gettime(). The patches to BasiliskII cause the timing functions such as timer_current_time() to use clock_get_time() instead of gettimeofday() on Mach-based systems that do not support clock_gettime().

The changes to SheepShaver involve the precise timer. The existing code for Linux uses pthreads and real-time signals to handle the timing. Mac OS X unfortunately does not seem to support real-time signals, so Mach calls are again used to suspend and resume the timer thread in order to attempt to duplicate the Linux and BeOS versions of the timer. The code is somewhat ugly right now, as I decided to leave alone the pre-existing style of the source file, which unfortunately involves #ifdefs scattered throughout the file and some duplication of code. A future patch may want to clean this up to separate out the OS-specific code and put it all together at the top of the file. However, for the time being, this seems to work.

This has not been extensively tested, because I have not been able to get my hands on a good test-case app for the classic Mac OS that would run inside the emulator and try out the timer. However, performance does seem to be better than with the pre-existing code, and nothing seems to have blown up as far as I can tell. I did find a game via a Google search -  Cap'n Magneto - that is known to have problems with Basilisk/SheepShaver's legacy 60 Hz timer, and the opening fade-to-color for this game appears to run much more smoothly with the precise timer code in place.

File Contents

# Content
1 /*
2 * timer.cpp - Time Manager emulation
3 *
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
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22 #include "timer.h"
23 #include "macos_util.h"
24 #include "main.h"
25 #include "cpu_emulation.h"
26
27 #ifdef PRECISE_TIMING_POSIX
28 #include <pthread.h>
29 #include <semaphore.h>
30 #endif
31
32 #ifdef PRECISE_TIMING_MACH
33 #include <mach/mach.h>
34 #endif
35
36 #define DEBUG 0
37 #include "debug.h"
38
39
40 #define TM_QUEUE 0 // Enable TMQueue management (doesn't work)
41
42
43 // Definitions for Time Manager
44 enum { // TMTask struct
45 tmAddr = 6,
46 tmCount = 10,
47 tmWakeUp = 14,
48 tmReserved = 18
49 };
50
51
52 // Array of additional info for each installed TMTask
53 struct TMDesc {
54 uint32 task; // Mac address of associated TMTask
55 tm_time_t wakeup; // Time this task is scheduled for execution
56 bool in_use; // Flag: descriptor in use
57 };
58
59 const int NUM_DESCS = 64; // Maximum number of descriptors
60 static TMDesc desc[NUM_DESCS];
61
62 #if PRECISE_TIMING
63 #ifdef PRECISE_TIMING_BEOS
64 static thread_id timer_thread = -1;
65 static bool thread_active = true;
66 static const tm_time_t wakeup_time_max = 0x7fffffffffffffff;
67 static volatile tm_time_t wakeup_time = wakeup_time_max;
68 static sem_id wakeup_time_sem = -1;
69 static int32 timer_func(void *arg);
70 #endif
71 #ifdef PRECISE_TIMING_POSIX
72 static pthread_t timer_thread;
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 pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER;
78 static void *timer_func(void *arg);
79 #endif
80 #ifdef PRECISE_TIMING_MACH
81 static clock_serv_t system_clock;
82 static thread_act_t timer_thread;
83 static bool timer_thread_active = false;
84 static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 };
85 static tm_time_t wakeup_time = wakeup_time_max;
86 static semaphore_t wakeup_time_sem;
87 static void *timer_func(void *arg);
88 #endif
89 #endif
90
91
92 /*
93 * Allocate descriptor for given TMTask in list
94 */
95
96 static int alloc_desc(uint32 tm)
97 {
98 // Search for first free descriptor
99 for (int i=0; i<NUM_DESCS; i++)
100 if (!desc[i].in_use) {
101 desc[i].task = tm;
102 desc[i].in_use = true;
103 return i;
104 }
105 return -1;
106 }
107
108
109 /*
110 * Free descriptor in list
111 */
112
113 inline static void free_desc(int i)
114 {
115 desc[i].in_use = false;
116 }
117
118
119 /*
120 * Find descriptor associated with given TMTask
121 */
122
123 inline static int find_desc(uint32 tm)
124 {
125 for (int i=0; i<NUM_DESCS; i++)
126 if (desc[i].in_use && desc[i].task == tm)
127 return i;
128 return -1;
129 }
130
131
132 /*
133 * Enqueue task in Time Manager queue
134 */
135
136 static void enqueue_tm(uint32 tm)
137 {
138 #if TM_QUEUE
139 uint32 tm_var = ReadMacInt32(0xb30);
140 WriteMacInt32(tm + qLink, ReadMacInt32(tm_var));
141 WriteMacInt32(tm_var, tm);
142 #endif
143 }
144
145
146 /*
147 * Remove task from Time Manager queue
148 */
149
150 static void dequeue_tm(uint32 tm)
151 {
152 #if TM_QUEUE
153 uint32 p = ReadMacInt32(0xb30);
154 while (p) {
155 uint32 next = ReadMacInt32(p + qLink);
156 if (next == tm) {
157 WriteMacInt32(p + qLink, ReadMacInt32(next + qLink));
158 return;
159 }
160 }
161 #endif
162 }
163
164
165 /*
166 * Timer thread operations
167 */
168
169 #ifdef PRECISE_TIMING_POSIX
170 const int SIGSUSPEND = SIGRTMIN + 6;
171 const int SIGRESUME = SIGRTMIN + 7;
172 static struct sigaction sigsuspend_action;
173 static struct sigaction sigresume_action;
174
175 static int suspend_count = 0;
176 static pthread_mutex_t suspend_count_lock = PTHREAD_MUTEX_INITIALIZER;
177 static sem_t suspend_ack_sem;
178 static sigset_t suspend_handler_mask;
179
180 // Signal handler for suspended thread
181 static void sigsuspend_handler(int sig)
182 {
183 sem_post(&suspend_ack_sem);
184 sigsuspend(&suspend_handler_mask);
185 }
186
187 // Signal handler for resumed thread
188 static void sigresume_handler(int sig)
189 {
190 /* simply trigger a signal to stop clock_nanosleep() */
191 }
192
193 // Initialize timer thread
194 static bool timer_thread_init(void)
195 {
196 // Install suspend signal handler
197 sigemptyset(&sigsuspend_action.sa_mask);
198 sigaddset(&sigsuspend_action.sa_mask, SIGRESUME);
199 sigsuspend_action.sa_handler = sigsuspend_handler;
200 sigsuspend_action.sa_flags = SA_RESTART;
201 #ifdef HAVE_SIGNAL_SA_RESTORER
202 sigsuspend_action.sa_restorer = NULL;
203 #endif
204 if (sigaction(SIGSUSPEND, &sigsuspend_action, NULL) < 0)
205 return false;
206
207 // Install resume signal handler
208 sigemptyset(&sigresume_action.sa_mask);
209 sigresume_action.sa_handler = sigresume_handler;
210 sigresume_action.sa_flags = SA_RESTART;
211 #ifdef HAVE_SIGNAL_SA_RESTORER
212 sigresume_action.sa_restorer = NULL;
213 #endif
214 if (sigaction(SIGRESUME, &sigresume_action, NULL) < 0)
215 return false;
216
217 // Initialize semaphore
218 if (sem_init(&suspend_ack_sem, 0, 0) < 0)
219 return false;
220
221 // Initialize suspend_handler_mask, it excludes SIGRESUME
222 if (sigfillset(&suspend_handler_mask) != 0)
223 return false;
224 if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0)
225 return false;
226
227 // Create thread in running state
228 suspend_count = 0;
229 return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0);
230 }
231
232 // Kill timer thread
233 static void timer_thread_kill(void)
234 {
235 timer_thread_cancel = true;
236 #ifdef HAVE_PTHREAD_CANCEL
237 pthread_cancel(timer_thread);
238 #endif
239 pthread_join(timer_thread, NULL);
240 }
241
242 // Suspend timer thread
243 static void timer_thread_suspend(void)
244 {
245 pthread_mutex_lock(&suspend_count_lock);
246 if (suspend_count == 0) {
247 suspend_count ++;
248 if (pthread_kill(timer_thread, SIGSUSPEND) == 0)
249 sem_wait(&suspend_ack_sem);
250 }
251 pthread_mutex_unlock(&suspend_count_lock);
252 }
253
254 // Resume timer thread
255 static void timer_thread_resume(void)
256 {
257 pthread_mutex_lock(&suspend_count_lock);
258 assert(suspend_count > 0);
259 if (suspend_count == 1) {
260 suspend_count = 0;
261 pthread_kill(timer_thread, SIGRESUME);
262 }
263 pthread_mutex_unlock(&suspend_count_lock);
264 }
265 #endif
266
267
268 /*
269 * Initialize Time Manager
270 */
271
272 void TimerInit(void)
273 {
274 // Mark all descriptors as inactive
275 for (int i=0; i<NUM_DESCS; i++)
276 free_desc(i);
277
278 #if PRECISE_TIMING
279 // Start timer thread
280 #ifdef PRECISE_TIMING_BEOS
281 wakeup_time_sem = create_sem(1, "Wakeup Time");
282 timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
283 resume_thread(timer_thread);
284 #elif PRECISE_TIMING_MACH
285 pthread_t pthread;
286
287 host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &system_clock);
288 semaphore_create(mach_task_self(), &wakeup_time_sem, SYNC_POLICY_FIFO, 1);
289
290 pthread_create(&pthread, NULL, &timer_func, NULL);
291 #endif
292 #ifdef PRECISE_TIMING_POSIX
293 timer_thread_active = timer_thread_init();
294 #endif
295 #endif
296 }
297
298
299 /*
300 * Exit Time Manager
301 */
302
303 void TimerExit(void)
304 {
305 #if PRECISE_TIMING
306 // Quit timer thread
307 if (timer_thread > 0) {
308 #ifdef PRECISE_TIMING_BEOS
309 status_t l;
310 thread_active = false;
311 suspend_thread(timer_thread);
312 resume_thread(timer_thread);
313 wait_for_thread(timer_thread, &l);
314 delete_sem(wakeup_time_sem);
315 #endif
316 #ifdef PRECISE_TIMING_MACH
317 timer_thread_active = false;
318 semaphore_destroy(mach_task_self(), wakeup_time_sem);
319 #endif
320 #ifdef PRECISE_TIMING_POSIX
321 timer_thread_kill();
322 #endif
323 }
324 #endif
325 }
326
327
328 /*
329 * Emulator reset, remove all timer tasks
330 */
331
332 void TimerReset(void)
333 {
334 // Mark all descriptors as inactive
335 for (int i=0; i<NUM_DESCS; i++)
336 free_desc(i);
337 }
338
339
340 /*
341 * Insert timer task
342 */
343
344 int16 InsTime(uint32 tm, uint16 trap)
345 {
346 D(bug("InsTime %08lx, trap %04x\n", tm, trap));
347 WriteMacInt16((uint32)tm + qType, ReadMacInt16((uint32)tm + qType) & 0x1fff | (trap << 4) & 0x6000);
348 if (find_desc(tm) >= 0)
349 printf("WARNING: InsTime(): Task re-inserted\n");
350 else {
351 int i = alloc_desc(tm);
352 if (i < 0)
353 printf("FATAL: InsTime(): No free Time Manager descriptor\n");
354 }
355 return 0;
356 }
357
358
359 /*
360 * Remove timer task
361 */
362
363 int16 RmvTime(uint32 tm)
364 {
365 D(bug("RmvTime %08lx\n", tm));
366
367 // Find descriptor
368 int i = find_desc(tm);
369 if (i < 0) {
370 printf("WARNING: RmvTime(%08lx): Descriptor not found\n", tm);
371 return 0;
372 }
373
374 // Task active?
375 #if PRECISE_TIMING_BEOS
376 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
377 suspend_thread(timer_thread);
378 #endif
379 #ifdef PRECISE_TIMING_MACH
380 semaphore_wait(wakeup_time_sem);
381 thread_suspend(timer_thread);
382 #endif
383 #if PRECISE_TIMING_POSIX
384 timer_thread_suspend();
385 pthread_mutex_lock(&wakeup_time_lock);
386 #endif
387 if (ReadMacInt16(tm + qType) & 0x8000) {
388
389 // Yes, make task inactive and remove it from the Time Manager queue
390 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
391 dequeue_tm(tm);
392 #if PRECISE_TIMING
393 // Look for next task to be called and set wakeup_time
394 wakeup_time = wakeup_time_max;
395 for (int j=0; j<NUM_DESCS; j++) {
396 if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
397 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
398 wakeup_time = desc[j].wakeup;
399 }
400 #endif
401
402 // Compute remaining time
403 tm_time_t remaining, current;
404 timer_current_time(current);
405 timer_sub_time(remaining, desc[i].wakeup, current);
406 WriteMacInt32(tm + tmCount, timer_host2mac_time(remaining));
407 } else
408 WriteMacInt32(tm + tmCount, 0);
409 D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
410 #if PRECISE_TIMING_BEOS
411 release_sem(wakeup_time_sem);
412 thread_info info;
413 do {
414 resume_thread(timer_thread); // This will unblock the thread
415 get_thread_info(timer_thread, &info);
416 } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
417 #endif
418 #ifdef PRECISE_TIMING_MACH
419 semaphore_signal(wakeup_time_sem);
420 thread_abort(timer_thread);
421 thread_resume(timer_thread);
422 #endif
423 #if PRECISE_TIMING_POSIX
424 pthread_mutex_unlock(&wakeup_time_lock);
425 timer_thread_resume();
426 assert(suspend_count == 0);
427 #endif
428
429 // Free descriptor
430 free_desc(i);
431 return 0;
432 }
433
434
435 /*
436 * Start timer task
437 */
438
439 int16 PrimeTime(uint32 tm, int32 time)
440 {
441 D(bug("PrimeTime %08lx, time %ld\n", tm, time));
442
443 // Find descriptor
444 int i = find_desc(tm);
445 if (i < 0) {
446 printf("FATAL: PrimeTime(): Descriptor not found\n");
447 return 0;
448 }
449
450 // Convert delay time
451 tm_time_t delay;
452 timer_mac2host_time(delay, time);
453
454 // Extended task?
455 if (ReadMacInt16(tm + qType) & 0x4000) {
456
457 // Yes, tmWakeUp set?
458 if (ReadMacInt32(tm + tmWakeUp)) {
459
460 // PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled"
461 // or (b) "continue previous delay" if an expired task was stopped via RmvTime() and
462 // then re-installed using InsXTime(). Since tmWakeUp was set, this is case (b).
463 // The remaining time was saved in tmCount by RmvTime().
464 if (time == 0) {
465 timer_mac2host_time(delay, ReadMacInt16(tm + tmCount));
466 }
467
468 // Yes, calculate wakeup time relative to last scheduled time
469 tm_time_t wakeup;
470 timer_add_time(wakeup, desc[i].wakeup, delay);
471 desc[i].wakeup = wakeup;
472
473 } else {
474
475 // No, calculate wakeup time relative to current time
476 tm_time_t now;
477 timer_current_time(now);
478 timer_add_time(desc[i].wakeup, now, delay);
479 }
480
481 // Set tmWakeUp to indicate that task was scheduled
482 WriteMacInt32(tm + tmWakeUp, 0x12345678);
483
484 } else {
485
486 // Not extended task, calculate wakeup time relative to current time
487 tm_time_t now;
488 timer_current_time(now);
489 timer_add_time(desc[i].wakeup, now, delay);
490 }
491
492 // Make task active and enqueue it in the Time Manager queue
493 #if PRECISE_TIMING_BEOS
494 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
495 suspend_thread(timer_thread);
496 #endif
497 #ifdef PRECISE_TIMING_MACH
498 semaphore_wait(wakeup_time_sem);
499 thread_suspend(timer_thread);
500 #endif
501 #if PRECISE_TIMING_POSIX
502 timer_thread_suspend();
503 pthread_mutex_lock(&wakeup_time_lock);
504 #endif
505 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
506 enqueue_tm(tm);
507 #if PRECISE_TIMING
508 // Look for next task to be called and set wakeup_time
509 wakeup_time = wakeup_time_max;
510 for (int j=0; j<NUM_DESCS; j++) {
511 if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
512 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
513 wakeup_time = desc[j].wakeup;
514 }
515 #ifdef PRECISE_TIMING_BEOS
516 release_sem(wakeup_time_sem);
517 thread_info info;
518 do {
519 resume_thread(timer_thread); // This will unblock the thread
520 get_thread_info(timer_thread, &info);
521 } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
522 #endif
523 #ifdef PRECISE_TIMING_MACH
524 semaphore_signal(wakeup_time_sem);
525 thread_abort(timer_thread);
526 thread_resume(timer_thread);
527 #endif
528 #ifdef PRECISE_TIMING_POSIX
529 pthread_mutex_unlock(&wakeup_time_lock);
530 timer_thread_resume();
531 assert(suspend_count == 0);
532 #endif
533 #endif
534 return 0;
535 }
536
537
538 /*
539 * Time Manager thread
540 */
541
542 #ifdef PRECISE_TIMING_BEOS
543 static int32 timer_func(void *arg)
544 {
545 while (thread_active) {
546
547 // Wait until time specified by wakeup_time
548 snooze_until(wakeup_time, B_SYSTEM_TIMEBASE);
549
550 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
551 if (wakeup_time < system_time()) {
552
553 // Timer expired, trigger interrupt
554 wakeup_time = 0x7fffffffffffffff;
555 SetInterruptFlag(INTFLAG_TIMER);
556 TriggerInterrupt();
557 }
558 release_sem(wakeup_time_sem);
559 }
560 return 0;
561 }
562 #endif
563
564 #ifdef PRECISE_TIMING_MACH
565 static void *timer_func(void *arg)
566 {
567 timer_thread = mach_thread_self();
568 timer_thread_active = true;
569
570 while (timer_thread_active) {
571 clock_sleep(system_clock, TIME_ABSOLUTE, wakeup_time, NULL);
572 semaphore_wait(wakeup_time_sem);
573
574 tm_time_t system_time;
575
576 timer_current_time(system_time);
577 if (timer_cmp_time(wakeup_time, system_time) < 0) {
578 wakeup_time = wakeup_time_max;
579 SetInterruptFlag(INTFLAG_TIMER);
580 TriggerInterrupt();
581 }
582 semaphore_signal(wakeup_time_sem);
583 }
584 }
585 #endif
586
587 #ifdef PRECISE_TIMING_POSIX
588 static void *timer_func(void *arg)
589 {
590 while (!timer_thread_cancel) {
591 // Wait until time specified by wakeup_time
592 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
593
594 tm_time_t system_time;
595 timer_current_time(system_time);
596 if (timer_cmp_time(wakeup_time, system_time) < 0) {
597
598 // Timer expired, trigger interrupt
599 pthread_mutex_lock(&wakeup_time_lock);
600 wakeup_time = wakeup_time_max;
601 pthread_mutex_unlock(&wakeup_time_lock);
602 SetInterruptFlag(INTFLAG_TIMER);
603 TriggerInterrupt();
604 }
605 }
606 return NULL;
607 }
608 #endif
609
610
611 /*
612 * Timer interrupt function (executed as part of 60Hz interrupt)
613 */
614
615 void TimerInterrupt(void)
616 {
617 // D(bug("TimerIRQ\n"));
618
619 // Look for active TMTasks that have expired
620 tm_time_t now;
621 timer_current_time(now);
622 for (int i=0; i<NUM_DESCS; i++)
623 if (desc[i].in_use) {
624 uint32 tm = desc[i].task;
625 if ((ReadMacInt16(tm + qType) & 0x8000) && timer_cmp_time(desc[i].wakeup, now) <= 0) {
626
627 // Found one, mark as inactive and remove it from the Time Manager queue
628 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
629 dequeue_tm(tm);
630
631 // Call timer function
632 uint32 addr = ReadMacInt32(tm + tmAddr);
633 if (addr) {
634 D(bug("Calling TimeTask %08lx, addr %08lx\n", tm, addr));
635 M68kRegisters r;
636 r.a[0] = addr;
637 r.a[1] = tm;
638 Execute68k(r.a[0], &r);
639 D(bug(" returned from TimeTask\n"));
640 }
641 }
642 }
643
644 #if PRECISE_TIMING
645 // Look for next task to be called and set wakeup_time
646 #if PRECISE_TIMING_BEOS
647 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
648 suspend_thread(timer_thread);
649 #endif
650 #if PRECISE_TIMING_MACH
651 semaphore_wait(wakeup_time_sem);
652 thread_suspend(timer_thread);
653 #endif
654 #if PRECISE_TIMING_POSIX
655 timer_thread_suspend();
656 pthread_mutex_lock(&wakeup_time_lock);
657 #endif
658 wakeup_time = wakeup_time_max;
659 for (int j=0; j<NUM_DESCS; j++) {
660 if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
661 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
662 wakeup_time = desc[j].wakeup;
663 }
664 #if PRECISE_TIMING_BEOS
665 release_sem(wakeup_time_sem);
666 thread_info info;
667 do {
668 resume_thread(timer_thread); // This will unblock the thread
669 get_thread_info(timer_thread, &info);
670 } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
671 #endif
672 #if PRECISE_TIMING_MACH
673 semaphore_signal(wakeup_time_sem);
674 thread_abort(timer_thread);
675 thread_resume(timer_thread);
676 #endif
677 #if PRECISE_TIMING_POSIX
678 pthread_mutex_unlock(&wakeup_time_lock);
679 timer_thread_resume();
680 assert(suspend_count == 0);
681 #endif
682 #endif
683 }