Chromium Code Reviews| Index: third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp |
| diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp |
| index 6bdb579eca2a0cc64a87bfb3f1e8d371cfcb76f3..e85477d35e90aef77fac6846b08a4e8c32e138c9 100644 |
| --- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp |
| +++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.cpp |
| @@ -95,6 +95,7 @@ BaseAudioContext::BaseAudioContext(Document* document) |
| destination_node_(nullptr), |
| is_cleared_(false), |
| is_resolving_resume_promises_(false), |
| + has_posted_cleanup_task_(false), |
| user_gesture_required_(false), |
| connection_count_(0), |
| deferred_task_handler_(DeferredTaskHandler::Create()), |
| @@ -759,6 +760,10 @@ bool BaseAudioContext::ReleaseFinishedSourceNodes() { |
| DCHECK(IsAudioThread()); |
| bool did_remove = false; |
| for (AudioHandler* handler : finished_source_handlers_) { |
| + // A main thread GC must not be running while the audio |
| + // thread iterates over the |active_source_nodes_| heap object. |
| + ThreadState::GCLockScope gc_lock(ThreadState::MainThreadState()); |
|
haraken
2017/06/08 01:07:55
Add a TODO to remove this somehow. We should refac
|
| + |
| for (AudioNode* node : active_source_nodes_) { |
| if (finished_source_nodes_.Contains(node)) |
| continue; |
| @@ -791,23 +796,10 @@ void BaseAudioContext::ReleaseActiveSourceNodes() { |
| } |
| void BaseAudioContext::HandleStoppableSourceNodes() { |
| + DCHECK(IsAudioThread()); |
| DCHECK(IsGraphOwner()); |
| - // Find AudioBufferSourceNodes to see if we can stop playing them. |
| - for (AudioNode* node : active_source_nodes_) { |
| - // If the AudioNode has been marked as finished and released by |
| - // the audio thread, but not yet removed by the main thread |
| - // (see releaseActiveSourceNodes() above), |node| must not be |
| - // touched as its handler may have been released already. |
| - if (finished_source_nodes_.Contains(node)) |
| - continue; |
| - if (node->Handler().GetNodeType() == |
| - AudioHandler::kNodeTypeAudioBufferSource) { |
| - AudioBufferSourceNode* source_node = |
| - static_cast<AudioBufferSourceNode*>(node); |
| - source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode(); |
| - } |
| - } |
| + ScheduleMainThreadCleanup(); |
| } |
| void BaseAudioContext::HandlePreRenderTasks( |
| @@ -861,22 +853,51 @@ void BaseAudioContext::HandlePostRenderTasks() { |
| RemoveFinishedSourceNodes(did_remove); |
| } |
| -void BaseAudioContext::ResolvePromisesForResumeOnMainThread() { |
| +void BaseAudioContext::PerformCleanupOnMainThread() { |
| DCHECK(IsMainThread()); |
| AutoLocker locker(this); |
| - for (auto& resolver : resume_resolvers_) { |
| - if (context_state_ == kClosed) { |
| - resolver->Reject(DOMException::Create( |
| - kInvalidStateError, "Cannot resume a context that has been closed")); |
| - } else { |
| - SetContextState(kRunning); |
| - resolver->Resolve(); |
| + if (is_resolving_resume_promises_) { |
| + for (auto& resolver : resume_resolvers_) { |
| + if (context_state_ == kClosed) { |
| + resolver->Reject(DOMException::Create( |
| + kInvalidStateError, |
| + "Cannot resume a context that has been closed")); |
| + } else { |
| + SetContextState(kRunning); |
| + resolver->Resolve(); |
| + } |
| } |
| + resume_resolvers_.clear(); |
| + is_resolving_resume_promises_ = false; |
| } |
| - resume_resolvers_.clear(); |
| - is_resolving_resume_promises_ = false; |
| + // Find AudioBufferSourceNodes to see if we can stop playing them. |
| + for (AudioNode* node : active_source_nodes_) { |
| + // If the AudioNode has been marked as finished and released by |
| + // the audio thread, but not yet removed by the main thread |
| + // (see releaseActiveSourceNodes() above), |node| must not be |
| + // touched as its handler may have been released already. |
| + if (finished_source_nodes_.Contains(node)) |
| + continue; |
| + if (node->Handler().GetNodeType() == |
| + AudioHandler::kNodeTypeAudioBufferSource) { |
| + AudioBufferSourceNode* source_node = |
| + static_cast<AudioBufferSourceNode*>(node); |
| + source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode(); |
| + } |
| + } |
| + has_posted_cleanup_task_ = false; |
| +} |
| + |
| +void BaseAudioContext::ScheduleMainThreadCleanup() { |
| + if (has_posted_cleanup_task_) |
| + return; |
| + Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( |
| + BLINK_FROM_HERE, |
| + CrossThreadBind(&BaseAudioContext::PerformCleanupOnMainThread, |
| + WrapCrossThreadPersistent(this))); |
| + has_posted_cleanup_task_ = true; |
| } |
| void BaseAudioContext::ResolvePromisesForResume() { |
| @@ -890,10 +911,7 @@ void BaseAudioContext::ResolvePromisesForResume() { |
| // often and it takes some time to resolve the promises in the main thread. |
| if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) { |
| is_resolving_resume_promises_ = true; |
| - Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( |
| - BLINK_FROM_HERE, |
| - CrossThreadBind(&BaseAudioContext::ResolvePromisesForResumeOnMainThread, |
| - WrapCrossThreadPersistent(this))); |
| + ScheduleMainThreadCleanup(); |
| } |
| } |