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.2 by cebix, 2004-01-12T15:37:19Z vs.
Revision 1.10 by asvitkine, 2009-07-31T20:43:28Z

# Line 1 | Line 1
1   /*
2   *  timer.cpp - Time Manager emulation
3   *
4 < *  SheepShaver (C) 1997-2004 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   #define DEBUG 0
33   #include "debug.h"
34  
35  
35 #if __BEOS__
36 #define PRECISE_TIMING 1
37 #else
38 #define PRECISE_TIMING 0
39 #endif
40
36   #define TM_QUEUE 0                      // Enable TMQueue management (doesn't work)
37  
38  
# Line 61 | Line 56 | const int NUM_DESCS = 64;              // Maximum nu
56   static TMDesc desc[NUM_DESCS];
57  
58   #if PRECISE_TIMING
59 + #ifdef PRECISE_TIMING_BEOS
60   static thread_id timer_thread = -1;
61   static bool thread_active = true;
62 < static volatile tm_time_t wakeup_time = 0x7fffffffffffffff;
62 > static const tm_time_t wakeup_time_max = 0x7fffffffffffffff;
63 > static volatile tm_time_t wakeup_time = wakeup_time_max;
64   static sem_id wakeup_time_sem = -1;
65   static int32 timer_func(void *arg);
66   #endif
67 + #ifdef PRECISE_TIMING_POSIX
68 + static pthread_t timer_thread;
69 + static bool timer_thread_active = false;
70 + static volatile bool timer_thread_cancel = false;
71 + static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 };
72 + static tm_time_t wakeup_time = wakeup_time_max;
73 + static pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER;
74 + static void *timer_func(void *arg);
75 + #endif
76 + #endif
77  
78  
79   /*
# Line 143 | Line 150 | static void dequeue_tm(uint32 tm)
150  
151  
152   /*
153 + *  Timer thread operations
154 + */
155 +
156 + #ifdef PRECISE_TIMING_POSIX
157 + const int SIGSUSPEND = SIGRTMIN + 6;
158 + const int SIGRESUME  = SIGRTMIN + 7;
159 + static struct sigaction sigsuspend_action;
160 + static struct sigaction sigresume_action;
161 +
162 + static int suspend_count = 0;
163 + static pthread_mutex_t suspend_count_lock = PTHREAD_MUTEX_INITIALIZER;
164 + static sem_t suspend_ack_sem;
165 + static sigset_t suspend_handler_mask;
166 +
167 + // Signal handler for suspended thread
168 + static void sigsuspend_handler(int sig)
169 + {
170 +        sem_post(&suspend_ack_sem);
171 +        sigsuspend(&suspend_handler_mask);
172 + }
173 +
174 + // Signal handler for resumed thread
175 + static void sigresume_handler(int sig)
176 + {
177 +        /* simply trigger a signal to stop clock_nanosleep() */
178 + }
179 +
180 + // Initialize timer thread
181 + static bool timer_thread_init(void)
182 + {
183 +        // Install suspend signal handler
184 +        sigemptyset(&sigsuspend_action.sa_mask);
185 +        sigaddset(&sigsuspend_action.sa_mask, SIGRESUME);
186 +        sigsuspend_action.sa_handler = sigsuspend_handler;
187 +        sigsuspend_action.sa_flags = SA_RESTART;
188 + #ifdef HAVE_SIGNAL_SA_RESTORER
189 +        sigsuspend_action.sa_restorer = NULL;
190 + #endif
191 +        if (sigaction(SIGSUSPEND, &sigsuspend_action, NULL) < 0)
192 +                return false;
193 +
194 +        // Install resume signal handler
195 +        sigemptyset(&sigresume_action.sa_mask);
196 +        sigresume_action.sa_handler = sigresume_handler;
197 +        sigresume_action.sa_flags = SA_RESTART;
198 + #ifdef HAVE_SIGNAL_SA_RESTORER
199 +        sigresume_action.sa_restorer = NULL;
200 + #endif
201 +        if (sigaction(SIGRESUME, &sigresume_action, NULL) < 0)
202 +                return false;
203 +
204 +        // Initialize semaphore
205 +        if (sem_init(&suspend_ack_sem, 0, 0) < 0)
206 +                return false;
207 +
208 +        // Initialize suspend_handler_mask, it excludes SIGRESUME
209 +        if (sigfillset(&suspend_handler_mask) != 0)
210 +                return false;
211 +        if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0)
212 +                return false;
213 +
214 +        // Create thread in running state
215 +        suspend_count = 0;
216 +        return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0);
217 + }
218 +
219 + // Kill timer thread
220 + static void timer_thread_kill(void)
221 + {
222 +        timer_thread_cancel = true;
223 + #ifdef HAVE_PTHREAD_CANCEL
224 +        pthread_cancel(timer_thread);
225 + #endif
226 +        pthread_join(timer_thread, NULL);
227 + }
228 +
229 + // Suspend timer thread
230 + static void timer_thread_suspend(void)
231 + {
232 +        pthread_mutex_lock(&suspend_count_lock);
233 +        if (suspend_count == 0) {
234 +                suspend_count ++;
235 +                if (pthread_kill(timer_thread, SIGSUSPEND) == 0)
236 +                        sem_wait(&suspend_ack_sem);
237 +        }
238 +        pthread_mutex_unlock(&suspend_count_lock);
239 + }
240 +
241 + // Resume timer thread
242 + static void timer_thread_resume(void)
243 + {
244 +        pthread_mutex_lock(&suspend_count_lock);
245 +        assert(suspend_count > 0);
246 +        if (suspend_count == 1) {
247 +                suspend_count = 0;
248 +                pthread_kill(timer_thread, SIGRESUME);
249 +        }
250 +        pthread_mutex_unlock(&suspend_count_lock);
251 + }
252 + #endif
253 +
254 +
255 + /*
256   *  Initialize Time Manager
257   */
258  
# Line 154 | Line 264 | void TimerInit(void)
264  
265   #if PRECISE_TIMING
266          // Start timer thread
267 + #ifdef PRECISE_TIMING_BEOS
268          wakeup_time_sem = create_sem(1, "Wakeup Time");
269          timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
270          resume_thread(timer_thread);
271   #endif
272 + #ifdef PRECISE_TIMING_POSIX
273 +        timer_thread_active = timer_thread_init();
274 + #endif
275 + #endif
276   }
277  
278  
# Line 170 | Line 285 | void TimerExit(void)
285   #if PRECISE_TIMING
286          // Quit timer thread
287          if (timer_thread > 0) {
288 + #ifdef PRECISE_TIMING_BEOS
289                  status_t l;
290                  thread_active = false;
291                  suspend_thread(timer_thread);
292                  resume_thread(timer_thread);
293                  wait_for_thread(timer_thread, &l);
294                  delete_sem(wakeup_time_sem);
295 + #endif
296 + #ifdef PRECISE_TIMING_POSIX
297 +                timer_thread_kill();
298 + #endif
299          }
300   #endif
301   }
# Line 228 | Line 348 | int16 RmvTime(uint32 tm)
348          }
349  
350          // Task active?
351 < #if PRECISE_TIMING
351 > #if PRECISE_TIMING_BEOS
352          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
353          suspend_thread(timer_thread);
354   #endif
355 + #if PRECISE_TIMING_POSIX
356 +        timer_thread_suspend();
357 +        pthread_mutex_lock(&wakeup_time_lock);
358 + #endif
359          if (ReadMacInt16(tm + qType) & 0x8000) {
360  
361                  // Yes, make task inactive and remove it from the Time Manager queue
# Line 239 | Line 363 | int16 RmvTime(uint32 tm)
363                  dequeue_tm(tm);
364   #if PRECISE_TIMING
365                  // Look for next task to be called and set wakeup_time
366 <                wakeup_time = 0x7fffffffffffffff;
366 >                wakeup_time = wakeup_time_max;
367                  for (int j=0; j<NUM_DESCS; j++) {
368                          if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
369 <                                if (desc[j].wakeup < wakeup_time)
369 >                                if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
370                                          wakeup_time = desc[j].wakeup;
371                  }
372   #endif
# Line 255 | Line 379 | int16 RmvTime(uint32 tm)
379          } else
380                  WriteMacInt32(tm + tmCount, 0);
381          D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
382 < #if PRECISE_TIMING
382 > #if PRECISE_TIMING_BEOS
383          release_sem(wakeup_time_sem);
384          thread_info info;
385          do {
# Line 263 | Line 387 | int16 RmvTime(uint32 tm)
387                  get_thread_info(timer_thread, &info);
388          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
389   #endif
390 + #if PRECISE_TIMING_POSIX
391 +        pthread_mutex_unlock(&wakeup_time_lock);
392 +        timer_thread_resume();
393 +        assert(suspend_count == 0);
394 + #endif
395  
396          // Free descriptor
397          free_desc(i);
# Line 295 | Line 424 | int16 PrimeTime(uint32 tm, int32 time)
424                  // Yes, tmWakeUp set?
425                  if (ReadMacInt32(tm + tmWakeUp)) {
426  
427 <                        //!! PrimeTime(0) means continue previous delay
428 <                        // (save wakeup time in RmvTime?)
429 <                        if (time == 0) {
430 <                                printf("FATAL: Unsupported PrimeTime(0)\n");
431 <                                return 0;
303 <                        }
427 >                        // PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled"
428 >                        // or (b) "continue previous delay" if an expired task was stopped via RmvTime() and
429 >                        // then re-installed using InsXTime(). Since tmWakeUp was set, this is case (b).
430 >                        // The remaining time was saved in tmCount by RmvTime().
431 >                        timer_mac2host_time(delay, ReadMacInt16(tm + tmCount));
432  
433                          // Yes, calculate wakeup time relative to last scheduled time
434                          tm_time_t wakeup;
# Line 327 | Line 455 | int16 PrimeTime(uint32 tm, int32 time)
455          }
456  
457          // Make task active and enqueue it in the Time Manager queue
458 < #if PRECISE_TIMING
458 > #if PRECISE_TIMING_BEOS
459          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
460          suspend_thread(timer_thread);
461   #endif
462 + #if PRECISE_TIMING_POSIX
463 +        timer_thread_suspend();
464 +        pthread_mutex_lock(&wakeup_time_lock);
465 + #endif
466          WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
467          enqueue_tm(tm);
468   #if PRECISE_TIMING
469          // Look for next task to be called and set wakeup_time
470 <        wakeup_time = 0x7fffffffffffffff;
470 >        wakeup_time = wakeup_time_max;
471          for (int j=0; j<NUM_DESCS; j++) {
472                  if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
473 <                        if (desc[j].wakeup < wakeup_time)
473 >                        if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
474                                  wakeup_time = desc[j].wakeup;
475          }
476 + #ifdef PRECISE_TIMING_BEOS
477          release_sem(wakeup_time_sem);
478          thread_info info;
479          do {
# Line 348 | Line 481 | int16 PrimeTime(uint32 tm, int32 time)
481                  get_thread_info(timer_thread, &info);
482          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
483   #endif
484 + #ifdef PRECISE_TIMING_POSIX
485 +        pthread_mutex_unlock(&wakeup_time_lock);
486 +        timer_thread_resume();
487 +        assert(suspend_count == 0);
488 + #endif
489 + #endif
490          return 0;
491   }
492  
493  
355 #if PRECISE_TIMING
494   /*
495   *  Time Manager thread
496   */
497  
498 + #ifdef PRECISE_TIMING_BEOS
499   static int32 timer_func(void *arg)
500   {
501          while (thread_active) {
# Line 378 | Line 517 | static int32 timer_func(void *arg)
517   }
518   #endif
519  
520 + #ifdef PRECISE_TIMING_POSIX
521 + static void *timer_func(void *arg)
522 + {
523 +        while (!timer_thread_cancel) {
524 +
525 +                // Wait until time specified by wakeup_time
526 +                clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
527 +
528 +                tm_time_t system_time;
529 +                timer_current_time(system_time);
530 +                if (timer_cmp_time(wakeup_time, system_time) < 0) {
531 +
532 +                        // Timer expired, trigger interrupt
533 +                        pthread_mutex_lock(&wakeup_time_lock);
534 +                        wakeup_time = wakeup_time_max;
535 +                        pthread_mutex_unlock(&wakeup_time_lock);
536 +                        SetInterruptFlag(INTFLAG_TIMER);
537 +                        TriggerInterrupt();
538 +                }
539 +        }
540 +        return NULL;
541 + }
542 + #endif
543 +
544  
545   /*
546   *  Timer interrupt function (executed as part of 60Hz interrupt)
# Line 414 | Line 577 | void TimerInterrupt(void)
577  
578   #if PRECISE_TIMING
579          // Look for next task to be called and set wakeup_time
580 + #if PRECISE_TIMING_BEOS
581          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
582          suspend_thread(timer_thread);
583 <        wakeup_time = 0x7fffffffffffffff;
583 > #endif
584 > #if PRECISE_TIMING_POSIX
585 >        timer_thread_suspend();
586 >        pthread_mutex_lock(&wakeup_time_lock);
587 > #endif
588 >        wakeup_time = wakeup_time_max;
589          for (int j=0; j<NUM_DESCS; j++) {
590                  if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
591 <                        if (desc[j].wakeup < wakeup_time)
591 >                        if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
592                                  wakeup_time = desc[j].wakeup;
593          }
594 + #if PRECISE_TIMING_BEOS
595          release_sem(wakeup_time_sem);
596          thread_info info;
597          do {
# Line 429 | Line 599 | void TimerInterrupt(void)
599                  get_thread_info(timer_thread, &info);
600          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
601   #endif
602 + #if PRECISE_TIMING_POSIX
603 +        pthread_mutex_unlock(&wakeup_time_lock);
604 +        timer_thread_resume();
605 +        assert(suspend_count == 0);
606 + #endif
607 + #endif
608   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines