[project @ 2003-12-22 16:27:10 by simonmar]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.36 2003/12/22 16:27:10 simonmar 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 #include "Rts.h"
21 #include "RtsFlags.h"
22 #include "Timer.h"
23 #include "Itimer.h"
24 #include "Proftimer.h"
25 #include "Schedule.h"
26
27 /* As recommended in the autoconf manual */
28 # ifdef TIME_WITH_SYS_TIME
29 #  include <sys/time.h>
30 #  include <time.h>
31 # else
32 #  ifdef HAVE_SYS_TIME_H
33 #   include <sys/time.h>
34 #  else
35 #   include <time.h>
36 #  endif
37 # endif
38
39 #ifdef HAVE_SIGNAL_H
40 # include <signal.h>
41 #endif
42
43 /* Major bogosity:
44  * 
45  * In the threaded RTS, we can't set the virtual timer because the
46  * thread which has the virtual timer might be sitting waiting for a
47  * capability, and the virtual timer only ticks in CPU time.
48  *
49  * So, possible solutions:
50  *
51  * (1) tick in realtime.  Not very good, because this ticker is used for
52  *     profiling, and this will give us unreliable time profiling
53  *     results.  Furthermore, this requires picking a single OS thread
54  *     to be the timekeeper, which is a bad idea because the thread in
55  *     question might just be making a temporary call into Haskell land.
56  *
57  * (2) save/restore the virtual timer around excursions into STG land.
58  *     Sounds great, but I tried it and the resolution of the virtual timer
59  *     isn't good enough (on Linux) - most of our excursions fall
60  *     within the timer's resolution and we never make any progress.
61  *   
62  * (3) have a virtual timer in every OS thread.  Might be reasonable,
63  *     because most of the time there is only ever one of these
64  *     threads running, so it approximates a single virtual timer.
65  *     But still quite bogus (and I got crashes when I tried this).
66  *
67  * For now, we're using (1), but this needs a better solution. --SDM
68  */
69 #ifdef RTS_SUPPORTS_THREADS
70 #define ITIMER_FLAVOUR  ITIMER_REAL
71 #define ITIMER_SIGNAL   SIGALRM
72 #else
73 #define ITIMER_FLAVOUR  ITIMER_VIRTUAL
74 #define ITIMER_SIGNAL   SIGVTALRM
75 #endif
76
77 static
78 int
79 install_vtalrm_handler(TickProc handle_tick)
80 {
81     struct sigaction action;
82
83     action.sa_handler = handle_tick;
84
85     sigemptyset(&action.sa_mask);
86     action.sa_flags = 0;
87
88     return sigaction(ITIMER_SIGNAL, &action, NULL);
89 }
90
91 int
92 startTicker(nat ms, TickProc handle_tick)
93 {
94 # ifndef HAVE_SETITIMER
95   /*    fprintf(stderr, "No virtual timer on this system\n"); */
96     return -1;
97 # else
98     struct itimerval it;
99
100     install_vtalrm_handler(handle_tick);
101
102     timestamp = getourtimeofday();
103
104     it.it_value.tv_sec = ms / 1000;
105     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
106     it.it_interval = it.it_value;
107     return (setitimer(ITIMER_FLAVOUR, &it, NULL));
108 # endif
109 }
110
111 int
112 stopTicker()
113 {
114 # ifndef HAVE_SETITIMER
115   /*    fprintf(stderr, "No virtual timer on this system\n"); */
116     return -1;
117 # else
118     struct itimerval it;
119   
120     it.it_value.tv_sec = 0;
121     it.it_value.tv_usec = 0;
122     it.it_interval = it.it_value;
123     return (setitimer(ITIMER_FLAVOUR, &it, NULL));
124 # endif
125 }
126
127 # if 0
128 /* This is a potential POSIX version */
129 int
130 startTicker(nat ms)
131 {
132     struct sigevent se;
133     struct itimerspec it;
134     timer_t tid;
135
136     timestamp = getourtimeofday();
137
138     se.sigev_notify = SIGEV_SIGNAL;
139     se.sigev_signo = ITIMER_SIGNAL;
140     se.sigev_value.sival_int = ITIMER_SIGNAL;
141     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
142         barf("can't create virtual timer");
143     }
144     it.it_value.tv_sec = ms / 1000;
145     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
146     it.it_interval = it.it_value;
147     return timer_settime(tid, TIMER_RELTIME, &it, NULL);
148 }
149
150 int
151 stopTicker()
152 {
153     struct sigevent se;
154     struct itimerspec it;
155     timer_t tid;
156
157     timestamp = getourtimeofday();
158
159     se.sigev_notify = SIGEV_SIGNAL;
160     se.sigev_signo = ITIMER_SIGNAL;
161     se.sigev_value.sival_int = ITIMER_SIGNAL;
162     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
163         barf("can't create virtual timer");
164     }
165     it.it_value.tv_sec = 0;
166     it.it_value.tv_nsec = 0;
167     it.it_interval = it.it_value;
168     return timer_settime(tid, TIMER_RELTIME, &it, NULL);
169 }
170 # endif
171
172 #if 0
173 /* Currently unused */
174 void
175 block_vtalrm_signal(void)
176 {
177     sigset_t signals;
178     
179     sigemptyset(&signals);
180     sigaddset(&signals, ITIMER_SIGNAL);
181
182     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
183 }
184
185 void
186 unblock_vtalrm_signal(void)
187 {
188     sigset_t signals;
189     
190     sigemptyset(&signals);
191     sigaddset(&signals, ITIMER_SIGNAL);
192
193     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
194 }
195 #endif
196
197 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
198  * only calling it 50 times/s, it shouldn't have any great impact.
199  */
200 unsigned int 
201 getourtimeofday(void)
202 {
203   struct timeval tv;
204   gettimeofday(&tv, (struct timezone *) NULL);
205   return (tv.tv_sec * TICK_FREQUENCY +
206           tv.tv_usec * TICK_FREQUENCY / 1000000);
207 }
208