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); |
} |