60e30e786d649780815d0e8d0d21faabc00118ce
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.13 2000/04/03 15:54:49 simonmar Exp $
3  *
4  * (c) The GHC Team, 1995-1999
5  *
6  * Interval timer for profiling and pre-emptive scheduling.
7  *
8  * ---------------------------------------------------------------------------*/
9
10 /*
11  * The interval timer is used for profiling and for context switching in the
12  * threaded build.  Though POSIX 1003.1b includes a standard interface for
13  * such things, no one really seems to be implementing them yet.  Even 
14  * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're
15  * keen on getting access to @CLOCK_VIRTUAL@.
16  * 
17  * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
18  * to support.  So much for standards.
19  */
20
21 #if !defined(_AIX)
22 # define NON_POSIX_SOURCE
23 #endif
24
25 #include "Rts.h"
26 #include "Itimer.h"
27 #include "Proftimer.h"
28 #include "Schedule.h"
29
30 /* As recommended in the autoconf manual */
31 # ifdef TIME_WITH_SYS_TIME
32 #  include <sys/time.h>
33 #  include <time.h>
34 # else
35 #  ifdef HAVE_SYS_TIME_H
36 #   include <sys/time.h>
37 #  else
38 #   include <time.h>
39 #  endif
40 # endif
41
42 #if HAVE_WINDOWS_H
43 # include <windows.h>
44 #endif
45  
46 lnat total_ticks = 0;
47
48 static
49 void
50 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
51 CALLBACK
52 #endif
53 handle_tick(int unused STG_UNUSED);
54
55 /* -----------------------------------------------------------------------------
56    Tick handler
57
58    We use the ticker for two things: supporting threadDelay, and time
59    profiling.
60
61    SMP note: this signal could be delivered to *any* thread.  We have
62    to ensure that it doesn't matter which thread actually runs the
63    signal handler.
64    -------------------------------------------------------------------------- */
65
66 static
67 void
68 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
69 CALLBACK
70 #endif
71 handle_tick(int unused STG_UNUSED)
72 {
73   total_ticks++;
74
75 #ifdef PROFILING
76   handleProfTick();
77 #endif
78
79   /* For threadDelay etc., see Select.c */
80   ticks_since_select++;
81 }
82
83
84 /*
85  * Handling timer events under cygwin32 is not done with signal/setitimer.
86  * Instead of the two steps of first registering a signal handler to handle
87  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
88  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
89  * creates a separate thread that will notify the main thread of timer
90  * expiry). -- SOF 7/96
91  *
92  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
93  */
94
95 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
96
97 /* 
98  * Sigh - to avoid requiring anyone that wants to build ghc to have
99  * to augment the Win32 header files that comes with cygwinb20.1,
100  * include the missing MM API decls here inline.
101  *
102  * ToDo: check and remove these once the next version of cygwin is
103  * released.
104  */
105 #define TIMERR_NOERROR   0
106 #define TIMERR_NOCANDO   97
107 #define TIME_PERIODIC    1
108
109 typedef UINT MMRESULT;
110 typedef void CALLBACK (*TIMECALLBACK) (UINT, UINT, DWORD, DWORD, DWORD);
111 typedef TIMECALLBACK *LPTIMECALLBACK;
112 MMRESULT STDCALL  timeSetEvent(UINT, UINT, LPTIMECALLBACK, DWORD, UINT);
113 /*
114   vtalrm_handler is assigned and set up in Signals.c
115
116   vtalrm_id (defined in Signals.c) holds
117   the system id for the current timer (used to 
118   later block/kill it.)
119 */
120 extern nat vtalrm_id;
121 TIMECALLBACK *vtalrm_cback;
122  
123 nat
124 initialize_virtual_timer(nat ms)
125 {
126 # ifdef PROFILING
127   /* On Win32 setups that don't have support for
128      setitimer(), we use the MultiMedia API's timer
129      support.
130      
131      As the delivery of ticks isn't free, we only
132      enable it if we really needed, i.e., when profiling.
133      (the RTS now also needs timer ticks to implement
134      threadDelay in non-profiling mode, but the pure
135      Win32 port doesn't support that.....yet.)
136   */
137   unsigned int delay,vtalrm_id;
138  
139   delay = timeBeginPeriod(1);
140   if (delay == TIMERR_NOCANDO) { /* error of some sort. */
141      return delay;
142   }
143   vtalrm_id =
144     timeSetEvent(ms,     /* event every `delay' milliseconds. */
145                 1,       /* precision is within 5 millisecs. */
146                 (LPTIMECALLBACK)vtalrm_cback,
147                 0,
148                 TIME_PERIODIC);
149 # endif
150   return 0;
151 }
152  
153 #else
154
155 nat
156 initialize_virtual_timer(nat ms)
157 {
158 # ifndef HAVE_SETITIMER
159   /*    fprintf(stderr, "No virtual timer on this system\n"); */
160     return -1;
161 # else
162     struct itimerval it;
163
164     it.it_value.tv_sec = ms / 1000;
165     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
166     it.it_interval = it.it_value;
167     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
168 # endif
169 }
170
171 #endif /* !cygwin32_TARGET_OS */
172
173 # if 0
174 /* This is a potential POSIX version */
175 nat
176 initialize_virtual_timer(nat ms)
177 {
178     struct sigevent se;
179     struct itimerspec it;
180     timer_t tid;
181
182     se.sigev_notify = SIGEV_SIGNAL;
183     se.sigev_signo = SIGVTALRM;
184     se.sigev_value.sival_int = SIGVTALRM;
185     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
186         barf("can't create virtual timer");
187     }
188     it.it_value.tv_sec = ms / 1000;
189     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
190     it.it_interval = it.it_value;
191     timer_settime(tid, TIMER_RELTIME, &it, NULL);
192 }
193 # endif
194
195 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
196 int
197 install_vtalrm_handler(void)
198 {
199   vtalrm_cback = handle_tick;
200   return 0;
201 }
202
203 #else
204 int
205 install_vtalrm_handler(void)
206 {
207     struct sigaction action;
208
209     action.sa_handler = handle_tick;
210
211     sigemptyset(&action.sa_mask);
212     action.sa_flags = 0;
213
214     return sigaction(SIGVTALRM, &action, NULL);
215 }
216
217 void
218 block_vtalrm_signal(void)
219 {
220     sigset_t signals;
221     
222     sigemptyset(&signals);
223     sigaddset(&signals, SIGVTALRM);
224
225     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
226 }
227
228 void
229 unblock_vtalrm_signal(void)
230 {
231     sigset_t signals;
232     
233     sigemptyset(&signals);
234     sigaddset(&signals, SIGVTALRM);
235
236     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
237 }
238 #endif
239
240 #if !defined(HAVE_SETITIMER) && !defined(mingw32_TARGET_OS)
241 unsigned int 
242 getourtimeofday(void)
243 {
244   struct timeval tv;
245   gettimeofday(&tv, (struct timezone *) NULL);
246   return (tv.tv_sec * 1000000 + tv.tv_usec);
247 }
248 #endif