Chromium Code Reviews| Index: third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.cpp |
| diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.cpp b/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..470e213e130701906f6d739ea83ce2185840a87f |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.cpp |
| @@ -0,0 +1,262 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "modules/webaudio/AudioWorkletThread.h" |
| + |
| +#include "bindings/core/v8/Microtask.h" |
| +#include "bindings/core/v8/ScriptSourceCode.h" |
| +#include "bindings/core/v8/V8Binding.h" |
| +#include "bindings/core/v8/V8GCController.h" |
| +#include "bindings/core/v8/V8Initializer.h" |
| +#include "bindings/core/v8/V8PerIsolateData.h" |
| +#include "bindings/core/v8/WorkerOrWorkletScriptController.h" |
| +#include "core/events/MessageEvent.h" |
| +#include "modules/webaudio/AudioDestinationNode.h" |
| +#include "modules/webaudio/AudioWorkletGlobalScope.h" |
| +#include "modules/webaudio/AudioWorkletThreadStartupData.h" |
| +#include "platform/Logging.h" |
| +#include "platform/Task.h" |
| +#include "platform/ThreadSafeFunctional.h" |
| +#include "platform/WebThreadSupportingGC.h" |
| +#include "public/platform/Platform.h" |
| +#include "public/platform/WebThread.h" |
| + |
| +namespace blink { |
| + |
| +namespace { |
| + |
| + class WorkletMicrotaskRunner : public WebThread::TaskObserver { |
| + |
| + public: |
| + explicit WorkletMicrotaskRunner(AudioWorkletThread* audioWorkletThread) |
| + : m_audioWorkletThread(audioWorkletThread) |
| + { |
| + } |
| + |
| + void willProcessTask() final {} |
| + |
| + void didProcessTask() final |
| + { |
| + Microtask::performCheckpoint(m_audioWorkletThread->isolate()); |
| + } |
| + |
| + private: |
| + // Thread owns the microtask runner; reference remains |
| + // valid for the lifetime of this object. |
| + AudioWorkletThread* m_audioWorkletThread; |
| + }; |
| + |
| + void destroyThread(WebThreadSupportingGC* thread) |
| + { |
| + ASSERT(isMainThread()); |
| + // The destructor for |thread| will block until all tasks have completed. |
| + // This guarantees that shutdown will finish before the thread is destroyed. |
| + delete thread; |
| + } |
| + |
| + class AudioWorkletSharedState { |
| + public: |
| + static AudioWorkletSharedState& instance() |
| + { |
| + DEFINE_THREAD_SAFE_STATIC_LOCAL(AudioWorkletSharedState, audioWorkletSharedState, (new AudioWorkletSharedState())); |
| + return audioWorkletSharedState; |
| + } |
| + |
| + WebThreadSupportingGC* audioWorkletThread() |
| + { |
| + MutexLocker lock(m_mutex); |
| + if (!m_thread && isMainThread()) { |
| + ASSERT(!m_workerCount); |
| + WebThread* platformThread = Platform::current()->createThread("offline audio renderer"); |
| + m_thread = WebThreadSupportingGC::createForThread(platformThread); |
| + } |
| + return m_thread.get(); |
| + } |
| + |
| + void initializeBackingThread() |
| + { |
| + MutexLocker lock(m_mutex); |
| + ASSERT(m_thread->isCurrentThread()); |
| + |
| + // ++m_workerCount; |
| + // if (m_workerCount > 1) |
| + // return; |
| + |
| + m_thread->initialize(); |
| + |
| + // Initialize the isolate at the same time. |
| + ASSERT(!m_isolate); |
| + m_isolate = V8PerIsolateData::initialize(); |
| + V8Initializer::initializeWorker(m_isolate); |
| + |
| + // OwnPtr<V8IsolateInterruptor> interruptor = adoptPtr(new V8IsolateInterruptor(m_isolate)); |
| + // ThreadState::current()->addInterruptor(interruptor.release()); |
| + // ThreadState::current()->registerTraceDOMWrappers(m_isolate, V8GCController::traceDOMWrappers); |
| + } |
| + |
| + void shutdownBackingThread() |
| + { |
| + MutexLocker lock(m_mutex); |
| + ASSERT(m_thread->isCurrentThread()); |
| + |
| + // ASSERT(m_workerCount > 0); |
| + // --m_workerCount; |
| + |
| + if (m_workerCount == 0) { |
| + m_thread->shutdown(); |
| + Platform::current()->mainThread()->getWebTaskRunner()->postTask( |
| + BLINK_FROM_HERE, threadSafeBind(destroyThread, AllowCrossThreadAccess(m_thread.leakPtr()))); |
| + } |
| + } |
| + |
| + v8::Isolate* isolate() |
| + { |
| + MutexLocker lock(m_mutex); |
| + ASSERT(m_thread->isCurrentThread()); |
| + ASSERT(m_isolate); |
| + // It is safe to use the existing isolate even if TerminateExecution() has been |
| + // called on it, without calling CancelTerminateExecution(). |
| + return m_isolate; |
| + } |
| + |
| + void willDestroyIsolate() |
| + { |
| + MutexLocker lock(m_mutex); |
| + ASSERT(m_thread->isCurrentThread()); |
| + if (m_workerCount == 1) |
| + V8PerIsolateData::willBeDestroyed(m_isolate); |
| + } |
| + |
| + void destroyIsolate() |
| + { |
| + MutexLocker lock(m_mutex); |
| + if (!m_thread) { |
| + ASSERT(m_workerCount == 0); |
| + V8PerIsolateData::destroy(m_isolate); |
| + m_isolate = nullptr; |
| + } |
| + } |
| + |
| + void terminateV8Execution() |
| + { |
| + MutexLocker lock(m_mutex); |
| + ASSERT(isMainThread()); |
| + if (m_workerCount > 1) |
| + return; |
| + m_isolate->TerminateExecution(); |
| + } |
| + |
| + private: |
| + AudioWorkletSharedState() {} |
| + ~AudioWorkletSharedState() {} |
| + Mutex m_mutex; |
| + OwnPtr<WebThreadSupportingGC> m_thread; |
| + int m_workerCount = 0; |
| + v8::Isolate* m_isolate = nullptr; |
| + }; |
| + |
| +} // namespace |
| + |
| +PassRefPtr<AudioWorkletThread> AudioWorkletThread::create(AudioDestinationHandler& audioDestinationHandler) |
| +{ |
| + ASSERT(isMainThread()); |
| + return adoptRef(new AudioWorkletThread(audioDestinationHandler)); |
| +} |
| + |
| +AudioWorkletThread::AudioWorkletThread(AudioDestinationHandler& audioDestinationHandler) |
| + : m_destinationHandler(audioDestinationHandler) |
| +{ |
| +} |
| + |
| +AudioWorkletThread::~AudioWorkletThread() |
| +{ |
| +} |
| + |
| +bool AudioWorkletThread::isCurrentThread() |
| +{ |
| + return audioRenderThread().isCurrentThread(); |
| +} |
| + |
| +void AudioWorkletThread::initialize(LocalFrame* frame, PassOwnPtr<AudioWorkletThreadStartupData> startupData) |
| +{ |
| + ASSERT(isMainThread()); |
| + |
| + if (m_initialized) |
| + return; |
| + |
| + m_initialized = true; |
| + audioRenderThread().postTask(BLINK_FROM_HERE, threadSafeBind(&AudioWorkletThread::initializeInternal, AllowCrossThreadAccess(this), frame, passed(std::move(startupData)))); |
|
hongchan
2016/05/23 21:48:54
In file included from ../../third_party/WebKit/Sou
|
| +} |
| + |
| +void AudioWorkletThread::initializeInternal(LocalFrame* frame, PassOwnPtr<AudioWorkletThreadStartupData> startupData) |
| +{ |
| + ASSERT(audioRenderThread().isCurrentThread()); |
| + { |
| + MutexLocker lock(m_threadStateMutex); |
| + m_microtaskRunner = adoptPtr(new WorkletMicrotaskRunner(this)); |
| + AudioWorkletSharedState::instance().initializeBackingThread(); |
| + audioRenderThread().addTaskObserver(m_microtaskRunner.get()); |
| + m_isolate = AudioWorkletSharedState::instance().isolate(); |
| + m_audioWorkletGlobalScope = AudioWorkletGlobalScope::create(frame, startupData->m_url, startupData->m_userAgent, startupData->m_securityOrigin.release(), m_isolate); |
| + } |
| +} |
| + |
| +void AudioWorkletThread::shutdown() |
| +{ |
| + ASSERT(isMainThread()); |
| + m_audioWorkletGlobalScope->scriptController()->willScheduleExecutionTermination(); |
| + AudioWorkletSharedState::instance().terminateV8Execution(); |
| + audioRenderThread().postTask(BLINK_FROM_HERE, threadSafeBind(&AudioWorkletThread::shutdownInternal, AllowCrossThreadAccess(this))); |
| +} |
| + |
| +void AudioWorkletThread::evaluate(const String& sourceCode, const KURL& scriptURL) |
| +{ |
| + ASSERT(isMainThread()); |
| + audioRenderThread().postTask(BLINK_FROM_HERE, threadSafeBind(&AudioWorkletThread::evaluateInternal, AllowCrossThreadAccess(this), sourceCode.isolatedCopy(), scriptURL.copy())); |
| +} |
| + |
| +void AudioWorkletThread::evaluateInternal(const String& sourceCode, const KURL& scriptURL) |
| +{ |
| + ASSERT(audioRenderThread().isCurrentThread()); |
| + m_audioWorkletGlobalScope->scriptController()->evaluate(ScriptSourceCode(sourceCode, scriptURL)); |
| +} |
| + |
| +WebThreadSupportingGC& AudioWorkletThread::audioRenderThread() |
| +{ |
| + return *AudioWorkletSharedState::instance().audioWorkletThread(); |
| +} |
| + |
| +void AudioWorkletThread::shutdownInternal() |
| +{ |
| + ASSERT(isCurrentThread()); |
| + { |
| + MutexLocker lock(m_threadStateMutex); |
| + if (m_shutdown) |
| + return; |
| + m_shutdown = true; |
| + } |
| + m_audioWorkletGlobalScope->dispose(); |
| + audioRenderThread().postTask(BLINK_FROM_HERE, threadSafeBind(&AudioWorkletThread::performShutdownTask, AllowCrossThreadAccess(this))); |
| +} |
| + |
| +void AudioWorkletThread::performShutdownTask() |
| +{ |
| +// The below assignment will destroy the context, which will in turn notify messaging proxy. |
| +// We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. |
| +// If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out. |
| +#if !ENABLE(OILPAN) |
| + ASSERT(m_audioWorkletGlobalScope->hasOneRef()); |
| +#endif |
| + m_audioWorkletGlobalScope->notifyContextDestroyed(); |
| + m_audioWorkletGlobalScope = nullptr; |
| + AudioWorkletSharedState::instance().willDestroyIsolate(); |
| + AudioWorkletSharedState::instance().shutdownBackingThread(); |
| + AudioWorkletSharedState::instance().terminateV8Execution(); |
| + m_isolate = nullptr; |
| + m_microtaskRunner = nullptr; |
| + // Notify the proxy that the WorkerGlobalScope has been disposed of. |
| + // This can free this thread object, hence it must not be touched afterwards. |
| + m_destinationHandler.stopRendering(); |
| +} |
| +} // namespace blink |