ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/timer.cpp
(Generate patch)

Comparing SheepShaver/src/timer.cpp (file contents):
Revision 1.1 by cebix, 2002-02-04T16:58:13Z vs.
Revision 1.12 by asvitkine, 2009-08-17T20:44:30Z

# Line 1 | Line 1
1   /*
2   *  timer.cpp - Time Manager emulation
3   *
4 < *  SheepShaver (C) 1997-2002 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
# Line 18 | Line 18
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  
21 /*
22 * TODO: Prime(0)
23 */
24
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  
35 #if __BEOS__
36 #define PRECISE_TIMING 1
37 #else
38 #define PRECISE_TIMING 0
39 #endif
40
40   #define TM_QUEUE 0                      // Enable TMQueue management (doesn't work)
41  
42  
# Line 61 | Line 60 | const int NUM_DESCS = 64;              // Maximum nu
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 volatile tm_time_t wakeup_time = 0x7fffffffffffffff;
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   /*
# Line 143 | Line 163 | static void dequeue_tm(uint32 tm)
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  
# Line 154 | Line 277 | void TimerInit(void)
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  
# Line 170 | Line 305 | void TimerExit(void)
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   }
# Line 228 | Line 372 | int16 RmvTime(uint32 tm)
372          }
373  
374          // Task active?
375 < #if PRECISE_TIMING
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
# Line 239 | Line 391 | int16 RmvTime(uint32 tm)
391                  dequeue_tm(tm);
392   #if PRECISE_TIMING
393                  // Look for next task to be called and set wakeup_time
394 <                wakeup_time = 0x7fffffffffffffff;
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 (desc[j].wakeup < wakeup_time)
397 >                                if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
398                                          wakeup_time = desc[j].wakeup;
399                  }
400   #endif
# Line 255 | Line 407 | int16 RmvTime(uint32 tm)
407          } else
408                  WriteMacInt32(tm + tmCount, 0);
409          D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
410 < #if PRECISE_TIMING
410 > #if PRECISE_TIMING_BEOS
411          release_sem(wakeup_time_sem);
412          thread_info info;
413          do {
# Line 263 | Line 415 | int16 RmvTime(uint32 tm)
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);
# Line 295 | Line 457 | int16 PrimeTime(uint32 tm, int32 time)
457                  // Yes, tmWakeUp set?
458                  if (ReadMacInt32(tm + tmWakeUp)) {
459  
460 <                        //!! PrimeTime(0) means continue previous delay
461 <                        // (save wakeup time in RmvTime?)
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 <                                printf("FATAL: Unsupported PrimeTime(0)\n");
302 <                                return 0;
465 >                                timer_mac2host_time(delay, ReadMacInt16(tm + tmCount));
466                          }
467  
468                          // Yes, calculate wakeup time relative to last scheduled time
# Line 327 | Line 490 | int16 PrimeTime(uint32 tm, int32 time)
490          }
491  
492          // Make task active and enqueue it in the Time Manager queue
493 < #if PRECISE_TIMING
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 = 0x7fffffffffffffff;
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 (desc[j].wakeup < wakeup_time)
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 {
# Line 348 | Line 520 | int16 PrimeTime(uint32 tm, int32 time)
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  
355 #if PRECISE_TIMING
538   /*
539   *  Time Manager thread
540   */
541  
542 + #ifdef PRECISE_TIMING_BEOS
543   static int32 timer_func(void *arg)
544   {
545          while (thread_active) {
# Line 378 | Line 561 | static int32 timer_func(void *arg)
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)
# Line 414 | Line 643 | void TimerInterrupt(void)
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 <        wakeup_time = 0x7fffffffffffffff;
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 (desc[j].wakeup < wakeup_time)
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 {
# Line 429 | Line 669 | void TimerInterrupt(void)
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   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines