| Index: Source/core/workers/WorkerThread.cpp
|
| diff --git a/Source/core/workers/WorkerThread.cpp b/Source/core/workers/WorkerThread.cpp
|
| index f59a49182a66ff3dcd4b57653f081d209767d181..292b10a626e8a442a8a9c1515a71c8b095555c95 100644
|
| --- a/Source/core/workers/WorkerThread.cpp
|
| +++ b/Source/core/workers/WorkerThread.cpp
|
| @@ -38,14 +38,13 @@
|
| #include "core/workers/WorkerClients.h"
|
| #include "core/workers/WorkerReportingProxy.h"
|
| #include "core/workers/WorkerThreadStartupData.h"
|
| -#include "platform/PlatformThreadData.h"
|
| #include "platform/Task.h"
|
| #include "platform/ThreadSafeFunctional.h"
|
| -#include "platform/ThreadTimers.h"
|
| #include "platform/heap/SafePoint.h"
|
| #include "platform/heap/ThreadState.h"
|
| #include "platform/weborigin/KURL.h"
|
| #include "public/platform/Platform.h"
|
| +#include "public/platform/WebScheduler.h"
|
| #include "public/platform/WebThread.h"
|
| #include "public/platform/WebWaitableEvent.h"
|
| #include "wtf/Noncopyable.h"
|
| @@ -55,8 +54,7 @@
|
| namespace blink {
|
|
|
| namespace {
|
| -const int64_t kShortIdleHandlerDelayMs = 1000;
|
| -const int64_t kLongIdleHandlerDelayMs = 10*1000;
|
| +const double kLongIdleHandlerDelaySecs = 1.0;
|
|
|
| class MicrotaskRunner : public WebThread::TaskObserver {
|
| public:
|
| @@ -101,100 +99,6 @@ unsigned WorkerThread::workerThreadCount()
|
| return workerThreads().size();
|
| }
|
|
|
| -class WorkerThreadCancelableTask final : public ExecutionContextTask {
|
| - WTF_MAKE_NONCOPYABLE(WorkerThreadCancelableTask); WTF_MAKE_FAST_ALLOCATED(WorkerThreadCancelableTask);
|
| -public:
|
| - static PassOwnPtr<WorkerThreadCancelableTask> create(PassOwnPtr<Closure> closure)
|
| - {
|
| - return adoptPtr(new WorkerThreadCancelableTask(closure));
|
| - }
|
| -
|
| - virtual ~WorkerThreadCancelableTask() { }
|
| -
|
| - virtual void performTask(ExecutionContext*) override
|
| - {
|
| - if (!m_taskCanceled)
|
| - (*m_closure)();
|
| - }
|
| -
|
| - WeakPtr<WorkerThreadCancelableTask> createWeakPtr() { return m_weakFactory.createWeakPtr(); }
|
| - void cancelTask() { m_taskCanceled = true; }
|
| -
|
| -private:
|
| - explicit WorkerThreadCancelableTask(PassOwnPtr<Closure> closure)
|
| - : m_closure(closure)
|
| - , m_weakFactory(this)
|
| - , m_taskCanceled(false)
|
| - { }
|
| -
|
| - OwnPtr<Closure> m_closure;
|
| - WeakPtrFactory<WorkerThreadCancelableTask> m_weakFactory;
|
| - bool m_taskCanceled;
|
| -};
|
| -
|
| -class WorkerSharedTimer : public SharedTimer {
|
| -public:
|
| - explicit WorkerSharedTimer(WorkerThread* workerThread)
|
| - : m_workerThread(workerThread)
|
| - , m_running(false)
|
| - { }
|
| -
|
| - typedef void (*SharedTimerFunction)();
|
| - virtual void setFiredFunction(SharedTimerFunction func)
|
| - {
|
| - m_sharedTimerFunction = func;
|
| - }
|
| -
|
| - virtual void setFireInterval(double interval)
|
| - {
|
| - ASSERT(m_sharedTimerFunction);
|
| -
|
| - // See BlinkPlatformImpl::setSharedTimerFireInterval for explanation of
|
| - // why ceil is used in the interval calculation.
|
| - int64_t delay = static_cast<int64_t>(ceil(interval * 1000));
|
| -
|
| - if (delay < 0) {
|
| - delay = 0;
|
| - }
|
| -
|
| - m_running = true;
|
| -
|
| - if (m_lastQueuedTask.get())
|
| - m_lastQueuedTask->cancelTask();
|
| -
|
| - // Now queue the task as a cancellable one.
|
| - OwnPtr<WorkerThreadCancelableTask> task = WorkerThreadCancelableTask::create(bind(&WorkerSharedTimer::OnTimeout, this));
|
| - m_lastQueuedTask = task->createWeakPtr();
|
| - m_workerThread->postDelayedTask(FROM_HERE, task.release(), delay);
|
| - }
|
| -
|
| - virtual void stop()
|
| - {
|
| - m_running = false;
|
| - m_lastQueuedTask = nullptr;
|
| - }
|
| -
|
| -private:
|
| - void OnTimeout()
|
| - {
|
| - ASSERT(m_workerThread->workerGlobalScope());
|
| -
|
| - m_lastQueuedTask = nullptr;
|
| -
|
| - if (m_sharedTimerFunction && m_running && !m_workerThread->workerGlobalScope()->isClosing())
|
| - m_sharedTimerFunction();
|
| - }
|
| -
|
| - WorkerThread* m_workerThread;
|
| - SharedTimerFunction m_sharedTimerFunction;
|
| - bool m_running;
|
| -
|
| - // The task to run OnTimeout, if any. While OnTimeout resets
|
| - // m_lastQueuedTask, this must be a weak pointer because the
|
| - // worker runloop may delete the task as it is shutting down.
|
| - WeakPtr<WorkerThreadCancelableTask> m_lastQueuedTask;
|
| -};
|
| -
|
| class WorkerThreadTask : public blink::WebThread::Task {
|
| WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED(WorkerThreadTask);
|
| public:
|
| @@ -265,6 +169,8 @@ WorkerThread::WorkerThread(const char* threadName, PassRefPtr<WorkerLoaderProxy>
|
| , m_isolate(nullptr)
|
| , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
|
| , m_terminationEvent(adoptPtr(blink::Platform::current()->createWaitableEvent()))
|
| + , m_webScheduler(nullptr)
|
| + , m_preShutdown(false)
|
| {
|
| MutexLocker lock(threadSetMutex());
|
| workerThreads().add(this);
|
| @@ -283,6 +189,7 @@ void WorkerThread::start()
|
| return;
|
|
|
| m_thread = createWebThreadSupportingGC();
|
| + m_webScheduler = m_thread->platformThread().scheduler();
|
| m_thread->postTask(FROM_HERE, new Task(threadSafeBind(&WorkerThread::initialize, AllowCrossThreadAccess(this))));
|
| }
|
|
|
| @@ -300,6 +207,24 @@ PlatformThreadId WorkerThread::platformThreadId() const
|
| return m_thread->platformThread().threadId();
|
| }
|
|
|
| +// TODO(alexclarke): Use base::Bind instead of this class when the repo's merge. Unfortunately we
|
| +// can't use WTF::bind because the type-erasure for member function pointers with parameters is broken.
|
| +class WorkerThreadIdleTask : public WebThread::IdleTask {
|
| +public:
|
| + explicit WorkerThreadIdleTask(WorkerThread* thread)
|
| + : m_thread(thread) { }
|
| +
|
| + ~WorkerThreadIdleTask() override { }
|
| +
|
| + void run(double deadlineSeconds) override
|
| + {
|
| + m_thread->idleTask(deadlineSeconds);
|
| + }
|
| +
|
| +private:
|
| + RawPtr<WorkerThread> m_thread; // NOT OWNED
|
| +};
|
| +
|
| void WorkerThread::initialize()
|
| {
|
| KURL scriptURL = m_startupData->m_scriptURL;
|
| @@ -326,8 +251,6 @@ void WorkerThread::initialize()
|
| m_isolate = initializeIsolate();
|
| m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release());
|
| m_workerGlobalScope->scriptLoaded(sourceCode.length(), cachedMetaData.get() ? cachedMetaData->size() : 0);
|
| -
|
| - PlatformThreadData::current().threadTimers().setSharedTimer(adoptPtr(new WorkerSharedTimer(this)));
|
| }
|
|
|
| // The corresponding call to stopRunLoop() is in ~WorkerScriptController().
|
| @@ -349,7 +272,7 @@ void WorkerThread::initialize()
|
|
|
| postInitialize();
|
|
|
| - postDelayedTask(FROM_HERE, createSameThreadTask(&WorkerThread::idleHandler, this), kShortIdleHandlerDelayMs);
|
| + m_webScheduler->postIdleTaskAfterWakeup(FROM_HERE, new WorkerThreadIdleTask(this));
|
| }
|
|
|
| PassOwnPtr<WebThreadSupportingGC> WorkerThread::createWebThreadSupportingGC()
|
| @@ -382,9 +305,6 @@ void WorkerThread::cleanup()
|
| workerReportingProxy().workerThreadTerminated();
|
|
|
| m_terminationEvent->signal();
|
| -
|
| - // Clean up PlatformThreadData before WTF::WTFThreadData goes away!
|
| - PlatformThreadData::current().destroy();
|
| }
|
|
|
| class WorkerThreadShutdownFinishTask : public ExecutionContextTask {
|
| @@ -401,7 +321,7 @@ public:
|
|
|
| WorkerThread* workerThread = workerGlobalScope->thread();
|
| workerThread->willDestroyIsolate();
|
| - workerThread->m_thread->postTask(FROM_HERE, new Task(WTF::bind(&WorkerThread::cleanup, workerThread)));
|
| + workerThread->m_webScheduler->postShutdownTask(FROM_HERE, new Task(WTF::bind(&WorkerThread::cleanup, workerThread)), 0);
|
| }
|
|
|
| virtual bool isCleanupTask() const { return true; }
|
| @@ -418,7 +338,6 @@ public:
|
| {
|
| WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
|
| workerGlobalScope->stopActiveDOMObjects();
|
| - PlatformThreadData::current().threadTimers().setSharedTimer(nullptr);
|
|
|
| // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects,
|
| // which become dangling once Heap is destroyed.
|
| @@ -432,6 +351,41 @@ public:
|
| virtual bool isCleanupTask() const { return true; }
|
| };
|
|
|
| +class PreShutDownTask : public WebThread::Task {
|
| +public:
|
| + PreShutDownTask(WebWaitableEvent* completion, WebScheduler* webScheduler)
|
| + : m_completion(completion)
|
| + , m_webScheduler(webScheduler) { }
|
| +
|
| + ~PreShutDownTask() override { }
|
| +
|
| + void run()
|
| + {
|
| + m_webScheduler->preShutdown();
|
| + m_completion->signal();
|
| + }
|
| +
|
| +private:
|
| + RawPtr<WebWaitableEvent> m_completion; // NOT OWNED.
|
| + RawPtr<WebScheduler> m_webScheduler; // NOT OWNED.
|
| +};
|
| +
|
| +void WorkerThread::preShutdown()
|
| +{
|
| + MutexLocker lock(m_preShutdownMutex);
|
| + if (m_preShutdown || !m_webScheduler)
|
| + return;
|
| + m_preShutdown = true;
|
| +
|
| + if (m_thread->isCurrentThread()) {
|
| + m_webScheduler->preShutdown();
|
| + } else {
|
| + OwnPtr<WebWaitableEvent> completion(adoptPtr(blink::Platform::current()->createWaitableEvent()));
|
| + m_thread->postTask(FROM_HERE, new PreShutDownTask(completion.get(), m_webScheduler.get()));
|
| + completion->wait();
|
| + }
|
| +}
|
| +
|
| void WorkerThread::stop()
|
| {
|
| // Prevent the deadlock between GC and an attempt to stop a thread.
|
| @@ -459,26 +413,30 @@ bool WorkerThread::terminated()
|
| void WorkerThread::stopInternal()
|
| {
|
| // Protect against this method and initialize() racing each other.
|
| - MutexLocker lock(m_threadCreationMutex);
|
| + {
|
| + MutexLocker lock(m_threadCreationMutex);
|
|
|
| - // If stop has already been called, just return.
|
| - if (m_terminated)
|
| - return;
|
| - m_terminated = true;
|
| + // If stop has already been called, just return.
|
| + if (m_terminated)
|
| + return;
|
| + m_terminated = true;
|
|
|
| - // Signal the thread to notify that the thread's stopping.
|
| - if (m_shutdownEvent)
|
| - m_shutdownEvent->signal();
|
| + // Signal the thread to notify that the thread's stopping.
|
| + if (m_shutdownEvent)
|
| + m_shutdownEvent->signal();
|
|
|
| - if (!m_workerGlobalScope)
|
| - return;
|
| + if (!m_workerGlobalScope)
|
| + return;
|
|
|
| - // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
|
| - terminateV8Execution();
|
| + // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever.
|
| + terminateV8Execution();
|
|
|
| - InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get());
|
| - m_debuggerMessageQueue.kill();
|
| - postTask(FROM_HERE, WorkerThreadShutdownStartTask::create());
|
| + InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get());
|
| + m_debuggerMessageQueue.kill();
|
| + postTask(FROM_HERE, WorkerThreadShutdownStartTask::create());
|
| + }
|
| + // Prevent further execution of non-shutdown tasks on the thread. Must happen after any v8 task has been terminated.
|
| + preShutdown();
|
| }
|
|
|
| void WorkerThread::didStartRunLoop()
|
| @@ -510,31 +468,46 @@ bool WorkerThread::isCurrentThread() const
|
| return m_thread && m_thread->isCurrentThread();
|
| }
|
|
|
| -void WorkerThread::idleHandler()
|
| +void WorkerThread::idleTask(double deadlineSeconds)
|
| {
|
| - ASSERT(m_workerGlobalScope.get());
|
| - int64_t delay = kLongIdleHandlerDelayMs;
|
| + if (terminated())
|
| + return;
|
|
|
| - // Do a script engine idle notification if the next event is distant enough.
|
| - const double kMinIdleTimespan = 0.3;
|
| - const double nextFireTime = PlatformThreadData::current().threadTimers().nextFireTime();
|
| - if (nextFireTime == 0.0 || nextFireTime > currentTime() + kMinIdleTimespan) {
|
| - bool hasMoreWork = !isolate()->IdleNotificationDeadline(Platform::current()->monotonicallyIncreasingTime() + 1.0);
|
| - if (hasMoreWork)
|
| - delay = kShortIdleHandlerDelayMs;
|
| - }
|
| + double gcDeadlineSeconds = deadlineSeconds;
|
|
|
| - postDelayedTask(FROM_HERE, createSameThreadTask(&WorkerThread::idleHandler, this), delay);
|
| + // The V8 GC does some GC steps (e.g. compaction) only when the idle notification is ~1s.
|
| + // TODO(rmcilroy): Refactor so extending the deadline like this this isn't needed.
|
| + if (m_webScheduler->canExceedIdleDeadlineIfRequired())
|
| + gcDeadlineSeconds = Platform::current()->monotonicallyIncreasingTime() + kLongIdleHandlerDelaySecs;
|
| +
|
| + if (doIdleGc(gcDeadlineSeconds))
|
| + m_webScheduler->postIdleTaskAfterWakeup(FROM_HERE, new WorkerThreadIdleTask(this));
|
| + else
|
| + m_webScheduler->postIdleTask(FROM_HERE, new WorkerThreadIdleTask(this));
|
| +}
|
| +
|
| +bool WorkerThread::doIdleGc(double deadlineSeconds)
|
| +{
|
| + bool gcFinished = false;
|
| + if (deadlineSeconds > Platform::current()->monotonicallyIncreasingTime())
|
| + gcFinished = isolate()->IdleNotificationDeadline(deadlineSeconds);
|
| + return gcFinished;
|
| }
|
|
|
| void WorkerThread::postTask(const WebTraceLocation& location, PassOwnPtr<ExecutionContextTask> task)
|
| {
|
| - m_thread->postTask(location, WorkerThreadTask::create(*this, task, true).leakPtr());
|
| + if (task->isCleanupTask())
|
| + m_webScheduler->postShutdownTask(location, WorkerThreadTask::create(*this, task, true).leakPtr(), 0);
|
| + else
|
| + m_thread->postTask(location, WorkerThreadTask::create(*this, task, true).leakPtr());
|
| }
|
|
|
| void WorkerThread::postDelayedTask(const WebTraceLocation& location, PassOwnPtr<ExecutionContextTask> task, long long delayMs)
|
| {
|
| - m_thread->postDelayedTask(location, WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs);
|
| + if (task->isCleanupTask())
|
| + m_webScheduler->postShutdownTask(location, WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs);
|
| + else
|
| + m_thread->postDelayedTask(location, WorkerThreadTask::create(*this, task, true).leakPtr(), delayMs);
|
| }
|
|
|
| v8::Isolate* WorkerThread::initializeIsolate()
|
|
|