| Index: Source/core/workers/WorkerThread.cpp
 | 
| diff --git a/Source/core/workers/WorkerThread.cpp b/Source/core/workers/WorkerThread.cpp
 | 
| index 4afc1f86c0dd84d85880c9f0ca1d31ee3f73c104..2f93068f30f1ab643c47c5a1ff4087362f41f7bb 100644
 | 
| --- a/Source/core/workers/WorkerThread.cpp
 | 
| +++ b/Source/core/workers/WorkerThread.cpp
 | 
| @@ -180,48 +180,50 @@ private:
 | 
|      WorkerThreadCancelableTask* m_lastQueuedTask;
 | 
|  };
 | 
|  
 | 
| -class WorkerThreadTask : public blink::WebThread::Task {
 | 
| -    WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED;
 | 
| -public:
 | 
| -    static PassOwnPtr<WorkerThreadTask> create(WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
 | 
| -    {
 | 
| -        return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented));
 | 
| -    }
 | 
| -
 | 
| -    virtual ~WorkerThreadTask() { }
 | 
| +// WorkerThreadTask made private to WorkerThread
 | 
| +PassOwnPtr<WorkerThread::WorkerThreadTask> WorkerThread::WorkerThreadTask::create(WorkerThread& workerThread
 | 
| +    , PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
 | 
| +{
 | 
| +    return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented));
 | 
| +}
 | 
|  
 | 
| -    virtual void run() override
 | 
| -    {
 | 
| -        WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope();
 | 
| -        // Tasks could be put on the message loop after the cleanup task,
 | 
| -        // ensure none of those are ran.
 | 
| -        if (!workerGlobalScope)
 | 
| -            return;
 | 
| +void WorkerThread::WorkerThreadTask::run()
 | 
| +{
 | 
| +    WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope();
 | 
| +    // Tasks could be put on the message loop after the cleanup task,
 | 
| +    // ensure none of those are ran.
 | 
| +    if (!workerGlobalScope || m_isTaskCanceled)
 | 
| +        return;
 | 
|  
 | 
| -        if (m_isInstrumented)
 | 
| -            InspectorInstrumentation::willPerformExecutionContextTask(workerGlobalScope, m_task.get());
 | 
| -        if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) || m_task->isCleanupTask())
 | 
| +    if (m_isInstrumented)
 | 
| +        InspectorInstrumentation::willPerformExecutionContextTask(workerGlobalScope, m_task.get());
 | 
| +    if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) || m_task->isCleanupTask()) {
 | 
| +        if (!m_isIdleHandlerTask || (m_isIdleHandlerTask && !m_workerThread.taskCount()))
 | 
|              m_task->performTask(workerGlobalScope);
 | 
| -        if (m_isInstrumented)
 | 
| -            InspectorInstrumentation::didPerformExecutionContextTask(workerGlobalScope);
 | 
|      }
 | 
| +    if (m_isInstrumented)
 | 
| +        InspectorInstrumentation::didPerformExecutionContextTask(workerGlobalScope);
 | 
|  
 | 
| -private:
 | 
| -    WorkerThreadTask(WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
 | 
| -        : m_workerThread(workerThread)
 | 
| -        , m_task(task)
 | 
| -        , m_isInstrumented(isInstrumented)
 | 
| -    {
 | 
| -        if (m_isInstrumented)
 | 
| -            m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty();
 | 
| -        if (m_isInstrumented)
 | 
| -            InspectorInstrumentation::didPostExecutionContextTask(m_workerThread.workerGlobalScope(), m_task.get());
 | 
| -    }
 | 
| +    // If no more tasks are queued up after this, then queue up the idleHandler
 | 
| +    // to trigger GC.
 | 
| +    if (!m_isIdleHandlerTask && !m_workerThread.decrementAndReturnTaskCount() && !m_workerThread.m_lastQueuedIdleHandlerTask)
 | 
| +        m_workerThread.queueUpIdleHandlerNow(kLongIdleHandlerDelayMs);
 | 
| +}
 | 
|  
 | 
| -    WorkerThread& m_workerThread;
 | 
| -    OwnPtr<ExecutionContextTask> m_task;
 | 
| -    bool m_isInstrumented;
 | 
| -};
 | 
| +WorkerThread::WorkerThreadTask::WorkerThreadTask(WorkerThread& workerThread
 | 
| +    , PassOwnPtr<ExecutionContextTask> task, bool isInstrumented)
 | 
| +    : m_workerThread(workerThread)
 | 
| +    , m_task(task)
 | 
| +    , m_isInstrumented(isInstrumented)
 | 
| +    , m_isIdleHandlerTask(false)
 | 
| +    , m_isTaskCanceled(false)
 | 
| +    , m_timeStamp(currentTime())
 | 
| +{
 | 
| +    if (m_isInstrumented)
 | 
| +        m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty();
 | 
| +    if (m_isInstrumented)
 | 
| +        InspectorInstrumentation::didPostExecutionContextTask(m_workerThread.workerGlobalScope(), m_task.get());
 | 
| +}
 | 
|  
 | 
|  class RunDebuggerQueueTask final : public ExecutionContextTask {
 | 
|  public:
 | 
| @@ -248,6 +250,8 @@ WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReporting
 | 
|      , m_startupData(startupData)
 | 
|      , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
 | 
|      , m_terminationEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
 | 
| +    , m_lastQueuedIdleHandlerTask(nullptr)
 | 
| +    , m_tasksCount(0)
 | 
|  {
 | 
|      MutexLocker lock(threadSetMutex());
 | 
|      workerThreads().add(this);
 | 
| @@ -330,7 +334,6 @@ void WorkerThread::initialize()
 | 
|  
 | 
|  void WorkerThread::cleanup()
 | 
|  {
 | 
| -
 | 
|      // This should be called before we start the shutdown procedure.
 | 
|      workerReportingProxy().willDestroyWorkerGlobalScope();
 | 
|  
 | 
| @@ -462,29 +465,80 @@ bool WorkerThread::isCurrentThread() const
 | 
|      return m_thread && m_thread->isCurrentThread();
 | 
|  }
 | 
|  
 | 
| +void WorkerThread::queueUpIdleHandlerNow(long long delayMs)
 | 
| +{
 | 
| +    OwnPtr<WorkerThreadTask> idleHandlerTask = WorkerThreadTask::create(*this, createSameThreadTask(&WorkerThread::idleHandler, this), true);
 | 
| +
 | 
| +    idleHandlerTask->setIsIdleHandlerTask(true);
 | 
| +
 | 
| +    if (clearIdleHandlerTaskIfSet()) {
 | 
| +        WorkerThreadTask* tmp = idleHandlerTask.get();
 | 
| +        m_thread->postDelayedTask(idleHandlerTask.leakPtr(), delayMs);
 | 
| +        m_lastQueuedIdleHandlerTask = tmp;
 | 
| +    }
 | 
| +}
 | 
| +
 | 
|  void WorkerThread::idleHandler()
 | 
|  {
 | 
|      ASSERT(m_workerGlobalScope.get());
 | 
|      int64_t delay = kLongIdleHandlerDelayMs;
 | 
| +    m_lastQueuedIdleHandlerTask = nullptr;
 | 
| +
 | 
| +    // Some tasks may have been started in the mean time, so lets return back
 | 
| +    // and wait for the next idleHandler to be fired.
 | 
| +    if (m_tasksCount)
 | 
| +        return;
 | 
|  
 | 
|      // Do a script engine idle notification if the next event is distant enough.
 | 
|      const double kMinIdleTimespan = 0.3;
 | 
| +    // This is set to true when idleNotification returns false.
 | 
| +    // idleNotification returns false only when more GC is required. It returns
 | 
| +    // true if no more GC is required and that is when callGCAgain will be set to false.
 | 
| +    bool callGCAgain = false;
 | 
|      if (m_sharedTimer->nextFireTime() == 0.0 || m_sharedTimer->nextFireTime() > currentTime() + kMinIdleTimespan) {
 | 
| -        bool hasMoreWork = !m_workerGlobalScope->idleNotification();
 | 
| -        if (hasMoreWork)
 | 
| +        callGCAgain = !m_workerGlobalScope->idleNotification();
 | 
| +        if (callGCAgain)
 | 
|              delay = kShortIdleHandlerDelayMs;
 | 
|      }
 | 
|  
 | 
| -    postDelayedTask(createSameThreadTask(&WorkerThread::idleHandler, this), delay);
 | 
| +    if (callGCAgain)
 | 
| +        queueUpIdleHandlerNow(delay);
 | 
| +}
 | 
| +
 | 
| +int WorkerThread::decrementAndReturnTaskCount()
 | 
| +{
 | 
| +    atomicDecrement(&m_tasksCount);
 | 
| +    return m_tasksCount;
 | 
| +}
 | 
| +
 | 
| +bool WorkerThread::clearIdleHandlerTaskIfSet()
 | 
| +{
 | 
| +    // If the event was created more than 3 seconds ago, so its too close to the task completion
 | 
| +    // hence cancel it and create a new one.
 | 
| +    const double kIdleHandlerAgeThreshold = 3.0;
 | 
| +    if (!m_lastQueuedIdleHandlerTask)
 | 
| +        return true;
 | 
| +    double idleTaskHandlerAge = currentTime() - m_lastQueuedIdleHandlerTask->creationTimeStamp();
 | 
| +    if (idleTaskHandlerAge < kIdleHandlerAgeThreshold)
 | 
| +        return false;
 | 
| +
 | 
| +    m_lastQueuedIdleHandlerTask->cancelTask();
 | 
| +    m_lastQueuedIdleHandlerTask = nullptr;
 | 
| +
 | 
| +    return true;
 | 
|  }
 | 
|  
 | 
|  void WorkerThread::postTask(PassOwnPtr<ExecutionContextTask> task)
 | 
|  {
 | 
| +    atomicIncrement(&m_tasksCount);
 | 
| +    clearIdleHandlerTaskIfSet();
 | 
|      m_thread->postTask(WorkerThreadTask::create(*this, task, true).leakPtr());
 | 
|  }
 | 
|  
 | 
|  void WorkerThread::postDelayedTask(PassOwnPtr<ExecutionContextTask> task, long long delayMs)
 | 
|  {
 | 
| +    atomicIncrement(&m_tasksCount);
 | 
| +    clearIdleHandlerTaskIfSet();
 | 
|      m_thread->postDelayedTask(WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs);
 | 
|  }
 | 
|  
 | 
| 
 |