[project @ 2001-02-27 12:43:45 by rrt]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.21 2001/02/27 12:43:45 rrt 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   ticks_to_ctxt_switch--;
84   if (ticks_to_ctxt_switch <= 0) {
85       ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
86       context_switch = 1;       /* schedule a context switch */
87   }
88 }
89
90
91 /*
92  * Handling timer events under cygwin32 is not done with signal/setitimer.
93  * Instead of the two steps of first registering a signal handler to handle
94  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
95  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
96  * creates a separate thread that will notify the main thread of timer
97  * expiry). -- SOF 7/96
98  *
99  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
100  */
101
102 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
103
104 LPTIMECALLBACK vtalrm_cback;
105
106 nat
107 initialize_virtual_timer(nat ms)
108 {
109   /* On Win32 setups that don't have support for
110      setitimer(), we use the MultiMedia API's timer
111      support.
112      
113      The delivery of ticks isn't free; the performance hit should be checked.
114   */
115   unsigned int delay;
116   static unsigned int vtalrm_id;
117  
118   if (ms) {
119     delay = timeBeginPeriod(1);
120     if (delay == TIMERR_NOCANDO) { /* error of some sort. */
121       return delay;
122     }
123     vtalrm_id =
124       timeSetEvent(ms,      /* event every `delay' milliseconds. */
125                    1,       /* precision is within 1 ms */
126                    vtalrm_cback,
127                    TIME_CALLBACK_FUNCTION,     /* ordinary callback */
128                    TIME_PERIODIC);
129   } else {
130     timeKillEvent(vtalrm_id);
131     timeEndPeriod(1);
132   }
133
134   return 0;
135 }
136  
137 #else
138
139 nat
140 initialize_virtual_timer(nat ms)
141 {
142 # ifndef HAVE_SETITIMER
143   /*    fprintf(stderr, "No virtual timer on this system\n"); */
144     return -1;
145 # else
146     struct itimerval it;
147
148     timestamp = getourtimeofday();
149     ticks_since_timestamp = 0;
150
151     it.it_value.tv_sec = ms / 1000;
152     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
153     it.it_interval = it.it_value;
154     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
155 # endif
156 }
157
158 #endif /* !cygwin32_TARGET_OS */
159
160 # if 0
161 /* This is a potential POSIX version */
162 nat
163 initialize_virtual_timer(nat ms)
164 {
165     struct sigevent se;
166     struct itimerspec it;
167     timer_t tid;
168
169     timestamp = getourtimeofday();
170     ticks_since_timestamp = 0;
171
172     se.sigev_notify = SIGEV_SIGNAL;
173     se.sigev_signo = SIGVTALRM;
174     se.sigev_value.sival_int = SIGVTALRM;
175     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
176         barf("can't create virtual timer");
177     }
178     it.it_value.tv_sec = ms / 1000;
179     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
180     it.it_interval = it.it_value;
181     timer_settime(tid, TIMER_RELTIME, &it, NULL);
182 }
183 # endif
184
185 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
186 int
187 install_vtalrm_handler(void)
188 {
189   vtalrm_cback = handle_tick;
190   return 0;
191 }
192
193 #else
194 int
195 install_vtalrm_handler(void)
196 {
197     struct sigaction action;
198
199     action.sa_handler = handle_tick;
200
201     sigemptyset(&action.sa_mask);
202     action.sa_flags = 0;
203
204     return sigaction(SIGVTALRM, &action, NULL);
205 }
206
207 void
208 block_vtalrm_signal(void)
209 {
210     sigset_t signals;
211     
212     sigemptyset(&signals);
213     sigaddset(&signals, SIGVTALRM);
214
215     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
216 }
217
218 void
219 unblock_vtalrm_signal(void)
220 {
221     sigset_t signals;
222     
223     sigemptyset(&signals);
224     sigaddset(&signals, SIGVTALRM);
225
226     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
227 }
228 #endif
229
230 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
231  * only calling it 50 times/s, it shouldn't have any great impact.
232  */
233 #if !defined(mingw32_TARGET_OS)
234 unsigned int 
235 getourtimeofday(void)
236 {
237   struct timeval tv;
238   gettimeofday(&tv, (struct timezone *) NULL);
239   return (tv.tv_sec * TICK_FREQUENCY +
240           tv.tv_usec * TICK_FREQUENCY / 1000000);
241 }
242 #else
243 unsigned int
244 getourtimeofday(void)
245 {
246   return ((unsigned int)GetTickCount() * TICK_FREQUENCY) / 1000;
247 }
248 #endif