2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "CallFrame.h"
30 #include <wtf/CurrentTime.h>
31 #include <wtf/MathExtras.h>
35 #define NO_LIMIT std::numeric_limits<double>::infinity()
38 : m_timerDidFire(false)
59 void Watchdog::setTimeLimit(VM
& vm
, double limit
,
60 ShouldTerminateCallback callback
, void* data1
, void* data2
)
62 bool wasEnabled
= isEnabled();
67 m_didFire
= false; // Reset the watchdog.
70 m_callback
= callback
;
71 m_callbackData1
= data1
;
72 m_callbackData2
= data2
;
74 // If this is the first time that timeout is being enabled, then any
75 // previously JIT compiled code will not have the needed polling checks.
76 // Hence, we need to flush all the pre-existing compiled code.
78 // However, if the timeout is already enabled, and we're just changing the
79 // timeout value, then any existing JITted code will have the appropriate
80 // polling checks. Hence, there is no need to re-do this flushing.
82 // And if we've previously compiled any functions, we need to revert
83 // them because they don't have the needed polling checks yet.
84 vm
.releaseExecutableMemory();
87 startCountdownIfNeeded();
90 bool Watchdog::didFire(ExecState
* exec
)
97 m_timerDidFire
= false;
100 double currentTime
= currentCPUTime();
101 double deltaTime
= currentTime
- m_startTime
;
102 double totalElapsedTime
= m_elapsedTime
+ deltaTime
;
103 if (totalElapsedTime
> m_limit
) {
104 // Case 1: the allowed CPU time has elapsed.
106 // If m_callback is not set, then we terminate by default.
107 // Else, we let m_callback decide if we should terminate or not.
108 bool needsTermination
= !m_callback
109 || m_callback(exec
, m_callbackData1
, m_callbackData2
);
110 if (needsTermination
) {
115 // The m_callback may have set a new limit. So, we may need to restart
117 startCountdownIfNeeded();
120 // Case 2: the allowed CPU time has NOT elapsed.
122 // Tell the timer to alarm us again when it thinks we've reached the
123 // end of the allowed time.
124 double remainingTime
= m_limit
- totalElapsedTime
;
125 m_elapsedTime
= totalElapsedTime
;
126 m_startTime
= currentTime
;
127 startCountdown(remainingTime
);
133 bool Watchdog::isEnabled()
135 return (m_limit
!= NO_LIMIT
);
138 void Watchdog::fire()
146 if (m_reentryCount
== 1)
147 startCountdownIfNeeded();
150 void Watchdog::disarm()
152 ASSERT(m_reentryCount
> 0);
153 if (m_reentryCount
== 1)
158 void Watchdog::startCountdownIfNeeded()
161 return; // Already started.
164 return; // Not executing JS script. No need to start.
168 m_startTime
= currentCPUTime();
169 startCountdown(m_limit
);
173 void Watchdog::startCountdown(double limit
)
180 void Watchdog::stopCountdown()
188 Watchdog::Scope::Scope(Watchdog
& watchdog
)
189 : m_watchdog(watchdog
)
194 Watchdog::Scope::~Scope()