summaryrefslogtreecommitdiff
path: root/ghc/rts/Itimer.c
blob: 0db283c6629d91e7caf8bb1983fdebdfa154df8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* -----------------------------------------------------------------------------
 * $Id: Itimer.c,v 1.8 2000/01/13 12:40:15 simonmar Exp $
 *
 * (c) The GHC Team, 1995-1999
 *
 * Interval timer for profiling and pre-emptive scheduling.
 *
 * ---------------------------------------------------------------------------*/

/*
 * The interval timer is used for profiling and for context switching in the
 * threaded build.  Though POSIX 1003.1b includes a standard interface for
 * such things, no one really seems to be implementing them yet.  Even 
 * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're
 * keen on getting access to @CLOCK_VIRTUAL@.
 * 
 * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
 * to support.  So much for standards.
 */

#if !defined(_AIX)
# define NON_POSIX_SOURCE
#endif

#include "Rts.h"
#include "Itimer.h"
#include "Schedule.h"

/* As recommended in the autoconf manual */
# ifdef TIME_WITH_SYS_TIME
#  include <sys/time.h>
#  include <time.h>
# else
#  ifdef HAVE_SYS_TIME_H
#   include <sys/time.h>
#  else
#   include <time.h>
#  endif
# endif

#if HAVE_WINDOWS_H
# include <windows.h>
#endif
 
lnat total_ticks = 0;
rtsBool do_prof_ticks = rtsFalse;

static void handle_tick(int unused STG_UNUSED);

/* -----------------------------------------------------------------------------
   Tick handler

   We use the ticker for two things: supporting threadDelay, and time
   profiling.

   SMP note: this signal could be delivered to *any* thread.  We have
   to ensure that it doesn't matter which thread actually runs the
   signal handler.
   -------------------------------------------------------------------------- */

static void
handle_tick(int unused STG_UNUSED)
{
  total_ticks++;

#ifdef PROFILING
  if (do_prof_ticks = rtsTrue) {
    CCS_TICK(CCCS);
  }
#endif

  /* For threadDelay etc., see Select.c */
  ticks_since_select++;
}


/*
 * Handling timer events under cygwin32 is not done with signal/setitimer.
 * Instead of the two steps of first registering a signal handler to handle
 * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
 * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
 * creates a separate thread that will notify the main thread of timer
 * expiry). -- SOF 7/96
 *
 * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
 */

#if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))

/*
  vtalrm_handler is assigned and set up in Signals.c

  vtalrm_id (defined in Signals.c) holds
  the system id for the current timer (used to 
  later block/kill it.)
*/
extern nat vtalrm_id;
TIMECALLBACK *vtalrm_cback;
 
nat
initialize_virtual_timer(nat ms)
{
# ifdef PROFILING
  /* On Win32 setups that don't have support for
     setitimer(), we use the MultiMedia API's timer
     support.
     
     As the delivery of ticks isn't free, we only
     enable it if we really needed, i.e., when profiling.
     (the RTS now also needs timer ticks to implement
     threadDelay in non-profiling mode, but the pure
     Win32 port doesn't support that.....yet.)
  */
  unsigned int delay,vtalrm_id;
 
  delay = timeBeginPeriod(1);
  if (delay == TIMERR_NOCANDO) { /* error of some sort. */
     return delay;
  }
  vtalrm_id =
    timeSetEvent(ms,     /* event every `delay' milliseconds. */
 	        1,       /* precision is within 5 millisecs. */
 	        (LPTIMECALLBACK)vtalrm_cback,
 		0,
 		TIME_PERIODIC);
# endif
  return 0;
}
 
#else

nat
initialize_virtual_timer(nat ms)
{
# ifndef HAVE_SETITIMER
    fprintf(stderr, "No virtual timer on this system\n");
    return -1;
# else
    struct itimerval it;

    it.it_value.tv_sec = ms / 1000;
    it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
    it.it_interval = it.it_value;
    return (setitimer(ITIMER_VIRTUAL, &it, NULL));
# endif
}

#endif /* !cygwin32_TARGET_OS */

# if 0
/* This is a potential POSIX version */
nat
initialize_virtual_timer(nat ms)
{
    struct sigevent se;
    struct itimerspec it;
    timer_t tid;

    se.sigev_notify = SIGEV_SIGNAL;
    se.sigev_signo = SIGVTALRM;
    se.sigev_value.sival_int = SIGVTALRM;
    if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
	barf("can't create virtual timer");
    }
    it.it_value.tv_sec = ms / 1000;
    it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
    it.it_interval = it.it_value;
    timer_settime(tid, TIMER_RELTIME, &it, NULL);
}
# endif

#if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
int
install_vtalrm_handler(void)
{
  vtalrm_cback = handle_tick;
  return 0;
}

#else
int
install_vtalrm_handler(void)
{
    struct sigaction action;

    action.sa_handler = handle_tick;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    return sigaction(SIGVTALRM, &action, NULL);
}

void
block_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
    sigaddset(&signals, SIGVTALRM);

    (void) sigprocmask(SIG_BLOCK, &signals, NULL);
}

void
unblock_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
    sigaddset(&signals, SIGVTALRM);

    (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
}
#endif