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
|
/* -----------------------------------------------------------------------------
*
* (c) The GHC Team, 1995-2005
*
* Interval timer service for profiling and pre-emptive scheduling.
*
* ---------------------------------------------------------------------------*/
/*
* The interval timer is used for profiling and for context switching in the
* threaded build.
*
* This file defines the platform-independent view of interval timing, relying
* on platform-specific services to install and run the timers.
*
*/
#include "PosixSource.h"
#include "Rts.h"
#include "Timer.h"
#include "Proftimer.h"
#include "Schedule.h"
#include "Ticker.h"
#include "Capability.h"
#include "RtsSignals.h"
/* ticks left before next pre-emptive context switch */
static int ticks_to_ctxt_switch = 0;
/* idle ticks left before GC allowed */
static int idle_ticks_to_gc = 0;
/* inter-idle GC ticks left before GC allowed */
static int inter_gc_ticks_to_gc = 0;
/*
* Function: handle_tick()
*
* At each occurrence of a tick, the OS timer will invoke
* handle_tick().
*/
static
void
handle_tick(int unused STG_UNUSED)
{
handleProfTick();
if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0) {
ticks_to_ctxt_switch--;
if (ticks_to_ctxt_switch <= 0) {
ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
contextSwitchAllCapabilities(); /* schedule a context switch */
}
}
/*
* If we've been inactive for idleGCDelayTime (set by +RTS
* -I), tell the scheduler to wake up and do a GC, to check
* for threads that are deadlocked. However, ensure we wait
* at least interIdleGCWait (+RTS -Iw) between idle GCs.
*/
switch (recent_activity) {
case ACTIVITY_YES:
recent_activity = ACTIVITY_MAYBE_NO;
idle_ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime /
RtsFlags.MiscFlags.tickInterval;
break;
case ACTIVITY_MAYBE_NO:
if (idle_ticks_to_gc == 0 && inter_gc_ticks_to_gc == 0) {
if (RtsFlags.GcFlags.doIdleGC) {
recent_activity = ACTIVITY_INACTIVE;
inter_gc_ticks_to_gc = RtsFlags.GcFlags.interIdleGCWait /
RtsFlags.MiscFlags.tickInterval;
#if defined(THREADED_RTS)
wakeUpRts();
// The scheduler will call stopTimer() when it has done
// the GC.
#endif
} else {
recent_activity = ACTIVITY_DONE_GC;
// disable timer signals (see #1623, #5991, #9105)
// but only if we're not profiling (e.g. passed -h or -p RTS
// flags). If we are profiling we need to keep the timer active
// so that samples continue to be collected.
#if defined(PROFILING)
if (!(RtsFlags.ProfFlags.doHeapProfile
|| RtsFlags.CcFlags.doCostCentres)) {
stopTimer();
}
#else
stopTimer();
#endif
}
} else {
if (idle_ticks_to_gc) idle_ticks_to_gc--;
if (inter_gc_ticks_to_gc) inter_gc_ticks_to_gc--;
}
break;
default:
break;
}
}
// This global counter is used to allow multiple threads to stop the
// timer temporarily with a stopTimer()/startTimer() pair. If
// timer_enabled == 0 timer is enabled
// timer_disabled == N, N > 0 timer is disabled by N threads
// When timer_enabled makes a transition to 0, we enable the timer,
// and when it makes a transition to non-0 we disable it.
static StgWord timer_disabled;
void
initTimer(void)
{
initProfTimer();
if (RtsFlags.MiscFlags.tickInterval != 0) {
initTicker(RtsFlags.MiscFlags.tickInterval, handle_tick);
}
timer_disabled = 1;
}
void
startTimer(void)
{
if (atomic_dec(&timer_disabled) == 0) {
if (RtsFlags.MiscFlags.tickInterval != 0) {
startTicker();
}
}
}
void
stopTimer(void)
{
if (atomic_inc(&timer_disabled, 1) == 1) {
if (RtsFlags.MiscFlags.tickInterval != 0) {
stopTicker();
}
}
}
void
exitTimer (bool wait)
{
if (RtsFlags.MiscFlags.tickInterval != 0) {
exitTicker(wait);
}
}
|