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