Fix build on OS X
[ghc.git] / rts / posix / Itimer.c
1 /* -----------------------------------------------------------------------------
2 *
3 * (c) The GHC Team, 1995-2007
4 *
5 * Interval timer for profiling and pre-emptive scheduling.
6 *
7 * ---------------------------------------------------------------------------*/
8
9 /*
10 * The interval timer is used for profiling and for context switching in the
11 * threaded build. Though POSIX 1003.1b includes a standard interface for
12 * such things, no one really seems to be implementing them yet. Even
13 * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're
14 * keen on getting access to @CLOCK_VIRTUAL@.
15 *
16 * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
17 * to support. So much for standards.
18 */
19
20 #include "PosixSource.h"
21 #include "Rts.h"
22
23 #include "Ticker.h"
24 #include "Itimer.h"
25 #include "Proftimer.h"
26 #include "Schedule.h"
27 #include "Clock.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 #ifdef HAVE_SIGNAL_H
42 # include <signal.h>
43 #endif
44
45 #include <string.h>
46
47 /*
48 * We use a realtime timer by default. I found this much more
49 * reliable than a CPU timer:
50 *
51 * Experiments with different frequences: using
52 * CLOCK_REALTIME/CLOCK_MONOTONIC on Linux 2.6.32,
53 * 1000us has <1% impact on runtime
54 * 100us has ~2% impact on runtime
55 * 10us has ~40% impact on runtime
56 *
57 * using CLOCK_PROCESS_CPUTIME_ID on Linux 2.6.32,
58 * I cannot get it to tick faster than 10ms (10000us)
59 * which isn't great for profiling.
60 *
61 * In the threaded RTS, we can't tick in CPU time because the thread
62 * which has the virtual timer might be idle, so the tick would never
63 * fire. Therfore we used to tick in realtime in the threaded RTS and
64 * in CPU time otherwise, but now we always tick in realtime, for
65 * several reasons:
66 *
67 * - resolution (see above)
68 * - consistency (-threaded is the same as normal)
69 * - more consistency: Windows only has a realtime timer
70 *
71 * Note we want to use CLOCK_MONOTONIC rather than CLOCK_REALTIME,
72 * because the latter may jump around (NTP adjustments, leap seconds
73 * etc.).
74 */
75
76 #if defined(USE_TIMER_CREATE)
77 # define ITIMER_SIGNAL SIGVTALRM
78 #elif defined(HAVE_SETITIMER)
79 # define ITIMER_SIGNAL SIGALRM
80 // Using SIGALRM can leads to problems, see #850. But we have no
81 // option if timer_create() is not available.
82 #else
83 # error No way to set an interval timer.
84 #endif
85
86 #if defined(USE_TIMER_CREATE)
87 static timer_t timer;
88 #endif
89
90 static Time itimer_interval = DEFAULT_TICK_INTERVAL;
91
92 static void install_vtalrm_handler(TickProc handle_tick)
93 {
94 struct sigaction action;
95
96 action.sa_handler = handle_tick;
97
98 sigemptyset(&action.sa_mask);
99
100 #ifdef SA_RESTART
101 // specify SA_RESTART. One consequence if we don't do this is
102 // that readline gets confused by the -threaded RTS. It seems
103 // that if a SIGALRM handler is installed without SA_RESTART,
104 // readline installs its own SIGALRM signal handler (see
105 // readline's signals.c), and this somehow causes readline to go
106 // wrong when the input exceeds a single line (try it).
107 action.sa_flags = SA_RESTART;
108 #else
109 action.sa_flags = 0;
110 #endif
111
112 if (sigaction(ITIMER_SIGNAL, &action, NULL) == -1) {
113 sysErrorBelch("sigaction");
114 stg_exit(EXIT_FAILURE);
115 }
116 }
117
118 void
119 initTicker (Time interval, TickProc handle_tick)
120 {
121 itimer_interval = interval;
122
123 #if defined(USE_TIMER_CREATE)
124 {
125 struct sigevent ev;
126
127 // Keep programs like valgrind happy
128 memset(&ev, 0, sizeof(ev));
129
130 ev.sigev_notify = SIGEV_SIGNAL;
131 ev.sigev_signo = ITIMER_SIGNAL;
132
133 if (timer_create(CLOCK_ID, &ev, &timer) != 0) {
134 sysErrorBelch("timer_create");
135 stg_exit(EXIT_FAILURE);
136 }
137 }
138 #endif
139
140 install_vtalrm_handler(handle_tick);
141 }
142
143 void
144 startTicker(void)
145 {
146 #if defined(USE_TIMER_CREATE)
147 {
148 struct itimerspec it;
149
150 it.it_value.tv_sec = TimeToSeconds(itimer_interval);
151 it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000;
152 it.it_interval = it.it_value;
153
154 if (timer_settime(timer, 0, &it, NULL) != 0) {
155 sysErrorBelch("timer_settime");
156 stg_exit(EXIT_FAILURE);
157 }
158 }
159 #else
160 {
161 struct itimerval it;
162
163 it.it_value.tv_sec = TimeToSeconds(itimer_interval);
164 it.it_value.tv_usec = TimeToUS(itimer_interval) % 1000000;
165 it.it_interval = it.it_value;
166
167 if (setitimer(ITIMER_REAL, &it, NULL) != 0) {
168 sysErrorBelch("setitimer");
169 stg_exit(EXIT_FAILURE);
170 }
171 }
172 #endif
173 }
174
175 void
176 stopTicker(void)
177 {
178 #if defined(USE_TIMER_CREATE)
179 struct itimerspec it;
180
181 it.it_value.tv_sec = 0;
182 it.it_value.tv_nsec = 0;
183 it.it_interval = it.it_value;
184
185 if (timer_settime(timer, 0, &it, NULL) != 0) {
186 sysErrorBelch("timer_settime");
187 stg_exit(EXIT_FAILURE);
188 }
189 #else
190 struct itimerval it;
191
192 it.it_value.tv_sec = 0;
193 it.it_value.tv_usec = 0;
194 it.it_interval = it.it_value;
195
196 if (setitimer(ITIMER_REAL, &it, NULL) != 0) {
197 sysErrorBelch("setitimer");
198 stg_exit(EXIT_FAILURE);
199 }
200 #endif
201 }
202
203 void
204 exitTicker (rtsBool wait STG_UNUSED)
205 {
206 #if defined(USE_TIMER_CREATE)
207 timer_delete(timer);
208 // ignore errors - we don't really care if it fails.
209 #endif
210 }
211
212 int
213 rtsTimerSignal(void)
214 {
215 return ITIMER_SIGNAL;
216 }