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.9 by asvitkine, 2009-07-31T19:41:50Z

# 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 28 | Line 28
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  
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 + #endif
81  
82  
83   /*
# Line 143 | Line 154 | static void dequeue_tm(uint32 tm)
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 +        sigemptyset(&sigsuspend_action.sa_mask);
189 +        sigaddset(&sigsuspend_action.sa_mask, SIGRESUME);
190 +        sigsuspend_action.sa_handler = sigsuspend_handler;
191 +        sigsuspend_action.sa_flags = SA_RESTART;
192 + #ifdef HAVE_SIGNAL_SA_RESTORER
193 +        sigsuspend_action.sa_restorer = NULL;
194 + #endif
195 +        if (sigaction(SIGSUSPEND, &sigsuspend_action, NULL) < 0)
196 +                return false;
197 +
198 +        // Install resume signal handler
199 +        sigemptyset(&sigresume_action.sa_mask);
200 +        sigresume_action.sa_handler = sigresume_handler;
201 +        sigresume_action.sa_flags = SA_RESTART;
202 + #ifdef HAVE_SIGNAL_SA_RESTORER
203 +        sigresume_action.sa_restorer = NULL;
204 + #endif
205 +        if (sigaction(SIGRESUME, &sigresume_action, NULL) < 0)
206 +                return false;
207 +
208 +        // Initialize semaphore
209 +        if (sem_init(&suspend_ack_sem, 0, 0) < 0)
210 +                return false;
211 +
212 +        // Initialize suspend_handler_mask, it excludes SIGRESUME
213 +        if (sigfillset(&suspend_handler_mask) != 0)
214 +                return false;
215 +        if (sigdelset(&suspend_handler_mask, SIGRESUME) != 0)
216 +                return false;
217 +
218 +        // Create thread in running state
219 +        suspend_count = 0;
220 +        return (pthread_create(&timer_thread, NULL, timer_func, NULL) == 0);
221 + }
222 +
223 + // Kill timer thread
224 + static void timer_thread_kill(void)
225 + {
226 +        timer_thread_cancel = true;
227 + #ifdef HAVE_PTHREAD_CANCEL
228 +        pthread_cancel(timer_thread);
229 + #endif
230 +        pthread_join(timer_thread, NULL);
231 + }
232 +
233 + // Suspend timer thread
234 + static void timer_thread_suspend(void)
235 + {
236 +        pthread_mutex_lock(&suspend_count_lock);
237 +        if (suspend_count == 0) {
238 +                suspend_count ++;
239 +                if (pthread_kill(timer_thread, SIGSUSPEND) == 0)
240 +                        sem_wait(&suspend_ack_sem);
241 +        }
242 +        pthread_mutex_unlock(&suspend_count_lock);
243 + }
244 +
245 + // Resume timer thread
246 + static void timer_thread_resume(void)
247 + {
248 +        pthread_mutex_lock(&suspend_count_lock);
249 +        assert(suspend_count > 0);
250 +        if (suspend_count == 1) {
251 +                suspend_count = 0;
252 +                pthread_kill(timer_thread, SIGRESUME);
253 +        }
254 +        pthread_mutex_unlock(&suspend_count_lock);
255 + }
256 + #endif
257 +
258 +
259 + /*
260   *  Initialize Time Manager
261   */
262  
# Line 154 | Line 268 | void TimerInit(void)
268  
269   #if PRECISE_TIMING
270          // Start timer thread
271 + #ifdef PRECISE_TIMING_BEOS
272          wakeup_time_sem = create_sem(1, "Wakeup Time");
273          timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
274          resume_thread(timer_thread);
275   #endif
276 + #ifdef PRECISE_TIMING_POSIX
277 +        timer_thread_active = timer_thread_init();
278 + #endif
279 + #endif
280   }
281  
282  
# Line 170 | Line 289 | void TimerExit(void)
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 + #endif
303          }
304   #endif
305   }
# Line 228 | Line 352 | int16 RmvTime(uint32 tm)
352          }
353  
354          // Task active?
355 < #if PRECISE_TIMING
355 > #if PRECISE_TIMING_BEOS
356          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
357          suspend_thread(timer_thread);
358   #endif
359 + #if PRECISE_TIMING_POSIX
360 +        timer_thread_suspend();
361 +        pthread_mutex_lock(&wakeup_time_lock);
362 + #endif
363          if (ReadMacInt16(tm + qType) & 0x8000) {
364  
365                  // Yes, make task inactive and remove it from the Time Manager queue
# Line 239 | Line 367 | int16 RmvTime(uint32 tm)
367                  dequeue_tm(tm);
368   #if PRECISE_TIMING
369                  // Look for next task to be called and set wakeup_time
370 <                wakeup_time = 0x7fffffffffffffff;
370 >                wakeup_time = wakeup_time_max;
371                  for (int j=0; j<NUM_DESCS; j++) {
372                          if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
373 <                                if (desc[j].wakeup < wakeup_time)
373 >                                if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
374                                          wakeup_time = desc[j].wakeup;
375                  }
376   #endif
# Line 255 | Line 383 | int16 RmvTime(uint32 tm)
383          } else
384                  WriteMacInt32(tm + tmCount, 0);
385          D(bug(" tmCount %ld\n", ReadMacInt32(tm + tmCount)));
386 < #if PRECISE_TIMING
386 > #if PRECISE_TIMING_BEOS
387          release_sem(wakeup_time_sem);
388          thread_info info;
389          do {
# Line 263 | Line 391 | int16 RmvTime(uint32 tm)
391                  get_thread_info(timer_thread, &info);
392          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
393   #endif
394 + #if PRECISE_TIMING_POSIX
395 +        pthread_mutex_unlock(&wakeup_time_lock);
396 +        timer_thread_resume();
397 +        assert(suspend_count == 0);
398 + #endif
399  
400          // Free descriptor
401          free_desc(i);
# Line 295 | Line 428 | int16 PrimeTime(uint32 tm, int32 time)
428                  // Yes, tmWakeUp set?
429                  if (ReadMacInt32(tm + tmWakeUp)) {
430  
431 +                        // PrimeTime(0) can either mean (a) "the task runs as soon as interrupts are enabled"
432 +                        // or (b) "continue previous delay" if an expired task was stopped via RmvTime() and
433 +                        // then re-installed using InsXTime(). This currently only handles (a).
434 +                        //
435 +                        // API reference: http://developer.apple.com/documentation/mac/Processes/Processes-68.html
436 +
437 + #if 0
438                          //!! PrimeTime(0) means continue previous delay
439                          // (save wakeup time in RmvTime?)
440                          if (time == 0) {
441                                  printf("FATAL: Unsupported PrimeTime(0)\n");
442                                  return 0;
443                          }
444 + #endif
445  
446                          // Yes, calculate wakeup time relative to last scheduled time
447                          tm_time_t wakeup;
# Line 327 | Line 468 | int16 PrimeTime(uint32 tm, int32 time)
468          }
469  
470          // Make task active and enqueue it in the Time Manager queue
471 < #if PRECISE_TIMING
471 > #if PRECISE_TIMING_BEOS
472          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
473          suspend_thread(timer_thread);
474   #endif
475 + #if PRECISE_TIMING_POSIX
476 +        timer_thread_suspend();
477 +        pthread_mutex_lock(&wakeup_time_lock);
478 + #endif
479          WriteMacInt16(tm + qType, ReadMacInt16(tm + qType) | 0x8000);
480          enqueue_tm(tm);
481   #if PRECISE_TIMING
482          // Look for next task to be called and set wakeup_time
483 <        wakeup_time = 0x7fffffffffffffff;
483 >        wakeup_time = wakeup_time_max;
484          for (int j=0; j<NUM_DESCS; j++) {
485                  if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
486 <                        if (desc[j].wakeup < wakeup_time)
486 >                        if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
487                                  wakeup_time = desc[j].wakeup;
488          }
489 + #ifdef PRECISE_TIMING_BEOS
490          release_sem(wakeup_time_sem);
491          thread_info info;
492          do {
# Line 348 | Line 494 | int16 PrimeTime(uint32 tm, int32 time)
494                  get_thread_info(timer_thread, &info);
495          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
496   #endif
497 + #ifdef PRECISE_TIMING_POSIX
498 +        pthread_mutex_unlock(&wakeup_time_lock);
499 +        timer_thread_resume();
500 +        assert(suspend_count == 0);
501 + #endif
502 + #endif
503          return 0;
504   }
505  
506  
355 #if PRECISE_TIMING
507   /*
508   *  Time Manager thread
509   */
510  
511 + #ifdef PRECISE_TIMING_BEOS
512   static int32 timer_func(void *arg)
513   {
514          while (thread_active) {
# Line 378 | Line 530 | static int32 timer_func(void *arg)
530   }
531   #endif
532  
533 + #ifdef PRECISE_TIMING_POSIX
534 + static void *timer_func(void *arg)
535 + {
536 +        while (!timer_thread_cancel) {
537 +
538 +                // Wait until time specified by wakeup_time
539 +                clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
540 +
541 +                tm_time_t system_time;
542 +                timer_current_time(system_time);
543 +                if (timer_cmp_time(wakeup_time, system_time) < 0) {
544 +
545 +                        // Timer expired, trigger interrupt
546 +                        pthread_mutex_lock(&wakeup_time_lock);
547 +                        wakeup_time = wakeup_time_max;
548 +                        pthread_mutex_unlock(&wakeup_time_lock);
549 +                        SetInterruptFlag(INTFLAG_TIMER);
550 +                        TriggerInterrupt();
551 +                }
552 +        }
553 +        return NULL;
554 + }
555 + #endif
556 +
557  
558   /*
559   *  Timer interrupt function (executed as part of 60Hz interrupt)
# Line 414 | Line 590 | void TimerInterrupt(void)
590  
591   #if PRECISE_TIMING
592          // Look for next task to be called and set wakeup_time
593 + #if PRECISE_TIMING_BEOS
594          while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
595          suspend_thread(timer_thread);
596 <        wakeup_time = 0x7fffffffffffffff;
596 > #endif
597 > #if PRECISE_TIMING_POSIX
598 >        timer_thread_suspend();
599 >        pthread_mutex_lock(&wakeup_time_lock);
600 > #endif
601 >        wakeup_time = wakeup_time_max;
602          for (int j=0; j<NUM_DESCS; j++) {
603                  if (desc[j].in_use && (ReadMacInt16(desc[j].task + qType) & 0x8000))
604 <                        if (desc[j].wakeup < wakeup_time)
604 >                        if (timer_cmp_time(desc[j].wakeup, wakeup_time) < 0)
605                                  wakeup_time = desc[j].wakeup;
606          }
607 + #if PRECISE_TIMING_BEOS
608          release_sem(wakeup_time_sem);
609          thread_info info;
610          do {
# Line 429 | Line 612 | void TimerInterrupt(void)
612                  get_thread_info(timer_thread, &info);
613          } while (info.state == B_THREAD_SUSPENDED);     // Sometimes, resume_thread() doesn't work (BeOS bug?)
614   #endif
615 + #if PRECISE_TIMING_POSIX
616 +        pthread_mutex_unlock(&wakeup_time_lock);
617 +        timer_thread_resume();
618 +        assert(suspend_count == 0);
619 + #endif
620 + #endif
621   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines