ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/timer.cpp
Revision: 1.5
Committed: 2005-03-15T20:46:50Z (19 years, 1 month ago) by gbeauche
Branch: MAIN
Changes since 1.4: +13 -9 lines
Log Message:
fix initialization of timer thread that could be put in indefinite suspended
state from the start.

File Contents

# Content
1 /*
2 * timer.cpp - Time Manager emulation
3 *
4 * SheepShaver (C) 1997-2005 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 /*
22 * TODO: Prime(0)
23 */
24
25 #include "sysdeps.h"
26 #include "timer.h"
27 #include "macos_util.h"
28 #include "main.h"
29 #include "cpu_emulation.h"
30
31 #ifdef PRECISE_TIMING_POSIX
32 #include <pthread.h>
33 #include <semaphore.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 sem_t wakeup_time_sem;
78 static void *timer_func(void *arg);
79 #endif
80 #endif
81
82
83 /*
84 * Allocate descriptor for given TMTask in list
85 */
86
87 static int alloc_desc(uint32 tm)
88 {
89 // Search for first free descriptor
90 for (int i=0; i<NUM_DESCS; i++)
91 if (!desc[i].in_use) {
92 desc[i].task = tm;
93 desc[i].in_use = true;
94 return i;
95 }
96 return -1;
97 }
98
99
100 /*
101 * Free descriptor in list
102 */
103
104 inline static void free_desc(int i)
105 {
106 desc[i].in_use = false;
107 }
108
109
110 /*
111 * Find descriptor associated with given TMTask
112 */
113
114 inline static int find_desc(uint32 tm)
115 {
116 for (int i=0; i<NUM_DESCS; i++)
117 if (desc[i].in_use && desc[i].task == tm)
118 return i;
119 return -1;
120 }
121
122
123 /*
124 * Enqueue task in Time Manager queue
125 */
126
127 static void enqueue_tm(uint32 tm)
128 {
129 #if TM_QUEUE
130 uint32 tm_var = ReadMacInt32(0xb30);
131 WriteMacInt32(tm + qLink, ReadMacInt32(tm_var));
132 WriteMacInt32(tm_var, tm);
133 #endif
134 }
135
136
137 /*
138 * Remove task from Time Manager queue
139 */
140
141 static void dequeue_tm(uint32 tm)
142 {
143 #if TM_QUEUE
144 uint32 p = ReadMacInt32(0xb30);
145 while (p) {
146 uint32 next = ReadMacInt32(p + qLink);
147 if (next == tm) {
148 WriteMacInt32(p + qLink, ReadMacInt32(next + qLink));
149 return;
150 }
151 }
152 #endif
153 }
154
155
156 /*
157 * Timer thread operations
158 */
159
160 #ifdef PRECISE_TIMING_POSIX
161 const int SIGSUSPEND = SIGRTMIN + 6;
162 const int SIGRESUME = SIGRTMIN + 7;
163 static struct sigaction sigsuspend_action;
164 static struct sigaction sigresume_action;
165
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 sigsuspend(&suspend_handler_mask);
176 }
177
178 // Signal handler for resumed thread
179 static void sigresume_handler(int sig)
180 {
181 /* simply trigger a signal to stop clock_nanosleep() */
182 }
183
184 // Initialize timer thread
185 static bool timer_thread_init(void)
186 {
187 // Install suspend signal handler
188 sigfillset(&sigsuspend_action.sa_mask);
189 sigsuspend_action.sa_handler = sigsuspend_handler;
190 sigsuspend_action.sa_flags = SA_RESTART;
191 #ifdef HAVE_SIGNAL_SA_RESTORER
192 sigsuspend_action.sa_restorer = NULL;
193 #endif
194 if (sigaction(SIGSUSPEND, &sigsuspend_action, NULL) < 0)
195 return false;
196
197 // Install resume signal handler
198 sigfillset(&sigresume_action.sa_mask);
199 sigresume_action.sa_handler = sigresume_handler;
200 sigresume_action.sa_flags = SA_RESTART;
201 #ifdef HAVE_SIGNAL_SA_RESTORER
202 sigresume_action.sa_restorer = NULL;
203 #endif
204 if (sigaction(SIGRESUME, &sigresume_action, NULL) < 0)
205 return false;
206
207 // Initialize semaphore
208 if (sem_init(&suspend_ack_sem, 0, 0) < 0)
209 return false;
210
211 // Initialize suspend_handler_mask, it excludes SIGRESUME
212 if (sigfillset(&suspend_handler_mask) != 0)
213 return false;
214 if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0)
215 return false;
216
217 // Create thread in running state
218 suspend_count = 0;
219 return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0);
220 }
221
222 // Kill timer thread
223 static void timer_thread_kill(void)
224 {
225 timer_thread_cancel = true;
226 #ifdef HAVE_PTHREAD_CANCEL
227 pthread_cancel(timer_thread);
228 #endif
229 pthread_join(timer_thread, NULL);
230 }
231
232 // Suspend timer thread
233 static void timer_thread_suspend(void)
234 {
235 pthread_mutex_lock(&suspend_count_lock);
236 if (suspend_count == 0) {
237 suspend_count ++;
238 if (pthread_kill(timer_thread, SIGSUSPEND) == 0)
239 sem_wait(&suspend_ack_sem);
240 }
241 pthread_mutex_unlock(&suspend_count_lock);
242 }
243
244 // Resume timer thread
245 static void timer_thread_resume(void)
246 {
247 pthread_mutex_lock(&suspend_count_lock);
248 assert(suspend_count > 0);
249 if (suspend_count == 1) {
250 suspend_count = 0;
251 pthread_kill(timer_thread, SIGRESUME);
252 }
253 pthread_mutex_unlock(&suspend_count_lock);
254 }
255 #endif
256
257
258 /*
259 * Initialize Time Manager
260 */
261
262 void TimerInit(void)
263 {
264 // Mark all descriptors as inactive
265 for (int i=0; i<NUM_DESCS; i++)
266 free_desc(i);
267
268 #if PRECISE_TIMING
269 // Start timer thread
270 #ifdef PRECISE_TIMING_BEOS
271 wakeup_time_sem = create_sem(1, "Wakeup Time");
272 timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
273 resume_thread(timer_thread);
274 #endif
275 #ifdef PRECISE_TIMING_POSIX
276 sem_init(&wakeup_time_sem, 0, 1);
277 timer_thread_active = timer_thread_init();
278 #endif
279 #endif
280 }
281
282
283 /*
284 * Exit Time Manager
285 */
286
287 void TimerExit(void)
288 {
289 #if PRECISE_TIMING
290 // Quit timer thread
291 if (timer_thread > 0) {
292 #ifdef PRECISE_TIMING_BEOS
293 status_t l;
294 thread_active = false;
295 suspend_thread(timer_thread);
296 resume_thread(timer_thread);
297 wait_for_thread(timer_thread, &l);
298 delete_sem(wakeup_time_sem);
299 #endif
300 #ifdef PRECISE_TIMING_POSIX
301 timer_thread_kill();
302 sem_destroy(&wakeup_time_sem);
303 #endif
304 }
305 #endif
306 }
307
308
309 /*
310 * Emulator reset, remove all timer tasks
311 */
312
313 void TimerReset(void)
314 {
315 // Mark all descriptors as inactive
316 for (int i=0; i<NUM_DESCS; i++)
317 free_desc(i);
318 }
319
320
321 /*
322 * Insert timer task
323 */
324
325 int16 InsTime(uint32 tm, uint16 trap)
326 {
327 D(bug("InsTime %08lx, trap %04x\n", tm, trap));
328 WriteMacInt16((uint32)tm + qType, ReadMacInt16((uint32)tm + qType) & 0x1fff | (trap << 4) & 0x6000);
329 if (find_desc(tm) >= 0)
330 printf("WARNING: InsTime(): Task re-inserted\n");
331 else {
332 int i = alloc_desc(tm);
333 if (i < 0)
334 printf("FATAL: InsTime(): No free Time Manager descriptor\n");
335 }
336 return 0;
337 }
338
339
340 /*
341 * Remove timer task
342 */
343
344 int16 RmvTime(uint32 tm)
345 {
346 D(bug("RmvTime %08lx\n", tm));
347
348 // Find descriptor
349 int i = find_desc(tm);
350 if (i < 0) {
351 printf("WARNING: RmvTime(%08lx): Descriptor not found\n", tm);
352 return 0;
353 }
354
355 // Task active?
356 #if PRECISE_TIMING_BEOS
357 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
358 suspend_thread(timer_thread);
359 #endif
360 #if PRECISE_TIMING_POSIX
361 sem_wait(&wakeup_time_sem);
362 timer_thread_suspend();
363 #endif
364 if (ReadMacInt16(tm + qType) & 0x8000) {
365
366 // Yes, make task inactive and remove it from the Time Manager queue
367 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
368 dequeue_tm(tm);
369 #if PRECISE_TIMING
370 // Look for next task to be called and set wakeup_time
371 wakeup_time = wakeup_time_max;
372 for (int j=0; j<NUM_DESCS; j++) {
373 if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
374 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
375 wakeup_time = desc[j].wakeup;
376 }
377 #endif
378
379 // Compute remaining time
380 tm_time_t remaining, current;
381 timer_current_time(current);
382 timer_sub_time(remaining, desc[i].wakeup, current);
383 WriteMacInt32(tm + tmCount, timer_host2mac_time(remaining));
384 } else
385 WriteMacInt32(tm + tmCount, 0);
386 D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
387 #if PRECISE_TIMING_BEOS
388 release_sem(wakeup_time_sem);
389 thread_info info;
390 do {
391 resume_thread(timer_thread); // This will unblock the thread
392 get_thread_info(timer_thread, &info);
393 } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
394 #endif
395 #if PRECISE_TIMING_POSIX
396 sem_post(&wakeup_time_sem);
397 timer_thread_resume();
398 assert(suspend_count == 0);
399 #endif
400
401 // Free descriptor
402 free_desc(i);
403 return 0;
404 }
405
406
407 /*
408 * Start timer task
409 */
410
411 int16 PrimeTime(uint32 tm, int32 time)
412 {
413 D(bug("PrimeTime %08lx, time %ld\n", tm, time));
414
415 // Find descriptor
416 int i = find_desc(tm);
417 if (i < 0) {
418 printf("FATAL: PrimeTime(): Descriptor not found\n");
419 return 0;
420 }
421
422 // Convert delay time
423 tm_time_t delay;
424 timer_mac2host_time(delay, time);
425
426 // Extended task?
427 if (ReadMacInt16(tm + qType) & 0x4000) {
428
429 // Yes, tmWakeUp set?
430 if (ReadMacInt32(tm + tmWakeUp)) {
431
432 //!! PrimeTime(0) means continue previous delay
433 // (save wakeup time in RmvTime?)
434 if (time == 0) {
435 printf("FATAL: Unsupported PrimeTime(0)\n");
436 return 0;
437 }
438
439 // Yes, calculate wakeup time relative to last scheduled time
440 tm_time_t wakeup;
441 timer_add_time(wakeup, desc[i].wakeup, delay);
442 desc[i].wakeup = wakeup;
443
444 } else {
445
446 // No, calculate wakeup time relative to current time
447 tm_time_t now;
448 timer_current_time(now);
449 timer_add_time(desc[i].wakeup, now, delay);
450 }
451
452 // Set tmWakeUp to indicate that task was scheduled
453 WriteMacInt32(tm + tmWakeUp, 0x12345678);
454
455 } else {
456
457 // Not extended task, calculate wakeup time relative to current time
458 tm_time_t now;
459 timer_current_time(now);
460 timer_add_time(desc[i].wakeup, now, delay);
461 }
462
463 // Make task active and enqueue it in the Time Manager queue
464 #if PRECISE_TIMING_BEOS
465 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
466 suspend_thread(timer_thread);
467 #endif
468 #if PRECISE_TIMING_POSIX
469 sem_wait(&wakeup_time_sem);
470 timer_thread_suspend();
471 #endif
472 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
473 enqueue_tm(tm);
474 #if PRECISE_TIMING
475 // Look for next task to be called and set wakeup_time
476 wakeup_time = wakeup_time_max;
477 for (int j=0; j<NUM_DESCS; j++) {
478 if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
479 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
480 wakeup_time = desc[j].wakeup;
481 }
482 #ifdef PRECISE_TIMING_BEOS
483 release_sem(wakeup_time_sem);
484 thread_info info;
485 do {
486 resume_thread(timer_thread); // This will unblock the thread
487 get_thread_info(timer_thread, &info);
488 } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
489 #endif
490 #ifdef PRECISE_TIMING_POSIX
491 sem_post(&wakeup_time_sem);
492 timer_thread_resume();
493 assert(suspend_count == 0);
494 #endif
495 #endif
496 return 0;
497 }
498
499
500 /*
501 * Time Manager thread
502 */
503
504 #ifdef PRECISE_TIMING_BEOS
505 static int32 timer_func(void *arg)
506 {
507 while (thread_active) {
508
509 // Wait until time specified by wakeup_time
510 snooze_until(wakeup_time, B_SYSTEM_TIMEBASE);
511
512 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
513 if (wakeup_time < system_time()) {
514
515 // Timer expired, trigger interrupt
516 wakeup_time = 0x7fffffffffffffff;
517 SetInterruptFlag(INTFLAG_TIMER);
518 TriggerInterrupt();
519 }
520 release_sem(wakeup_time_sem);
521 }
522 return 0;
523 }
524 #endif
525
526 #ifdef PRECISE_TIMING_POSIX
527 static void *timer_func(void *arg)
528 {
529 while (!timer_thread_cancel) {
530
531 // Wait until time specified by wakeup_time
532 clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
533
534 sem_wait(&wakeup_time_sem);
535 tm_time_t system_time;
536 timer_current_time(system_time);
537 if (timer_cmp_time(wakeup_time, system_time) < 0) {
538
539 // Timer expired, trigger interrupt
540 wakeup_time = wakeup_time_max;
541 SetInterruptFlag(INTFLAG_TIMER);
542 TriggerInterrupt();
543 }
544 sem_post(&wakeup_time_sem);
545 }
546 return NULL;
547 }
548 #endif
549
550
551 /*
552 * Timer interrupt function (executed as part of 60Hz interrupt)
553 */
554
555 void TimerInterrupt(void)
556 {
557 // D(bug("TimerIRQ\n"));
558
559 // Look for active TMTasks that have expired
560 tm_time_t now;
561 timer_current_time(now);
562 for (int i=0; i<NUM_DESCS; i++)
563 if (desc[i].in_use) {
564 uint32 tm = desc[i].task;
565 if ((ReadMacInt16(tm + qType) & 0x8000) && timer_cmp_time(desc[i].wakeup, now) <= 0) {
566
567 // Found one, mark as inactive and remove it from the Time Manager queue
568 WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) & 0x7fff);
569 dequeue_tm(tm);
570
571 // Call timer function
572 uint32 addr = ReadMacInt32(tm + tmAddr);
573 if (addr) {
574 D(bug("Calling TimeTask %08lx, addr %08lx\n", tm, addr));
575 M68kRegisters r;
576 r.a[0] = addr;
577 r.a[1] = tm;
578 Execute68k(r.a[0], &r);
579 D(bug(" returned from TimeTask\n"));
580 }
581 }
582 }
583
584 #if PRECISE_TIMING
585 // Look for next task to be called and set wakeup_time
586 #if PRECISE_TIMING_BEOS
587 while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
588 suspend_thread(timer_thread);
589 #endif
590 #if PRECISE_TIMING_POSIX
591 sem_wait(&wakeup_time_sem);
592 timer_thread_suspend();
593 #endif
594 wakeup_time = wakeup_time_max;
595 for (int j=0; j<NUM_DESCS; j++) {
596 if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
597 if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
598 wakeup_time = desc[j].wakeup;
599 }
600 #if PRECISE_TIMING_BEOS
601 release_sem(wakeup_time_sem);
602 thread_info info;
603 do {
604 resume_thread(timer_thread); // This will unblock the thread
605 get_thread_info(timer_thread, &info);
606 } while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
607 #endif
608 #if PRECISE_TIMING_POSIX
609 sem_post(&wakeup_time_sem);
610 timer_thread_resume();
611 assert(suspend_count == 0);
612 #endif
613 #endif
614 }