a4f55527dd48df0c96e8940ebd2a87782c2ce3fa
[ghc.git] / rts / posix / itimer / Pthread.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 * We use a realtime timer by default. I found this much more
11 * reliable than a CPU timer:
12 *
13 * Experiments with different frequences: using
14 * CLOCK_REALTIME/CLOCK_MONOTONIC on Linux 2.6.32,
15 * 1000us has <1% impact on runtime
16 * 100us has ~2% impact on runtime
17 * 10us has ~40% impact on runtime
18 *
19 * using CLOCK_PROCESS_CPUTIME_ID on Linux 2.6.32,
20 * I cannot get it to tick faster than 10ms (10000us)
21 * which isn't great for profiling.
22 *
23 * In the threaded RTS, we can't tick in CPU time because the thread
24 * which has the virtual timer might be idle, so the tick would never
25 * fire. Therfore we used to tick in realtime in the threaded RTS and
26 * in CPU time otherwise, but now we always tick in realtime, for
27 * several reasons:
28 *
29 * - resolution (see above)
30 * - consistency (-threaded is the same as normal)
31 * - more consistency: Windows only has a realtime timer
32 *
33 * Note we want to use CLOCK_MONOTONIC rather than CLOCK_REALTIME,
34 * because the latter may jump around (NTP adjustments, leap seconds
35 * etc.).
36 */
37
38 #include "PosixSource.h"
39 #include "Rts.h"
40
41 #include "Ticker.h"
42 #include "Proftimer.h"
43 #include "Schedule.h"
44 #include "posix/Clock.h"
45
46 /* As recommended in the autoconf manual */
47 # if defined(TIME_WITH_SYS_TIME)
48 # include <sys/time.h>
49 # include <time.h>
50 # else
51 # if defined(HAVE_SYS_TIME_H)
52 # include <sys/time.h>
53 # else
54 # include <time.h>
55 # endif
56 # endif
57
58 #if defined(HAVE_SIGNAL_H)
59 # include <signal.h>
60 #endif
61
62 #include <string.h>
63
64 #include <pthread.h>
65 #include <unistd.h>
66 #include <fcntl.h>
67
68 #if defined(HAVE_SYS_TIMERFD_H)
69 #include <sys/timerfd.h>
70 #define USE_TIMERFD_FOR_ITIMER 1
71 #else
72 #define USE_TIMERFD_FOR_ITIMER 0
73 #endif
74
75 /*
76 * TFD_CLOEXEC has been added in Linux 2.6.26.
77 * If it is not available, we use fcntl(F_SETFD).
78 */
79 #if !defined(TFD_CLOEXEC)
80 #define TFD_CLOEXEC 0
81 #endif
82
83 static Time itimer_interval = DEFAULT_TICK_INTERVAL;
84
85 // Should we be firing ticks?
86 // Writers to this must hold the mutex below.
87 static volatile HsBool stopped = 0;
88
89 // should the ticker thread exit?
90 // This can be set without holding the mutex.
91 static volatile HsBool exited = 1;
92
93 // Signaled when we want to (re)start the timer
94 static Condition start_cond;
95 static Mutex mutex;
96 static OSThreadId thread;
97
98 static void *itimer_thread_func(void *_handle_tick)
99 {
100 TickProc handle_tick = _handle_tick;
101 uint64_t nticks;
102 int timerfd = -1;
103
104 #if defined(USE_TIMERFD_FOR_ITIMER) && USE_TIMERFD_FOR_ITIMER
105 struct itimerspec it;
106 it.it_value.tv_sec = TimeToSeconds(itimer_interval);
107 it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000;
108 it.it_interval = it.it_value;
109
110 timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
111 if (timerfd == -1) {
112 sysErrorBelch("timerfd_create");
113 stg_exit(EXIT_FAILURE);
114 }
115 if (!TFD_CLOEXEC) {
116 fcntl(timerfd, F_SETFD, FD_CLOEXEC);
117 }
118 if (timerfd_settime(timerfd, 0, &it, NULL)) {
119 sysErrorBelch("timerfd_settime");
120 stg_exit(EXIT_FAILURE);
121 }
122 #endif
123
124 while (!exited) {
125 if (USE_TIMERFD_FOR_ITIMER) {
126 if (read(timerfd, &nticks, sizeof(nticks)) != sizeof(nticks)) {
127 if (errno != EINTR) {
128 sysErrorBelch("Itimer: read(timerfd) failed");
129 }
130 }
131 } else {
132 if (usleep(TimeToUS(itimer_interval)) != 0 && errno != EINTR) {
133 sysErrorBelch("usleep(TimeToUS(itimer_interval) failed");
134 }
135 }
136
137 // first try a cheap test
138 if (stopped) {
139 ACQUIRE_LOCK(&mutex);
140 // should we really stop?
141 if (stopped) {
142 waitCondition(&start_cond, &mutex);
143 }
144 RELEASE_LOCK(&mutex);
145 } else {
146 handle_tick(0);
147 }
148 }
149
150 if (USE_TIMERFD_FOR_ITIMER)
151 close(timerfd);
152 closeMutex(&mutex);
153 closeCondition(&start_cond);
154 return NULL;
155 }
156
157 void
158 initTicker (Time interval, TickProc handle_tick)
159 {
160 itimer_interval = interval;
161 stopped = 0;
162 exited = 0;
163
164 initCondition(&start_cond);
165 initMutex(&mutex);
166
167 /*
168 * We can't use the RTS's createOSThread here as we need to remain attached
169 * to the thread we create so we can later join to it if requested
170 */
171 if (! pthread_create(&thread, NULL, itimer_thread_func, (void*)handle_tick)) {
172 #if defined(HAVE_PTHREAD_SETNAME_NP)
173 pthread_setname_np(thread, "ghc_ticker");
174 #endif
175 } else {
176 sysErrorBelch("Itimer: Failed to spawn thread");
177 stg_exit(EXIT_FAILURE);
178 }
179 }
180
181 void
182 startTicker(void)
183 {
184 ACQUIRE_LOCK(&mutex);
185 stopped = 0;
186 signalCondition(&start_cond);
187 RELEASE_LOCK(&mutex);
188 }
189
190 /* There may be at most one additional tick fired after a call to this */
191 void
192 stopTicker(void)
193 {
194 ACQUIRE_LOCK(&mutex);
195 stopped = 1;
196 RELEASE_LOCK(&mutex);
197 }
198
199 /* There may be at most one additional tick fired after a call to this */
200 void
201 exitTicker (bool wait)
202 {
203 ASSERT(!exited);
204 exited = 1;
205 // ensure that ticker wakes up if stopped
206 startTicker();
207
208 // wait for ticker to terminate if necessary
209 if (wait) {
210 if (pthread_join(thread, NULL)) {
211 sysErrorBelch("Itimer: Failed to join");
212 }
213 } else {
214 pthread_detach(thread);
215 }
216 }
217
218 int
219 rtsTimerSignal(void)
220 {
221 return SIGALRM;
222 }