[project @ 1999-09-16 08:33:54 by sof]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.6 1999/09/16 08:33:54 sof 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 void handle_tick(int unused STG_UNUSED);
49
50 /* -----------------------------------------------------------------------------
51    Tick handler
52
53    We use the ticker for two things: supporting threadDelay, and time
54    profiling.
55    -------------------------------------------------------------------------- */
56
57 static void
58 handle_tick(int unused STG_UNUSED)
59 {
60   total_ticks++;
61
62 #ifdef PROFILING
63   if (do_prof_ticks = rtsTrue) {
64     CCS_TICK(CCCS);
65   }
66 #endif
67
68   /* For threadDelay etc., see Select.c */
69   ticks_since_select++;
70 }
71
72
73 /*
74  * Handling timer events under cygwin32 is not done with signal/setitimer.
75  * Instead of the two steps of first registering a signal handler to handle
76  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
77  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
78  * creates a separate thread that will notify the main thread of timer
79  * expiry). -- SOF 7/96
80  *
81  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
82  */
83
84 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
85
86 /*
87   vtalrm_handler is assigned and set up in Signals.c
88
89   vtalrm_id (defined in Signals.c) holds
90   the system id for the current timer (used to 
91   later block/kill it.)
92 */
93 extern nat vtalrm_id;
94 TIMECALLBACK *vtalrm_cback;
95  
96 nat
97 initialize_virtual_timer(nat ms)
98 {
99 # ifdef PROFILING
100   /* On Win32 setups that don't have support for
101      setitimer(), we use the MultiMedia API's timer
102      support.
103      
104      As the delivery of ticks isn't free, we only
105      enable it if we really needed, i.e., when profiling.
106      (the RTS now also needs timer ticks to implement
107      threadDelay in non-profiling mode, but the pure
108      Win32 port doesn't support that.....yet.)
109   */
110   unsigned int delay,vtalrm_id;
111  
112   delay = timeBeginPeriod(1);
113   if (delay == TIMERR_NOCANDO) { /* error of some sort. */
114      return delay;
115   }
116   vtalrm_id =
117     timeSetEvent(ms,     /* event every `delay' milliseconds. */
118                 1,       /* precision is within 5 millisecs. */
119                 (LPTIMECALLBACK)vtalrm_cback,
120                 0,
121                 TIME_PERIODIC);
122 # endif
123   return 0;
124 }
125  
126 #else
127
128 nat
129 initialize_virtual_timer(nat ms)
130 {
131 # ifndef HAVE_SETITIMER
132     fprintf(stderr, "No virtual timer on this system\n");
133     return -1;
134 # else
135     struct itimerval it;
136
137     it.it_value.tv_sec = ms / 1000;
138     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
139     it.it_interval = it.it_value;
140     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
141 # endif
142 }
143
144 #endif /* !cygwin32_TARGET_OS */
145
146 # if 0
147 /* This is a potential POSIX version */
148 nat
149 initialize_virtual_timer(nat ms)
150 {
151     struct sigevent se;
152     struct itimerspec it;
153     timer_t tid;
154
155     se.sigev_notify = SIGEV_SIGNAL;
156     se.sigev_signo = SIGVTALRM;
157     se.sigev_value.sival_int = SIGVTALRM;
158     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
159         fprintf(stderr, "Can't create virtual timer.\n");
160         EXIT(EXIT_FAILURE);
161     }
162     it.it_value.tv_sec = ms / 1000;
163     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
164     it.it_interval = it.it_value;
165     timer_settime(tid, TIMER_RELTIME, &it, NULL);
166 }
167 # endif
168
169 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
170 int
171 install_vtalrm_handler(void)
172 {
173   vtalrm_cback = handle_tick;
174   return 0;
175 }
176
177 #else
178 int
179 install_vtalrm_handler(void)
180 {
181     struct sigaction action;
182
183     action.sa_handler = handle_tick;
184
185     sigemptyset(&action.sa_mask);
186     action.sa_flags = 0;
187
188     return sigaction(SIGVTALRM, &action, NULL);
189 }
190
191 void
192 block_vtalrm_signal(void)
193 {
194     sigset_t signals;
195     
196     sigemptyset(&signals);
197     sigaddset(&signals, SIGVTALRM);
198
199     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
200 }
201
202 void
203 unblock_vtalrm_signal(void)
204 {
205     sigset_t signals;
206     
207     sigemptyset(&signals);
208     sigaddset(&signals, SIGVTALRM);
209
210     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
211 }
212 #endif