ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/timer_unix.cpp
Revision: 1.19
Committed: 2006-05-01T13:13:18Z (18 years, 6 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19
Changes since 1.18: +6 -2 lines
Log Message:
improve locks

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * timer_unix.cpp - Time Manager emulation, Unix specific stuff
3     *
4 gbeauche 1.15 * Basilisk II (C) 1997-2005 Christian Bauer
5 cebix 1.1 *
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     #include "sysdeps.h"
22 cebix 1.10 #include "macos_util.h"
23 cebix 1.1 #include "timer.h"
24    
25 cebix 1.11 #include <errno.h>
26    
27 cebix 1.1 #define DEBUG 0
28     #include "debug.h"
29    
30 cebix 1.8 // For NetBSD with broken pthreads headers
31     #ifndef CLOCK_REALTIME
32     #define CLOCK_REALTIME 0
33     #endif
34    
35 cebix 1.1
36     /*
37     * Return microseconds since boot (64 bit)
38     */
39    
40     void Microseconds(uint32 &hi, uint32 &lo)
41     {
42     D(bug("Microseconds\n"));
43     #ifdef HAVE_CLOCK_GETTIME
44     struct timespec t;
45     clock_gettime(CLOCK_REALTIME, &t);
46     uint64 tl = (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
47     #else
48     struct timeval t;
49     gettimeofday(&t, NULL);
50     uint64 tl = (uint64)t.tv_sec * 1000000 + t.tv_usec;
51     #endif
52     hi = tl >> 32;
53     lo = tl;
54     }
55    
56    
57     /*
58     * Return local date/time in Mac format (seconds since 1.1.1904)
59     */
60    
61     uint32 TimerDateTime(void)
62     {
63 cebix 1.10 return TimeToMacTime(time(NULL));
64 cebix 1.1 }
65    
66    
67     /*
68     * Get current time
69     */
70    
71     void timer_current_time(tm_time_t &t)
72     {
73     #ifdef HAVE_CLOCK_GETTIME
74     clock_gettime(CLOCK_REALTIME, &t);
75     #else
76     gettimeofday(&t, NULL);
77     #endif
78     }
79    
80    
81     /*
82     * Add times
83     */
84    
85     void timer_add_time(tm_time_t &res, tm_time_t a, tm_time_t b)
86     {
87     #ifdef HAVE_CLOCK_GETTIME
88     res.tv_sec = a.tv_sec + b.tv_sec;
89     res.tv_nsec = a.tv_nsec + b.tv_nsec;
90     if (res.tv_nsec >= 1000000000) {
91     res.tv_sec++;
92     res.tv_nsec -= 1000000000;
93     }
94     #else
95     res.tv_sec = a.tv_sec + b.tv_sec;
96     res.tv_usec = a.tv_usec + b.tv_usec;
97     if (res.tv_usec >= 1000000) {
98     res.tv_sec++;
99     res.tv_usec -= 1000000;
100     }
101     #endif
102     }
103    
104    
105     /*
106     * Subtract times
107     */
108    
109     void timer_sub_time(tm_time_t &res, tm_time_t a, tm_time_t b)
110     {
111     #ifdef HAVE_CLOCK_GETTIME
112     res.tv_sec = a.tv_sec - b.tv_sec;
113     res.tv_nsec = a.tv_nsec - b.tv_nsec;
114     if (res.tv_nsec < 0) {
115     res.tv_sec--;
116     res.tv_nsec += 1000000000;
117     }
118     #else
119     res.tv_sec = a.tv_sec - b.tv_sec;
120     res.tv_usec = a.tv_usec - b.tv_usec;
121     if (res.tv_usec < 0) {
122     res.tv_sec--;
123     res.tv_usec += 1000000;
124     }
125     #endif
126     }
127    
128    
129     /*
130     * Compare times (<0: a < b, =0: a = b, >0: a > b)
131     */
132    
133     int timer_cmp_time(tm_time_t a, tm_time_t b)
134     {
135     #ifdef HAVE_CLOCK_GETTIME
136     if (a.tv_sec == b.tv_sec)
137     return a.tv_nsec - b.tv_nsec;
138     else
139     return a.tv_sec - b.tv_sec;
140     #else
141     if (a.tv_sec == b.tv_sec)
142     return a.tv_usec - b.tv_usec;
143     else
144     return a.tv_sec - b.tv_sec;
145     #endif
146     }
147    
148    
149     /*
150     * Convert Mac time value (>0: microseconds, <0: microseconds) to tm_time_t
151     */
152    
153     void timer_mac2host_time(tm_time_t &res, int32 mactime)
154     {
155     #ifdef HAVE_CLOCK_GETTIME
156     if (mactime > 0) {
157     // Time in milliseconds
158     res.tv_sec = mactime / 1000;
159     res.tv_nsec = (mactime % 1000) * 1000000;
160     } else {
161     // Time in negative microseconds
162     res.tv_sec = -mactime / 1000000;
163     res.tv_nsec = (-mactime % 1000000) * 1000;
164     }
165     #else
166     if (mactime > 0) {
167     // Time in milliseconds
168     res.tv_sec = mactime / 1000;
169     res.tv_usec = (mactime % 1000) * 1000;
170     } else {
171     // Time in negative microseconds
172     res.tv_sec = -mactime / 1000000;
173     res.tv_usec = -mactime % 1000000;
174     }
175     #endif
176     }
177    
178    
179     /*
180     * Convert positive tm_time_t to Mac time value (>0: microseconds, <0: microseconds)
181     * A negative input value for hosttime results in a zero return value
182     * As long as the microseconds value fits in 32 bit, it must not be converted to milliseconds!
183     */
184    
185     int32 timer_host2mac_time(tm_time_t hosttime)
186     {
187     if (hosttime.tv_sec < 0)
188     return 0;
189     else {
190     #ifdef HAVE_CLOCK_GETTIME
191     uint64 t = (uint64)hosttime.tv_sec * 1000000 + hosttime.tv_nsec / 1000;
192     #else
193     uint64 t = (uint64)hosttime.tv_sec * 1000000 + hosttime.tv_usec;
194     #endif
195     if (t > 0x7fffffff)
196     return t / 1000; // Time in milliseconds
197     else
198     return -t; // Time in negative microseconds
199     }
200 cebix 1.11 }
201    
202    
203     /*
204     * Get current value of microsecond timer
205     */
206    
207     uint64 GetTicks_usec(void)
208     {
209     #ifdef HAVE_CLOCK_GETTIME
210     struct timespec t;
211     clock_gettime(CLOCK_REALTIME, &t);
212     return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
213     #else
214     struct timeval t;
215     gettimeofday(&t, NULL);
216     return (uint64)t.tv_sec * 1000000 + t.tv_usec;
217     #endif
218     }
219    
220    
221     /*
222     * Delay by specified number of microseconds (<1 second)
223     * (adapted from SDL_Delay() source; this function is designed to provide
224     * the highest accuracy possible)
225     */
226    
227     #if defined(linux)
228     // Linux select() changes its timeout parameter upon return to contain
229     // the remaining time. Most other unixen leave it unchanged or undefined.
230     #define SELECT_SETS_REMAINING
231 gbeauche 1.16 #elif defined(__FreeBSD__) || defined(__sun__) || (defined(__MACH__) && defined(__APPLE__))
232 cebix 1.11 #define USE_NANOSLEEP
233     #elif defined(HAVE_PTHREADS) && defined(sgi)
234     // SGI pthreads has a bug when using pthreads+signals+nanosleep,
235     // so instead of using nanosleep, wait on a CV which is never signalled.
236 cebix 1.12 #include <pthread.h>
237 cebix 1.11 #define USE_COND_TIMEDWAIT
238     #endif
239    
240     void Delay_usec(uint32 usec)
241     {
242     int was_error;
243    
244     #if defined(USE_NANOSLEEP)
245     struct timespec elapsed, tv;
246     #elif defined(USE_COND_TIMEDWAIT)
247     // Use a local mutex and cv, so threads remain independent
248     pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER;
249     pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER;
250     struct timespec elapsed;
251     uint64 future;
252     #else
253     struct timeval tv;
254     #ifndef SELECT_SETS_REMAINING
255     uint64 then, now, elapsed;
256     #endif
257     #endif
258    
259     // Set the timeout interval - Linux only needs to do this once
260     #if defined(SELECT_SETS_REMAINING)
261     tv.tv_sec = 0;
262     tv.tv_usec = usec;
263     #elif defined(USE_NANOSLEEP)
264     elapsed.tv_sec = 0;
265     elapsed.tv_nsec = usec * 1000;
266     #elif defined(USE_COND_TIMEDWAIT)
267     future = GetTicks_usec() + usec;
268     elapsed.tv_sec = future / 1000000;
269     elapsed.tv_nsec = (future % 1000000) * 1000;
270     #else
271     then = GetTicks_usec();
272     #endif
273    
274     do {
275     errno = 0;
276     #if defined(USE_NANOSLEEP)
277     tv.tv_sec = elapsed.tv_sec;
278     tv.tv_nsec = elapsed.tv_nsec;
279     was_error = nanosleep(&tv, &elapsed);
280     #elif defined(USE_COND_TIMEDWAIT)
281     was_error = pthread_mutex_lock(&delay_mutex);
282     was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed);
283     was_error = pthread_mutex_unlock(&delay_mutex);
284     #else
285     #ifndef SELECT_SETS_REMAINING
286     // Calculate the time interval left (in case of interrupt)
287     now = GetTicks_usec();
288     elapsed = now - then;
289     then = now;
290     if (elapsed >= usec)
291     break;
292     usec -= elapsed;
293     tv.tv_sec = 0;
294     tv.tv_usec = usec;
295     #endif
296     was_error = select(0, NULL, NULL, NULL, &tv);
297     #endif
298     } while (was_error && (errno == EINTR));
299 cebix 1.1 }
300 gbeauche 1.17
301    
302     /*
303     * Suspend emulator thread, virtual CPU in idle mode
304     */
305    
306     #ifdef HAVE_PTHREADS
307     #if defined(HAVE_PTHREAD_COND_INIT)
308     #define IDLE_USES_COND_WAIT 1
309     static pthread_mutex_t idle_lock = PTHREAD_MUTEX_INITIALIZER;
310     static pthread_cond_t idle_cond = PTHREAD_COND_INITIALIZER;
311     #elif defined(HAVE_SEM_INIT)
312     #define IDLE_USES_SEMAPHORE 1
313     #include <semaphore.h>
314     #ifdef HAVE_SPINLOCKS
315     static spinlock_t idle_lock = SPIN_LOCK_UNLOCKED;
316     #define LOCK_IDLE spin_lock(&idle_lock)
317     #define UNLOCK_IDLE spin_unlock(&idle_lock)
318     #else
319     static pthread_mutex_t idle_lock = PTHREAD_MUTEX_INITIALIZER;
320     #define LOCK_IDLE pthread_mutex_lock(&idle_lock)
321     #define UNLOCK_IDLE pthread_mutex_unlock(&idle_lock)
322     #endif
323     static sem_t idle_sem;
324     static int idle_sem_ok = -1;
325     #endif
326     #endif
327    
328     void idle_wait(void)
329     {
330     #ifdef IDLE_USES_COND_WAIT
331 gbeauche 1.18 pthread_mutex_lock(&idle_lock);
332 gbeauche 1.17 pthread_cond_wait(&idle_cond, &idle_lock);
333 gbeauche 1.18 pthread_mutex_unlock(&idle_lock);
334 gbeauche 1.17 #else
335     #ifdef IDLE_USES_SEMAPHORE
336 gbeauche 1.19 LOCK_IDLE;
337 gbeauche 1.17 if (idle_sem_ok < 0)
338     idle_sem_ok = (sem_init(&idle_sem, 0, 0) == 0);
339     if (idle_sem_ok > 0) {
340     idle_sem_ok++;
341     UNLOCK_IDLE;
342     sem_wait(&idle_sem);
343     return;
344     }
345 gbeauche 1.19 UNLOCK_IDLE;
346 gbeauche 1.17 #endif
347 gbeauche 1.19
348     // Fallback: sleep 10 ms
349 gbeauche 1.17 Delay_usec(10000);
350     #endif
351     }
352    
353    
354     /*
355     * Resume execution of emulator thread, events just arrived
356     */
357    
358     void idle_resume(void)
359     {
360     #ifdef IDLE_USES_COND_WAIT
361     pthread_cond_signal(&idle_cond);
362     #else
363     #ifdef IDLE_USES_SEMAPHORE
364 gbeauche 1.19 LOCK_IDLE;
365 gbeauche 1.17 if (idle_sem_ok > 1) {
366     idle_sem_ok--;
367     UNLOCK_IDLE;
368     sem_post(&idle_sem);
369     return;
370     }
371 gbeauche 1.19 UNLOCK_IDLE;
372 gbeauche 1.17 #endif
373     #endif
374     }