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.3 by gbeauche, 2005-01-30T21:48:19Z vs.
Revision 1.11 by asvitkine, 2009-08-01T07:02:41Z

# Line 1 | Line 1
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
# 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?)
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                          if (time == 0) {
432 <                                printf("FATAL: Unsupported PrimeTime(0)\n");
302 <                                return 0;
432 >                                timer_mac2host_time(delay, ReadMacInt16(tm + tmCount));
433                          }
434  
435                          // Yes, calculate wakeup time relative to last scheduled time
# Line 327 | Line 457 | int16 PrimeTime(uint32 tm, int32 time)
457          }
458  
459          // Make task active and enqueue it in the Time Manager queue
460 < #if PRECISE_TIMING
460 > #if PRECISE_TIMING_BEOS
461          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
462          suspend_thread(timer_thread);
463   #endif
464 + #if PRECISE_TIMING_POSIX
465 +        timer_thread_suspend();
466 +        pthread_mutex_lock(&wakeup_time_lock);
467 + #endif
468          WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
469          enqueue_tm(tm);
470   #if PRECISE_TIMING
471          // Look for next task to be called and set wakeup_time
472 <        wakeup_time = 0x7fffffffffffffff;
472 >        wakeup_time = wakeup_time_max;
473          for (int j=0; j<NUM_DESCS; j++) {
474                  if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
475 <                        if (desc[j].wakeup < wakeup_time)
475 >                        if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
476                                  wakeup_time = desc[j].wakeup;
477          }
478 + #ifdef PRECISE_TIMING_BEOS
479          release_sem(wakeup_time_sem);
480          thread_info info;
481          do {
# Line 348 | Line 483 | int16 PrimeTime(uint32 tm, int32 time)
483                  get_thread_info(timer_thread, &info);
484          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
485   #endif
486 + #ifdef PRECISE_TIMING_POSIX
487 +        pthread_mutex_unlock(&wakeup_time_lock);
488 +        timer_thread_resume();
489 +        assert(suspend_count == 0);
490 + #endif
491 + #endif
492          return 0;
493   }
494  
495  
355 #if PRECISE_TIMING
496   /*
497   *  Time Manager thread
498   */
499  
500 + #ifdef PRECISE_TIMING_BEOS
501   static int32 timer_func(void *arg)
502   {
503          while (thread_active) {
# Line 378 | Line 519 | static int32 timer_func(void *arg)
519   }
520   #endif
521  
522 + #ifdef PRECISE_TIMING_POSIX
523 + static void *timer_func(void *arg)
524 + {
525 +        while (!timer_thread_cancel) {
526 +
527 +                // Wait until time specified by wakeup_time
528 +                clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
529 +
530 +                tm_time_t system_time;
531 +                timer_current_time(system_time);
532 +                if (timer_cmp_time(wakeup_time, system_time) < 0) {
533 +
534 +                        // Timer expired, trigger interrupt
535 +                        pthread_mutex_lock(&wakeup_time_lock);
536 +                        wakeup_time = wakeup_time_max;
537 +                        pthread_mutex_unlock(&wakeup_time_lock);
538 +                        SetInterruptFlag(INTFLAG_TIMER);
539 +                        TriggerInterrupt();
540 +                }
541 +        }
542 +        return NULL;
543 + }
544 + #endif
545 +
546  
547   /*
548   *  Timer interrupt function (executed as part of 60Hz interrupt)
# Line 414 | Line 579 | void TimerInterrupt(void)
579  
580   #if PRECISE_TIMING
581          // Look for next task to be called and set wakeup_time
582 + #if PRECISE_TIMING_BEOS
583          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
584          suspend_thread(timer_thread);
585 <        wakeup_time = 0x7fffffffffffffff;
585 > #endif
586 > #if PRECISE_TIMING_POSIX
587 >        timer_thread_suspend();
588 >        pthread_mutex_lock(&wakeup_time_lock);
589 > #endif
590 >        wakeup_time = wakeup_time_max;
591          for (int j=0; j<NUM_DESCS; j++) {
592                  if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
593 <                        if (desc[j].wakeup < wakeup_time)
593 >                        if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
594                                  wakeup_time = desc[j].wakeup;
595          }
596 + #if PRECISE_TIMING_BEOS
597          release_sem(wakeup_time_sem);
598          thread_info info;
599          do {
# Line 429 | Line 601 | void TimerInterrupt(void)
601                  get_thread_info(timer_thread, &info);
602          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
603   #endif
604 + #if PRECISE_TIMING_POSIX
605 +        pthread_mutex_unlock(&wakeup_time_lock);
606 +        timer_thread_resume();
607 +        assert(suspend_count == 0);
608 + #endif
609 + #endif
610   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines