| 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 f78e15e1b3a881a32c8cfc541a6a8a4588925e64..09d432d27e07d2e84d786d659f64ba7650f45ca3 100644
|
| --- a/third_party/WebKit/Source/core/workers/WorkerThread.cpp
|
| +++ b/third_party/WebKit/Source/core/workers/WorkerThread.cpp
|
| @@ -76,7 +76,7 @@ public:
|
|
|
| // Dispose WorkerGlobalScope to stop associated ActiveDOMObjects
|
| // and close the event queue.
|
| - m_workerThread->prepareForShutdown();
|
| + m_workerThread->prepareForShutdownOnWorkerThread();
|
| }
|
| }
|
| }
|
| @@ -99,42 +99,184 @@ static HashSet<WorkerThread*>& workerThreads()
|
| return threads;
|
| }
|
|
|
| -unsigned WorkerThread::workerThreadCount()
|
| +WorkerThread::~WorkerThread()
|
| {
|
| MutexLocker lock(threadSetMutex());
|
| - return workerThreads().size();
|
| + DCHECK(workerThreads().contains(this));
|
| + workerThreads().remove(this);
|
| }
|
|
|
| -void WorkerThread::performTask(std::unique_ptr<ExecutionContextTask> task, bool isInstrumented)
|
| +void WorkerThread::start(PassOwnPtr<WorkerThreadStartupData> startupData)
|
| +{
|
| + DCHECK(isMainThread());
|
| +
|
| + if (m_started)
|
| + return;
|
| +
|
| + m_started = true;
|
| + workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::initializeOnWorkerThread, AllowCrossThreadAccess(this), passed(std::move(startupData))));
|
| +}
|
| +
|
| +void WorkerThread::terminate()
|
| +{
|
| + DCHECK(isMainThread());
|
| +
|
| + // Prevent the deadlock between GC and an attempt to terminate a thread.
|
| + SafePointScope safePointScope(BlinkGC::HeapPointersOnStack);
|
| +
|
| + // Protect against this method, initializeOnWorkerThread() or termination
|
| + // via the global scope racing each other.
|
| + MutexLocker lock(m_threadStateMutex);
|
| +
|
| + // If terminate 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_terminationEvent)
|
| + m_terminationEvent->signal();
|
| +
|
| + // 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 initializeOnWorkerThread().
|
| + if (!m_workerGlobalScope)
|
| + return;
|
| +
|
| + 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|.
|
| + 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::prepareForShutdownOnWorkerThread, AllowCrossThreadAccess(this)));
|
| + workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::performShutdownOnWorkerThread, AllowCrossThreadAccess(this)));
|
| +}
|
| +
|
| +void WorkerThread::terminateAndWait()
|
| +{
|
| + DCHECK(isMainThread());
|
| + terminate();
|
| + m_shutdownEvent->wait();
|
| +}
|
| +
|
| +void WorkerThread::terminateAndWaitForAllWorkers()
|
| +{
|
| + DCHECK(isMainThread());
|
| +
|
| + // Keep this lock to prevent WorkerThread instances from being destroyed.
|
| + MutexLocker lock(threadSetMutex());
|
| + HashSet<WorkerThread*> threads = workerThreads();
|
| + for (WorkerThread* thread : threads)
|
| + thread->terminate();
|
| +
|
| + for (WorkerThread* thread : threads)
|
| + thread->m_shutdownEvent->wait();
|
| +}
|
| +
|
| +v8::Isolate* WorkerThread::isolate()
|
| +{
|
| + return workerBackingThread().isolate();
|
| +}
|
| +
|
| +bool WorkerThread::isCurrentThread()
|
| +{
|
| + return m_started && workerBackingThread().backingThread().isCurrentThread();
|
| +}
|
| +
|
| +void WorkerThread::postTask(const WebTraceLocation& location, std::unique_ptr<ExecutionContextTask> task)
|
| +{
|
| + workerBackingThread().backingThread().postTask(location, createWorkerThreadTask(std::move(task), true));
|
| +}
|
| +
|
| +void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task)
|
| {
|
| - 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.
|
| - if (!globalScope) {
|
| - DCHECK(terminated());
|
| - return;
|
| + m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerTaskOnWorkerThread, AllowCrossThreadAccess(this), passed(std::move(task))));
|
| + {
|
| + MutexLocker lock(m_threadStateMutex);
|
| + if (isolate())
|
| + m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate());
|
| }
|
| + workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::runDebuggerTaskDontWaitOnWorkerThread, AllowCrossThreadAccess(this)));
|
| +}
|
|
|
| - InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isInstrumented);
|
| - task->performTask(globalScope);
|
| +void WorkerThread::startRunningDebuggerTasksOnPause()
|
| +{
|
| + m_pausedInDebugger = true;
|
| + ThreadDebugger::idleStarted(isolate());
|
| + std::unique_ptr<CrossThreadClosure> task;
|
| + do {
|
| + {
|
| + SafePointScope safePointScope(BlinkGC::HeapPointersOnStack);
|
| + task = m_inspectorTaskRunner->takeNextTask(InspectorTaskRunner::WaitForTask);
|
| + }
|
| + if (task)
|
| + (*task)();
|
| + // Keep waiting until execution is resumed.
|
| + } while (task && m_pausedInDebugger);
|
| + ThreadDebugger::idleFinished(isolate());
|
| }
|
|
|
| -std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::unique_ptr<ExecutionContextTask> task, bool isInstrumented)
|
| +void WorkerThread::stopRunningDebuggerTasksOnPause()
|
| {
|
| - if (isInstrumented)
|
| - isInstrumented = !task->taskNameForInstrumentation().isEmpty();
|
| - if (isInstrumented) {
|
| - DCHECK(isCurrentThread());
|
| - InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worker task", task.get());
|
| - }
|
| - return threadSafeBind(&WorkerThread::performTask, AllowCrossThreadAccess(this), passed(std::move(task)), isInstrumented);
|
| + m_pausedInDebugger = false;
|
| +}
|
| +
|
| +WorkerGlobalScope* WorkerThread::workerGlobalScope()
|
| +{
|
| + DCHECK(isCurrentThread());
|
| + return m_workerGlobalScope.get();
|
| +}
|
| +
|
| +bool WorkerThread::terminated()
|
| +{
|
| + MutexLocker lock(m_threadStateMutex);
|
| + return m_terminated;
|
| +}
|
| +
|
| +unsigned WorkerThread::workerThreadCount()
|
| +{
|
| + MutexLocker lock(threadSetMutex());
|
| + return workerThreads().size();
|
| +}
|
| +
|
| +PlatformThreadId WorkerThread::platformThreadId()
|
| +{
|
| + if (!m_started)
|
| + return 0;
|
| + return workerBackingThread().backingThread().platformThread().threadId();
|
| }
|
|
|
| WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, WorkerReportingProxy& workerReportingProxy)
|
| @@ -152,32 +294,18 @@ WorkerThread::WorkerThread(PassRefPtr<WorkerLoaderProxy> workerLoaderProxy, Work
|
| workerThreads().add(this);
|
| }
|
|
|
| -WorkerThread::~WorkerThread()
|
| -{
|
| - MutexLocker lock(threadSetMutex());
|
| - DCHECK(workerThreads().contains(this));
|
| - workerThreads().remove(this);
|
| -}
|
| -
|
| -void WorkerThread::start(PassOwnPtr<WorkerThreadStartupData> startupData)
|
| -{
|
| - DCHECK(isMainThread());
|
| -
|
| - if (m_started)
|
| - return;
|
| -
|
| - m_started = true;
|
| - workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::initialize, AllowCrossThreadAccess(this), passed(std::move(startupData))));
|
| -}
|
| -
|
| -PlatformThreadId WorkerThread::platformThreadId()
|
| +std::unique_ptr<CrossThreadClosure> WorkerThread::createWorkerThreadTask(std::unique_ptr<ExecutionContextTask> task, bool isInstrumented)
|
| {
|
| - if (!m_started)
|
| - return 0;
|
| - return workerBackingThread().backingThread().platformThread().threadId();
|
| + if (isInstrumented)
|
| + isInstrumented = !task->taskNameForInstrumentation().isEmpty();
|
| + if (isInstrumented) {
|
| + DCHECK(isCurrentThread());
|
| + InspectorInstrumentation::asyncTaskScheduled(workerGlobalScope(), "Worker task", task.get());
|
| + }
|
| + return threadSafeBind(&WorkerThread::performTaskOnWorkerThread, AllowCrossThreadAccess(this), passed(std::move(task)), isInstrumented);
|
| }
|
|
|
| -void WorkerThread::initialize(PassOwnPtr<WorkerThreadStartupData> startupData)
|
| +void WorkerThread::initializeOnWorkerThread(PassOwnPtr<WorkerThreadStartupData> startupData)
|
| {
|
| KURL scriptURL = startupData->m_scriptURL;
|
| String sourceCode = startupData->m_sourceCode;
|
| @@ -238,7 +366,7 @@ void WorkerThread::initialize(PassOwnPtr<WorkerThreadStartupData> startupData)
|
| postInitialize();
|
| }
|
|
|
| -void WorkerThread::prepareForShutdown()
|
| +void WorkerThread::prepareForShutdownOnWorkerThread()
|
| {
|
| DCHECK(isCurrentThread());
|
| {
|
| @@ -254,7 +382,7 @@ void WorkerThread::prepareForShutdown()
|
| workerBackingThread().backingThread().removeTaskObserver(m_microtaskRunner.get());
|
| }
|
|
|
| -void WorkerThread::performShutdown()
|
| +void WorkerThread::performShutdownOnWorkerThread()
|
| {
|
| DCHECK(isCurrentThread());
|
| #if DCHECK_IS_ON
|
| @@ -285,142 +413,28 @@ void WorkerThread::performShutdown()
|
| m_shutdownEvent->signal();
|
| }
|
|
|
| -void WorkerThread::terminate()
|
| -{
|
| - DCHECK(isMainThread());
|
| -
|
| - // Prevent the deadlock between GC and an attempt to terminate a thread.
|
| - SafePointScope safePointScope(BlinkGC::HeapPointersOnStack);
|
| -
|
| - // Protect against this method, initialize() or termination via the global
|
| - // scope racing each other.
|
| - MutexLocker lock(m_threadStateMutex);
|
| -
|
| - // If terminate 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_terminationEvent)
|
| - m_terminationEvent->signal();
|
| -
|
| - // 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;
|
| -
|
| - 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|.
|
| - 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::prepareForShutdown, AllowCrossThreadAccess(this)));
|
| - workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::performShutdown, AllowCrossThreadAccess(this)));
|
| -}
|
| -
|
| -void WorkerThread::terminateAndWait()
|
| -{
|
| - DCHECK(isMainThread());
|
| - terminate();
|
| - m_shutdownEvent->wait();
|
| -}
|
| -
|
| -void WorkerThread::terminateAndWaitForAllWorkers()
|
| -{
|
| - DCHECK(isMainThread());
|
| -
|
| - // Keep this lock to prevent WorkerThread instances from being destroyed.
|
| - MutexLocker lock(threadSetMutex());
|
| - HashSet<WorkerThread*> threads = workerThreads();
|
| - for (WorkerThread* thread : threads)
|
| - thread->terminate();
|
| -
|
| - for (WorkerThread* thread : threads)
|
| - thread->m_shutdownEvent->wait();
|
| -}
|
| -
|
| -WorkerGlobalScope* WorkerThread::workerGlobalScope()
|
| -{
|
| - DCHECK(isCurrentThread());
|
| - return m_workerGlobalScope.get();
|
| -}
|
| -
|
| -bool WorkerThread::terminated()
|
| -{
|
| - MutexLocker lock(m_threadStateMutex);
|
| - return m_terminated;
|
| -}
|
| -
|
| -v8::Isolate* WorkerThread::isolate()
|
| -{
|
| - return workerBackingThread().isolate();
|
| -}
|
| -
|
| -bool WorkerThread::isCurrentThread()
|
| -{
|
| - return m_started && workerBackingThread().backingThread().isCurrentThread();
|
| -}
|
| -
|
| -void WorkerThread::postTask(const WebTraceLocation& location, std::unique_ptr<ExecutionContextTask> task)
|
| -{
|
| - workerBackingThread().backingThread().postTask(location, createWorkerThreadTask(std::move(task), true));
|
| -}
|
| -
|
| -void WorkerThread::runDebuggerTaskDontWait()
|
| +void WorkerThread::performTaskOnWorkerThread(std::unique_ptr<ExecutionContextTask> task, bool isInstrumented)
|
| {
|
| DCHECK(isCurrentThread());
|
| - std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTask(InspectorTaskRunner::DontWaitForTask);
|
| - if (task)
|
| - (*task)();
|
| -}
|
| -
|
| -void WorkerThread::appendDebuggerTask(std::unique_ptr<CrossThreadClosure> task)
|
| -{
|
| {
|
| MutexLocker lock(m_threadStateMutex);
|
| if (m_readyToShutdown)
|
| return;
|
| }
|
| - m_inspectorTaskRunner->appendTask(threadSafeBind(&WorkerThread::runDebuggerTask, AllowCrossThreadAccess(this), passed(std::move(task))));
|
| - {
|
| - MutexLocker lock(m_threadStateMutex);
|
| - if (isolate())
|
| - m_inspectorTaskRunner->interruptAndRunAllTasksDontWait(isolate());
|
| +
|
| + 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.
|
| + if (!globalScope) {
|
| + DCHECK(terminated());
|
| + return;
|
| }
|
| - workerBackingThread().backingThread().postTask(BLINK_FROM_HERE, threadSafeBind(&WorkerThread::runDebuggerTaskDontWait, AllowCrossThreadAccess(this)));
|
| +
|
| + InspectorInstrumentation::AsyncTask asyncTask(globalScope, task.get(), isInstrumented);
|
| + task->performTask(globalScope);
|
| }
|
|
|
| -void WorkerThread::runDebuggerTask(std::unique_ptr<CrossThreadClosure> task)
|
| +void WorkerThread::runDebuggerTaskOnWorkerThread(std::unique_ptr<CrossThreadClosure> task)
|
| {
|
| DCHECK(isCurrentThread());
|
| InspectorTaskRunner::IgnoreInterruptsScope scope(m_inspectorTaskRunner.get());
|
| @@ -441,26 +455,12 @@ void WorkerThread::runDebuggerTask(std::unique_ptr<CrossThreadClosure> task)
|
| }
|
| }
|
|
|
| -void WorkerThread::startRunningDebuggerTasksOnPause()
|
| -{
|
| - m_pausedInDebugger = true;
|
| - ThreadDebugger::idleStarted(isolate());
|
| - std::unique_ptr<CrossThreadClosure> task;
|
| - do {
|
| - {
|
| - SafePointScope safePointScope(BlinkGC::HeapPointersOnStack);
|
| - task = m_inspectorTaskRunner->takeNextTask(InspectorTaskRunner::WaitForTask);
|
| - }
|
| - if (task)
|
| - (*task)();
|
| - // Keep waiting until execution is resumed.
|
| - } while (task && m_pausedInDebugger);
|
| - ThreadDebugger::idleFinished(isolate());
|
| -}
|
| -
|
| -void WorkerThread::stopRunningDebuggerTasksOnPause()
|
| +void WorkerThread::runDebuggerTaskDontWaitOnWorkerThread()
|
| {
|
| - m_pausedInDebugger = false;
|
| + DCHECK(isCurrentThread());
|
| + std::unique_ptr<CrossThreadClosure> task = m_inspectorTaskRunner->takeNextTask(InspectorTaskRunner::DontWaitForTask);
|
| + if (task)
|
| + (*task)();
|
| }
|
|
|
| } // namespace blink
|
|
|