[project @ 2000-03-20 09:42:49 by andy]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.11 2000/03/20 09:42:49 andy 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 "Schedule.h"
28
29 /* As recommended in the autoconf manual */
30 # ifdef TIME_WITH_SYS_TIME
31 #  include <sys/time.h>
32 #  include <time.h>
33 # else
34 #  ifdef HAVE_SYS_TIME_H
35 #   include <sys/time.h>
36 #  else
37 #   include <time.h>
38 #  endif
39 # endif
40
41 #if HAVE_WINDOWS_H
42 # include <windows.h>
43 #endif
44  
45 lnat total_ticks = 0;
46 rtsBool do_prof_ticks = rtsFalse;
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   if (do_prof_ticks == rtsTrue) {
77     CCS_TICK(CCCS);
78   }
79 #endif
80
81   /* For threadDelay etc., see Select.c */
82   ticks_since_select++;
83 }
84
85
86 /*
87  * Handling timer events under cygwin32 is not done with signal/setitimer.
88  * Instead of the two steps of first registering a signal handler to handle
89  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
90  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
91  * creates a separate thread that will notify the main thread of timer
92  * expiry). -- SOF 7/96
93  *
94  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
95  */
96
97 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
98
99 /* 
100  * Sigh - to avoid requiring anyone that wants to build ghc to have
101  * to augment the Win32 header files that comes with cygwinb20.1,
102  * include the missing MM API decls here inline.
103  *
104  * ToDo: check and remove these once the next version of cygwin is
105  * released.
106  */
107 #define TIMERR_NOERROR   0
108 #define TIMERR_NOCANDO   97
109 #define TIME_PERIODIC    1
110
111 typedef UINT MMRESULT;
112 typedef void CALLBACK (*TIMECALLBACK) (UINT, UINT, DWORD, DWORD, DWORD);
113 typedef TIMECALLBACK *LPTIMECALLBACK;
114 MMRESULT STDCALL  timeSetEvent(UINT, UINT, LPTIMECALLBACK, DWORD, UINT);
115 /*
116   vtalrm_handler is assigned and set up in Signals.c
117
118   vtalrm_id (defined in Signals.c) holds
119   the system id for the current timer (used to 
120   later block/kill it.)
121 */
122 extern nat vtalrm_id;
123 TIMECALLBACK *vtalrm_cback;
124  
125 nat
126 initialize_virtual_timer(nat ms)
127 {
128 # ifdef PROFILING
129   /* On Win32 setups that don't have support for
130      setitimer(), we use the MultiMedia API's timer
131      support.
132      
133      As the delivery of ticks isn't free, we only
134      enable it if we really needed, i.e., when profiling.
135      (the RTS now also needs timer ticks to implement
136      threadDelay in non-profiling mode, but the pure
137      Win32 port doesn't support that.....yet.)
138   */
139   unsigned int delay,vtalrm_id;
140  
141   delay = timeBeginPeriod(1);
142   if (delay == TIMERR_NOCANDO) { /* error of some sort. */
143      return delay;
144   }
145   vtalrm_id =
146     timeSetEvent(ms,     /* event every `delay' milliseconds. */
147                 1,       /* precision is within 5 millisecs. */
148                 (LPTIMECALLBACK)vtalrm_cback,
149                 0,
150                 TIME_PERIODIC);
151 # endif
152   return 0;
153 }
154  
155 #else
156
157 nat
158 initialize_virtual_timer(nat ms)
159 {
160 # ifndef HAVE_SETITIMER
161   /*    fprintf(stderr, "No virtual timer on this system\n"); */
162     return -1;
163 # else
164     struct itimerval it;
165
166     it.it_value.tv_sec = ms / 1000;
167     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
168     it.it_interval = it.it_value;
169     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
170 # endif
171 }
172
173 #endif /* !cygwin32_TARGET_OS */
174
175 # if 0
176 /* This is a potential POSIX version */
177 nat
178 initialize_virtual_timer(nat ms)
179 {
180     struct sigevent se;
181     struct itimerspec it;
182     timer_t tid;
183
184     se.sigev_notify = SIGEV_SIGNAL;
185     se.sigev_signo = SIGVTALRM;
186     se.sigev_value.sival_int = SIGVTALRM;
187     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
188         barf("can't create virtual timer");
189     }
190     it.it_value.tv_sec = ms / 1000;
191     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
192     it.it_interval = it.it_value;
193     timer_settime(tid, TIMER_RELTIME, &it, NULL);
194 }
195 # endif
196
197 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
198 int
199 install_vtalrm_handler(void)
200 {
201   vtalrm_cback = handle_tick;
202   return 0;
203 }
204
205 #else
206 int
207 install_vtalrm_handler(void)
208 {
209     struct sigaction action;
210
211     action.sa_handler = handle_tick;
212
213     sigemptyset(&action.sa_mask);
214     action.sa_flags = 0;
215
216     return sigaction(SIGVTALRM, &action, NULL);
217 }
218
219 void
220 block_vtalrm_signal(void)
221 {
222     sigset_t signals;
223     
224     sigemptyset(&signals);
225     sigaddset(&signals, SIGVTALRM);
226
227     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
228 }
229
230 void
231 unblock_vtalrm_signal(void)
232 {
233     sigset_t signals;
234     
235     sigemptyset(&signals);
236     sigaddset(&signals, SIGVTALRM);
237
238     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
239 }
240 #endif
241
242 unsigned int 
243 getourtimeofday(void)
244 {
245   struct timeval tv;
246   gettimeofday(&tv, (struct timezone *) NULL);
247   return (tv.tv_sec * 1000000 + tv.tv_usec);
248 }