5b10a5bbbc3f34cb1c65ad5f6196fa3e3214adf0
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.22 2001/03/02 10:52:15 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 "RtsFlags.h"
27 #include "Itimer.h"
28 #include "Proftimer.h"
29 #include "Schedule.h"
30
31 /* As recommended in the autoconf manual */
32 # ifdef TIME_WITH_SYS_TIME
33 #  include <sys/time.h>
34 #  include <time.h>
35 # else
36 #  ifdef HAVE_SYS_TIME_H
37 #   include <sys/time.h>
38 #  else
39 #   include <time.h>
40 #  endif
41 # endif
42
43 #if HAVE_WINDOWS_H
44 # include <windows.h>
45 #endif
46  
47 lnat total_ticks = 0;
48
49 /* ticks left before next pre-emptive context switch */
50 int ticks_to_ctxt_switch = 0;
51
52 /* -----------------------------------------------------------------------------
53    Tick handler
54
55    We use the ticker for time profiling.
56
57    SMP note: this signal could be delivered to *any* thread.  We have
58    to ensure that it doesn't matter which thread actually runs the
59    signal handler.
60    -------------------------------------------------------------------------- */
61
62 static
63 void
64 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
65 CALLBACK
66 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
67             DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
68 #else
69 handle_tick(int unused STG_UNUSED)
70 #endif
71 {
72   total_ticks++;
73
74 #ifdef PROFILING
75   handleProfTick();
76 #endif
77
78   /* so we can get a rough indication of the current time at any point
79    * without having to call gettimeofday() (see Select.c):
80    */
81   ticks_since_timestamp++;
82
83   if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0) {
84       ticks_to_ctxt_switch--;
85       if (ticks_to_ctxt_switch <= 0) {
86           ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
87           context_switch = 1;   /* schedule a context switch */
88       }
89   }
90 }
91
92
93 /*
94  * Handling timer events under cygwin32 is not done with signal/setitimer.
95  * Instead of the two steps of first registering a signal handler to handle
96  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
97  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
98  * creates a separate thread that will notify the main thread of timer
99  * expiry). -- SOF 7/96
100  *
101  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
102  */
103
104 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
105
106 LPTIMECALLBACK vtalrm_cback;
107
108 nat
109 initialize_virtual_timer(nat ms)
110 {
111   /* On Win32 setups that don't have support for
112      setitimer(), we use the MultiMedia API's timer
113      support.
114      
115      The delivery of ticks isn't free; the performance hit should be checked.
116   */
117   unsigned int delay;
118   static unsigned int vtalrm_id;
119  
120   if (ms) {
121     delay = timeBeginPeriod(1);
122     if (delay == TIMERR_NOCANDO) { /* error of some sort. */
123       return delay;
124     }
125     vtalrm_id =
126       timeSetEvent(ms,      /* event every `delay' milliseconds. */
127                    1,       /* precision is within 1 ms */
128                    vtalrm_cback,
129                    TIME_CALLBACK_FUNCTION,     /* ordinary callback */
130                    TIME_PERIODIC);
131   } else {
132     timeKillEvent(vtalrm_id);
133     timeEndPeriod(1);
134   }
135
136   return 0;
137 }
138  
139 #else
140
141 nat
142 initialize_virtual_timer(nat ms)
143 {
144 # ifndef HAVE_SETITIMER
145   /*    fprintf(stderr, "No virtual timer on this system\n"); */
146     return -1;
147 # else
148     struct itimerval it;
149
150     timestamp = getourtimeofday();
151     ticks_since_timestamp = 0;
152
153     it.it_value.tv_sec = ms / 1000;
154     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
155     it.it_interval = it.it_value;
156     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
157 # endif
158 }
159
160 #endif /* !cygwin32_TARGET_OS */
161
162 # if 0
163 /* This is a potential POSIX version */
164 nat
165 initialize_virtual_timer(nat ms)
166 {
167     struct sigevent se;
168     struct itimerspec it;
169     timer_t tid;
170
171     timestamp = getourtimeofday();
172     ticks_since_timestamp = 0;
173
174     se.sigev_notify = SIGEV_SIGNAL;
175     se.sigev_signo = SIGVTALRM;
176     se.sigev_value.sival_int = SIGVTALRM;
177     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
178         barf("can't create virtual timer");
179     }
180     it.it_value.tv_sec = ms / 1000;
181     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
182     it.it_interval = it.it_value;
183     timer_settime(tid, TIMER_RELTIME, &it, NULL);
184 }
185 # endif
186
187 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
188 int
189 install_vtalrm_handler(void)
190 {
191   vtalrm_cback = handle_tick;
192   return 0;
193 }
194
195 #else
196 int
197 install_vtalrm_handler(void)
198 {
199     struct sigaction action;
200
201     action.sa_handler = handle_tick;
202
203     sigemptyset(&action.sa_mask);
204     action.sa_flags = 0;
205
206     return sigaction(SIGVTALRM, &action, NULL);
207 }
208
209 void
210 block_vtalrm_signal(void)
211 {
212     sigset_t signals;
213     
214     sigemptyset(&signals);
215     sigaddset(&signals, SIGVTALRM);
216
217     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
218 }
219
220 void
221 unblock_vtalrm_signal(void)
222 {
223     sigset_t signals;
224     
225     sigemptyset(&signals);
226     sigaddset(&signals, SIGVTALRM);
227
228     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
229 }
230 #endif
231
232 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
233  * only calling it 50 times/s, it shouldn't have any great impact.
234  */
235 #if !defined(mingw32_TARGET_OS)
236 unsigned int 
237 getourtimeofday(void)
238 {
239   struct timeval tv;
240   gettimeofday(&tv, (struct timezone *) NULL);
241   return (tv.tv_sec * TICK_FREQUENCY +
242           tv.tv_usec * TICK_FREQUENCY / 1000000);
243 }
244 #else
245 unsigned int
246 getourtimeofday(void)
247 {
248   return ((unsigned int)GetTickCount() * TICK_FREQUENCY) / 1000;
249 }
250 #endif