Ignore:
Timestamp:
Feb 9, 2007, 11:28:02 AM (18 years ago)
Author:
andersca
Message:

JavaScriptCore:

Reviewed by Geoff.

<rdar://problem/4930614>
Safari complains about "Slow Script" if GMail is left open and machine is busy


<rdar://problem/4649516>
Turn off slow script dialog or crank up time that makes it come up


<rdar://problem/4963589>
Slow script warning is displayed after closing of PROMPT or PRINT dialog


Re-do the way script timeouts are handled. No longer use a unix timer that sends signals. Instead, add a
tick count and increment it in loop bodies. If the tick count reaches a threshold, do a timeout check. If the total time executing
is higher than the timeout value, (possibly) interrupt the script. The timeout checker also adjusts the threshold dynamically
to prevent doing the timeout check too often.



  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Add winmm.lib.
  • kjs/interpreter.cpp: (KJS::Interpreter::init): (KJS::Interpreter::~Interpreter): (KJS::Interpreter::startTimeoutCheck): (KJS::Interpreter::stopTimeoutCheck): (KJS::Interpreter::resetTimeoutCheck): (KJS::getCurrentTime): (KJS::Interpreter::checkTimeout):
  • kjs/interpreter.h: (KJS::Interpreter::timedOut):
  • kjs/nodes.cpp: (DoWhileNode::execute): (WhileNode::execute): (ForNode::execute):

WebCore:

Reviewed by Geoff.

No need to pause timeout checks anymore.


  • bindings/js/kjs_window.cpp: (KJS::WindowFunc::callAsFunction):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/interpreter.cpp

    r18461 r19534  
    5858#include <stdio.h>
    5959
     60#if PLATFORM(WIN_OS)
     61#include <windows.h>
     62#endif
     63
    6064namespace KJS {
    6165
    62 class TimeoutChecker {
    63 public:
    64     void startTimeoutCheck(Interpreter*);
    65     void stopTimeoutCheck(Interpreter*);
    66     void pauseTimeoutCheck(Interpreter*);
    67     void resumeTimeoutCheck(Interpreter*);
    68 
    69 private:
    70 #if HAVE(SYS_TIME_H)
    71     static Interpreter* s_executingInterpreter;
    72     static void alarmHandler(int);
    73    
    74     Interpreter* m_oldInterpreter;
    75     itimerval m_oldtv;
    76     itimerval m_pausetv;   
    77     void (*m_oldAlarmHandler)(int);
    78 #endif
    79 };
    80 
    81 #if HAVE(SYS_TIME_H)
    82 Interpreter* TimeoutChecker::s_executingInterpreter = 0;
    83 #endif
    84 
    85 void TimeoutChecker::startTimeoutCheck(Interpreter *interpreter)
    86 {   
    87     if (!interpreter->m_timeoutTime)
    88         return;
    89    
    90     interpreter->m_startTimeoutCheckCount++;
    91    
    92 #if HAVE(SYS_TIME_H)
    93     if (s_executingInterpreter == interpreter)
    94         return;
    95    
    96     // Block signals
    97     m_oldAlarmHandler = signal(SIGALRM, SIG_IGN);
    98 
    99     m_oldInterpreter = s_executingInterpreter;
    100     s_executingInterpreter = interpreter;
    101    
    102     itimerval tv = {
    103       { interpreter->m_timeoutTime / 1000, (interpreter->m_timeoutTime % 1000) * 1000 },
    104       { interpreter->m_timeoutTime / 1000, (interpreter->m_timeoutTime % 1000) * 1000 }
    105     };
    106     setitimer(ITIMER_REAL, &tv, &m_oldtv);
    107    
    108     // Unblock signals
    109     signal(SIGALRM, alarmHandler);
    110 #endif
    111 }
    112 
    113 void TimeoutChecker::stopTimeoutCheck(Interpreter* interpreter)
    114 {
    115     if (!interpreter->m_timeoutTime)
    116         return;
    117 
    118     ASSERT(interpreter->m_startTimeoutCheckCount > 0);
    119    
    120     interpreter->m_startTimeoutCheckCount--;
    121    
    122     if (interpreter->m_startTimeoutCheckCount != 0)
    123         return;
    124        
    125 #if HAVE(SYS_TIME_H)
    126     signal(SIGALRM, SIG_IGN);
    127 
    128     s_executingInterpreter = m_oldInterpreter;
    129        
    130     setitimer(ITIMER_REAL, &m_oldtv, 0L);
    131     signal(SIGALRM, m_oldAlarmHandler);
    132 #endif   
    133 }
    134 
    135 #if HAVE(SYS_TIME_H)
    136 void TimeoutChecker::alarmHandler(int)
    137 {
    138     s_executingInterpreter->m_timedOut = true;
    139 }
    140 #endif
    141 
    142 void TimeoutChecker::pauseTimeoutCheck(Interpreter* interpreter)
    143 {
    144     if (interpreter->m_startTimeoutCheckCount == 0)
    145         return;
    146 
    147 #if HAVE(SYS_TIME_H)   
    148     ASSERT(interpreter == s_executingInterpreter);
    149 
    150     void (*currentSignalHandler)(int);
    151    
    152     // Block signal
    153     currentSignalHandler = signal(SIGALRM, SIG_IGN);
    154    
    155     if (currentSignalHandler != alarmHandler) {
    156         signal(SIGALRM, currentSignalHandler);
    157         return;
    158     }
    159 
    160     setitimer(ITIMER_REAL, 0L, &m_pausetv);
    161 #endif
    162 
    163     interpreter->m_pauseTimeoutCheckCount++;
    164 }
    165 
    166 void TimeoutChecker::resumeTimeoutCheck(Interpreter* interpreter)
    167 {
    168     if (interpreter->m_startTimeoutCheckCount == 0)
    169         return;
    170 
    171 #if HAVE(SYS_TIME_H)
    172     ASSERT(interpreter == s_executingInterpreter);
    173 #endif
    174 
    175     interpreter->m_pauseTimeoutCheckCount--;
    176 
    177     if (interpreter->m_pauseTimeoutCheckCount != 0)
    178         return;
    179 
    180 #if HAVE(SYS_TIME_H)
    181     void (*currentSignalHandler)(int);
    182    
    183     // Check so we have the right handler
    184     currentSignalHandler = signal(SIGALRM, SIG_IGN);
    185    
    186     if (currentSignalHandler != SIG_IGN) {
    187         signal(SIGALRM, currentSignalHandler);
    188         return;
    189     }
    190 
    191     setitimer(ITIMER_REAL, &m_pausetv, 0L);
    192 
    193     // Unblock signal
    194     currentSignalHandler = signal(SIGALRM, alarmHandler);   
    195 #endif
    196 }
     66// Default number of ticks before a timeout check should be done.
     67static const int initialTickCountThreshold = 255;
     68
     69// Preferred number of milliseconds between each timeout check
     70static const int preferredScriptCheckTimeInterval = 1000;
    19771
    19872Interpreter* Interpreter::s_hook = 0;
     
    228102    m_debugger= 0;
    229103    m_context = 0;
    230     m_timedOut = false;
    231     m_timeoutChecker = 0;
    232     m_startTimeoutCheckCount = 0;
    233     m_pauseTimeoutCheckCount = 0;
     104
     105    resetTimeoutCheck();
     106    m_timeoutCheckCount = 0;
     107   
    234108    m_compatMode = NativeMode;
    235109    m_argumentsPropertyName = &argumentsPropertyName;
     
    254128{
    255129    JSLock lock;
    256    
    257     ASSERT (m_startTimeoutCheckCount == 0);
    258     ASSERT (m_pauseTimeoutCheckCount == 0);
    259    
    260     delete m_timeoutChecker;
    261130   
    262131    if (m_debugger)
     
    775644void Interpreter::startTimeoutCheck()
    776645{
    777     if (!m_timeoutChecker)
    778         m_timeoutChecker = new TimeoutChecker;
    779    
    780     m_timeoutChecker->startTimeoutCheck(this);
     646    if (m_timeoutCheckCount == 0)
     647        resetTimeoutCheck();
     648   
     649    m_timeoutCheckCount++;
    781650}
    782651
    783652void Interpreter::stopTimeoutCheck()
    784653{
    785     ASSERT(m_timeoutChecker);
    786    
    787     m_timeoutChecker->stopTimeoutCheck(this);
    788 }
    789 
    790 void Interpreter::pauseTimeoutCheck()
    791 {
    792     ASSERT(m_timeoutChecker);
    793    
    794     m_timeoutChecker->pauseTimeoutCheck(this);
    795 }
    796 
    797 void Interpreter::resumeTimeoutCheck()
    798 {
    799     ASSERT(m_timeoutChecker);
    800 
    801     m_timeoutChecker->resumeTimeoutCheck(this);
    802 }
    803 
    804 bool Interpreter::handleTimeout()
    805 {
    806     m_timedOut = false;
    807 
    808     pauseTimeoutCheck();
    809     bool retval = shouldInterruptScript();
    810     resumeTimeoutCheck();
    811    
    812     return retval;
     654    m_timeoutCheckCount--;
     655}
     656
     657void Interpreter::resetTimeoutCheck()
     658{
     659    m_tickCount = 0;
     660    m_ticksUntilNextTimeoutCheck = initialTickCountThreshold;
     661    m_timeAtLastCheckTimeout = 0;
     662    m_timeExecuting = 0;
     663}
     664
     665// Returns the current time in milliseconds
     666// It doesn't matter what "current time" is here, just as long as
     667// it's possible to measure the time difference correctly.
     668static inline unsigned getCurrentTime() {
     669#if HAVE(SYS_TIME_H)
     670    struct timeval tv;
     671    gettimeofday(&tv, 0);
     672    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
     673#elif PLATFORM(WIN_OS)
     674    return timeGetTime();
     675#else
     676#error Platform does not have getCurrentTime function
     677#endif
     678}
     679
     680bool Interpreter::checkTimeout()
     681{   
     682    m_tickCount = 0;
     683   
     684    unsigned currentTime = getCurrentTime();
     685
     686    if (!m_timeAtLastCheckTimeout) {
     687        // Suspicious amount of looping in a script -- start timing it
     688        m_timeAtLastCheckTimeout = currentTime;
     689        return false;
     690    }
     691
     692    unsigned timeDiff = currentTime - m_timeAtLastCheckTimeout;
     693
     694    if (timeDiff == 0)
     695        timeDiff = 1;
     696   
     697    m_timeExecuting += timeDiff;
     698    m_timeAtLastCheckTimeout = currentTime;
     699   
     700    // Adjust the tick threshold so we get the next checkTimeout call in the interval specified in
     701    // preferredScriptCheckTimeInterval
     702    m_ticksUntilNextTimeoutCheck = (unsigned)((float)preferredScriptCheckTimeInterval / timeDiff) * m_ticksUntilNextTimeoutCheck;
     703
     704    // If the new threshold is 0 reset it to the default threshold. This can happen if the timeDiff is higher than the
     705    // preferred script check time interval.
     706    if (m_ticksUntilNextTimeoutCheck == 0)
     707        m_ticksUntilNextTimeoutCheck = initialTickCountThreshold;
     708
     709    if (m_timeoutTime && m_timeExecuting > m_timeoutTime) {
     710        if (shouldInterruptScript())
     711            return true;
     712       
     713        resetTimeoutCheck();
     714    }
     715   
     716    return false;
    813717}
    814718
Note: See TracChangeset for help on using the changeset viewer.