Index: third_party/WebKit/Source/core/workers/WorkerThread.cpp |
diff --git a/third_party/WebKit/Source/core/workers/WorkerThread.cpp b/third_party/WebKit/Source/core/workers/WorkerThread.cpp |
index 2df2a5ffec5362845b46e7bfd7fa33cca75aacea..e98767bbb1badde01a636a3c9a84cbd2c9d6f94d 100644 |
--- a/third_party/WebKit/Source/core/workers/WorkerThread.cpp |
+++ b/third_party/WebKit/Source/core/workers/WorkerThread.cpp |
@@ -71,8 +71,12 @@ public: |
if (WorkerOrWorkletScriptController* scriptController = globalScope->scriptController()) |
scriptController->getRejectedPromises()->processQueue(); |
if (globalScope->isClosing()) { |
+ // |m_workerThread| will eventually be requested to terminate. |
m_workerThread->workerReportingProxy().workerGlobalScopeClosed(); |
- m_workerThread->terminateFromWorkerThread(); |
+ |
+ // Dispose WorkerGlobalScope to stop associated ActiveDOMObjects |
+ // and close the event queue. |
+ m_workerThread->prepareForShutdown(); |
} |
} |
} |
@@ -104,6 +108,12 @@ unsigned WorkerThread::workerThreadCount() |
void WorkerThread::performTask(std::unique_ptr<ExecutionContextTask> task, bool isInstrumented) |
{ |
DCHECK(isCurrentThread()); |
+ { |
+ MutexLocker lock(m_threadStateMutex); |
+ if (m_readyToShutdown) |
+ return; |
+ } |
+ |
WorkerGlobalScope* globalScope = workerGlobalScope(); |
// If the thread is terminated before it had a chance initialize (see |
// WorkerThread::Initialize()), we mustn't run any of the posted tasks. |
@@ -128,13 +138,7 @@ std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::un |
} |
WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, WorkerReportingProxy& workerReportingProxy) |
- : m_started(false) |
- , m_terminated(false) |
- , m_shutdown(false) |
- , m_pausedInDebugger(false) |
- , m_runningDebuggerTask(false) |
- , m_shouldTerminateV8Execution(false) |
- , m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) |
+ : m_inspectorTaskRunner(adoptPtr(new InspectorTaskRunner())) |
, m_workerLoaderProxy(workerLoaderProxy) |
, m_workerReportingProxy(workerReportingProxy) |
, m_terminationEvent(adoptPtr(new WaitableEvent( |
@@ -234,29 +238,16 @@ void WorkerThread::initialize(PassOwnPtr<WorkerThreadStartupData> startupData) |
postInitialize(); |
} |
-void WorkerThread::shutdown() |
+void WorkerThread::performShutdownTask() |
{ |
DCHECK(isCurrentThread()); |
+#if DCHECK_IS_ON |
{ |
MutexLocker lock(m_threadStateMutex); |
- if (m_shutdown) |
- return; |
- m_shutdown = true; |
+ DCHECK(m_terminated); |
+ DCHECK(m_readyToShutdown); |
} |
- |
- // This should be called before we start the shutdown procedure. |
- workerReportingProxy().willDestroyWorkerGlobalScope(); |
- |
- InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); |
- workerGlobalScope()->dispose(); |
- |
- workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.get()); |
- postTask(BLINK_FROM_HERE, createSameThreadTask(&WorkerThread::performShutdownTask, this)); |
-} |
- |
-void WorkerThread::performShutdownTask() |
-{ |
- DCHECK(isCurrentThread()); |
+#endif |
// The below assignment will destroy the context, which will in turn notify |
// messaging proxy. We cannot let any objects survive past thread exit, |
@@ -298,43 +289,47 @@ void WorkerThread::terminate() |
if (m_terminationEvent) |
m_terminationEvent->signal(); |
- // If the thread has already initiated shutdown, just return. |
- if (m_shutdown) |
- return; |
- |
// If the worker thread was never initialized, don't start another |
// shutdown, but still wait for the thread to signal when shutdown has |
// completed on initialize(). |
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. |
- m_workerGlobalScope->scriptController()->willScheduleExecutionTermination(); |
- |
- if (workerBackingThread().workerScriptCount() == 1) { |
- // This condition is not entirely correct because other scripts |
- // can be being initialized or terminated simuletaneously. Though this |
+ if (!m_readyToShutdown) { |
+ // 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. |
+ // If |m_readyToShutdown| is set, the worker thread has already noticed |
+ // that the thread is about to be terminated and the worker global scope |
+ // is already disposed, so we don't have to explicitly terminate the |
+ // execution. |
+ m_workerGlobalScope->scriptController()->willScheduleExecutionTermination(); |
+ |
+ // This condition is not entirely correct because other scripts can |
+ // be being initialized or terminated simuletaneously. Though this |
// function itself is protected by a mutex, it is possible that |
// |workerScriptCount()| here is not consistent with that in |
// |initialize| and |shutdown|. |
- // TODO(yhirano): TerminateExecution should be called more carefully. |
- // https://crbug.com/413518 |
- if (m_runningDebuggerTask) { |
- // Terminating during debugger task may lead to crash due to heavy |
- // use of v8 api in debugger. Any debugger task is guaranteed to |
- // finish, so we can postpone termination after task has finished. |
- // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution |
- // access must be guarded by the lock. |
- m_shouldTerminateV8Execution = true; |
- } else { |
- isolate()->TerminateExecution(); |
+ if (workerBackingThread().workerScriptCount() == 1) { |
+ if (m_runningDebuggerTask) { |
+ // Terminating during debugger task may lead to crash due to |
+ // heavy use of v8 api in debugger. Any debugger task is |
+ // guaranteed to finish, so we can postpone termination after |
+ // task has finished. |
+ // Note: m_runningDebuggerTask and m_shouldTerminateV8Execution |
+ // access must be guarded by the lock. |
+ m_shouldTerminateV8Execution = true; |
+ } else { |
+ // TODO(yhirano): TerminateExecution should be called more |
+ // carefully (https://crbug.com/413518). |
+ isolate()->TerminateExecution(); |
+ } |
} |
} |
m_inspectorTaskRunner->kill(); |
- workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::shutdown, AllowCrossThreadAccess(this))); |
+ workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::prepareForShutdown, AllowCrossThreadAccess(this))); |
+ workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::performShutdownTask, AllowCrossThreadAccess(this))); |
} |
void WorkerThread::terminateAndWait() |
@@ -358,10 +353,20 @@ void WorkerThread::terminateAndWaitForAllWorkers() |
thread->m_shutdownEvent->wait(); |
} |
-void WorkerThread::terminateFromWorkerThread() |
+void WorkerThread::prepareForShutdown() |
{ |
DCHECK(isCurrentThread()); |
- shutdown(); |
+ { |
+ MutexLocker lock(m_threadStateMutex); |
+ if (m_readyToShutdown) |
+ return; |
+ m_readyToShutdown = true; |
+ } |
+ |
+ workerReportingProxy().willDestroyWorkerGlobalScope(); |
+ InspectorInstrumentation::allAsyncTasksCanceled(workerGlobalScope()); |
+ workerGlobalScope()->dispose(); |
+ workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.get()); |
} |
WorkerGlobalScope* WorkerThread::workerGlobalScope() |
@@ -403,7 +408,7 @@ void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task) |
{ |
{ |
MutexLocker lock(m_threadStateMutex); |
- if (m_shutdown) |
+ if (m_readyToShutdown) |
return; |
} |
m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerTask, AllowCrossThreadAccess(this), passed(std::move(task)))); |