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() |