Watchdog.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. * Copyright (C) 2013 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  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.
  12. *
  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.
  24. */
  25. #include "config.h"
  26. #include "Watchdog.h"
  27. #include "CallFrame.h"
  28. #include <wtf/CurrentTime.h>
  29. #include <wtf/MathExtras.h>
  30. namespace JSC {
  31. #define NO_LIMIT std::numeric_limits<double>::infinity()
  32. #if !(ENABLE(DETACHED_JIT) && BUILDING_DETACHED_JIT)
  33. Watchdog::Watchdog()
  34. : m_timerDidFire(false)
  35. , m_didFire(false)
  36. , m_limit(NO_LIMIT)
  37. , m_startTime(0)
  38. , m_elapsedTime(0)
  39. , m_reentryCount(0)
  40. , m_isStopped(true)
  41. , m_callback(0)
  42. , m_callbackData1(0)
  43. , m_callbackData2(0)
  44. {
  45. initTimer();
  46. }
  47. Watchdog::~Watchdog()
  48. {
  49. ASSERT(!isArmed());
  50. stopCountdown();
  51. destroyTimer();
  52. }
  53. void Watchdog::setTimeLimit(VM& vm, double limit,
  54. ShouldTerminateCallback callback, void* data1, void* data2)
  55. {
  56. bool wasEnabled = isEnabled();
  57. if (!m_isStopped)
  58. stopCountdown();
  59. m_didFire = false; // Reset the watchdog.
  60. m_limit = limit;
  61. m_callback = callback;
  62. m_callbackData1 = data1;
  63. m_callbackData2 = data2;
  64. // If this is the first time that timeout is being enabled, then any
  65. // previously JIT compiled code will not have the needed polling checks.
  66. // Hence, we need to flush all the pre-existing compiled code.
  67. //
  68. // However, if the timeout is already enabled, and we're just changing the
  69. // timeout value, then any existing JITted code will have the appropriate
  70. // polling checks. Hence, there is no need to re-do this flushing.
  71. if (!wasEnabled) {
  72. // And if we've previously compiled any functions, we need to revert
  73. // them because they don't have the needed polling checks yet.
  74. vm.releaseExecutableMemory();
  75. }
  76. startCountdownIfNeeded();
  77. }
  78. bool Watchdog::didFire(ExecState* exec)
  79. {
  80. #if PLATFORM(MANX)
  81. // Workaround for a bug preventing scripts from running if another script in the same VM has previously been terminated
  82. // (Upstream bugfix pending)
  83. if (!isArmed())
  84. return false;
  85. #endif
  86. if (m_didFire)
  87. return true;
  88. if (!m_timerDidFire)
  89. return false;
  90. m_timerDidFire = false;
  91. stopCountdown();
  92. double currentTime = currentCPUTime();
  93. double deltaTime = currentTime - m_startTime;
  94. double totalElapsedTime = m_elapsedTime + deltaTime;
  95. if (totalElapsedTime > m_limit) {
  96. // Case 1: the allowed CPU time has elapsed.
  97. // If m_callback is not set, then we terminate by default.
  98. // Else, we let m_callback decide if we should terminate or not.
  99. bool needsTermination = !m_callback
  100. || m_callback(exec, m_callbackData1, m_callbackData2);
  101. if (needsTermination) {
  102. m_didFire = true;
  103. return true;
  104. }
  105. // The m_callback may have set a new limit. So, we may need to restart
  106. // the countdown.
  107. startCountdownIfNeeded();
  108. } else {
  109. // Case 2: the allowed CPU time has NOT elapsed.
  110. // Tell the timer to alarm us again when it thinks we've reached the
  111. // end of the allowed time.
  112. double remainingTime = m_limit - totalElapsedTime;
  113. m_elapsedTime = totalElapsedTime;
  114. m_startTime = currentTime;
  115. startCountdown(remainingTime);
  116. }
  117. return false;
  118. }
  119. #endif // #if !(ENABLE(DETACHED_JIT) && BUILDING_DETACHED_JIT)
  120. bool Watchdog::isEnabled()
  121. {
  122. return (m_limit != NO_LIMIT);
  123. }
  124. #if !(ENABLE(DETACHED_JIT) && BUILDING_DETACHED_JIT)
  125. void Watchdog::fire()
  126. {
  127. m_didFire = true;
  128. }
  129. void Watchdog::arm()
  130. {
  131. m_reentryCount++;
  132. #if PLATFORM(MANX)
  133. // Workaround for a bug preventing scripts from running if another script in the same VM has previously been terminated
  134. // (Upstream bugfix pending)
  135. if (m_reentryCount == 1)
  136. m_didFire = false; // Reset the watchdog on new script executions.
  137. #endif
  138. if (m_reentryCount == 1)
  139. startCountdownIfNeeded();
  140. }
  141. void Watchdog::disarm()
  142. {
  143. ASSERT(m_reentryCount > 0);
  144. if (m_reentryCount == 1)
  145. stopCountdown();
  146. m_reentryCount--;
  147. }
  148. void Watchdog::startCountdownIfNeeded()
  149. {
  150. if (!m_isStopped)
  151. return; // Already started.
  152. if (!isArmed())
  153. return; // Not executing JS script. No need to start.
  154. if (isEnabled()) {
  155. m_elapsedTime = 0;
  156. m_startTime = currentCPUTime();
  157. startCountdown(m_limit);
  158. }
  159. }
  160. void Watchdog::startCountdown(double limit)
  161. {
  162. ASSERT(m_isStopped);
  163. m_isStopped = false;
  164. startTimer(limit);
  165. }
  166. void Watchdog::stopCountdown()
  167. {
  168. if (m_isStopped)
  169. return;
  170. stopTimer();
  171. m_isStopped = true;
  172. }
  173. Watchdog::Scope::Scope(Watchdog& watchdog)
  174. : m_watchdog(watchdog)
  175. {
  176. m_watchdog.arm();
  177. }
  178. Watchdog::Scope::~Scope()
  179. {
  180. m_watchdog.disarm();
  181. }
  182. #endif // #if !(ENABLE(DETACHED_JIT) && BUILDING_DETACHED_JIT)
  183. } // namespace JSC