| 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..69b31328a0c7c67d20094956baf00bf5d6894250 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()),
|
| @@ -149,7 +150,6 @@ BaseAudioContext::~BaseAudioContext() {
|
| // be in the destructor if there are still AudioNodes around.
|
| DCHECK(!IsDestinationInitialized());
|
| DCHECK(!active_source_nodes_.size());
|
| - DCHECK(!finished_source_handlers_.size());
|
| DCHECK(!is_resolving_resume_promises_);
|
| DCHECK(!resume_resolvers_.size());
|
| DCHECK(!autoplay_status_.has_value());
|
| @@ -700,34 +700,10 @@ void BaseAudioContext::NotifyStateChange() {
|
| void BaseAudioContext::NotifySourceNodeFinishedProcessing(
|
| AudioHandler* handler) {
|
| DCHECK(IsAudioThread());
|
| + MutexLocker lock(finished_source_handlers_mutex_);
|
| finished_source_handlers_.push_back(handler);
|
| }
|
|
|
| -void BaseAudioContext::RemoveFinishedSourceNodes(bool needs_removal) {
|
| - DCHECK(IsAudioThread());
|
| -
|
| - if (needs_removal) {
|
| - Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask(
|
| - BLINK_FROM_HERE,
|
| - CrossThreadBind(
|
| - &BaseAudioContext::RemoveFinishedSourceNodesOnMainThread,
|
| - WrapCrossThreadPersistent(this)));
|
| - }
|
| -}
|
| -
|
| -void BaseAudioContext::RemoveFinishedSourceNodesOnMainThread() {
|
| - DCHECK(IsMainThread());
|
| - AutoLocker locker(this);
|
| - // Quadratic worst case, but sizes of both vectors are considered
|
| - // manageable, especially |m_finishedSourceNodes| is likely to be short.
|
| - for (AudioNode* node : finished_source_nodes_) {
|
| - size_t i = active_source_nodes_.Find(node);
|
| - if (i != kNotFound)
|
| - active_source_nodes_.erase(i);
|
| - }
|
| - finished_source_nodes_.clear();
|
| -}
|
| -
|
| Document* BaseAudioContext::GetDocument() const {
|
| return ToDocument(GetExecutionContext());
|
| }
|
| @@ -754,26 +730,6 @@ bool BaseAudioContext::AreAutoplayRequirementsFulfilled() const {
|
| return false;
|
| }
|
|
|
| -bool BaseAudioContext::ReleaseFinishedSourceNodes() {
|
| - DCHECK(IsGraphOwner());
|
| - DCHECK(IsAudioThread());
|
| - bool did_remove = false;
|
| - for (AudioHandler* handler : finished_source_handlers_) {
|
| - for (AudioNode* node : active_source_nodes_) {
|
| - if (finished_source_nodes_.Contains(node))
|
| - continue;
|
| - if (handler == &node->Handler()) {
|
| - handler->BreakConnection();
|
| - finished_source_nodes_.insert(node);
|
| - did_remove = true;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - finished_source_handlers_.clear();
|
| - return did_remove;
|
| -}
|
| -
|
| void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) {
|
| DCHECK(IsMainThread());
|
| AutoLocker locker(this);
|
| @@ -791,23 +747,11 @@ 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();
|
| - }
|
| - }
|
| + if (active_source_nodes_.size())
|
| + ScheduleMainThreadCleanup();
|
| }
|
|
|
| void BaseAudioContext::HandlePreRenderTasks(
|
| @@ -844,39 +788,88 @@ void BaseAudioContext::HandlePostRenderTasks() {
|
| // is that there will be some nodes which will take slightly longer than usual
|
| // to be deleted or removed from the render graph (in which case they'll
|
| // render silence).
|
| - bool did_remove = false;
|
| if (TryLock()) {
|
| // Take care of AudioNode tasks where the tryLock() failed previously.
|
| GetDeferredTaskHandler().BreakConnections();
|
|
|
| - // Dynamically clean up nodes which are no longer needed.
|
| - did_remove = ReleaseFinishedSourceNodes();
|
| -
|
| GetDeferredTaskHandler().HandleDeferredTasks();
|
| GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread();
|
|
|
| unlock();
|
| }
|
| -
|
| - 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;
|
| + if (active_source_nodes_.size()) {
|
| + // Find AudioBufferSourceNodes to see if we can stop playing them.
|
| + for (AudioNode* node : active_source_nodes_) {
|
| + if (node->Handler().GetNodeType() ==
|
| + AudioHandler::kNodeTypeAudioBufferSource) {
|
| + AudioBufferSourceNode* source_node =
|
| + static_cast<AudioBufferSourceNode*>(node);
|
| + source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode();
|
| + }
|
| + }
|
| +
|
| + Vector<AudioHandler*> finished_handlers;
|
| + {
|
| + MutexLocker lock(finished_source_handlers_mutex_);
|
| + finished_source_handlers_.swap(finished_handlers);
|
| + }
|
| + // Break the connection and release active nodes that have finished
|
| + // playing.
|
| + unsigned remove_count = 0;
|
| + Vector<bool> removables;
|
| + removables.resize(active_source_nodes_.size());
|
| + for (AudioHandler* handler : finished_handlers) {
|
| + for (unsigned i = 0; i < active_source_nodes_.size(); ++i) {
|
| + if (handler == &active_source_nodes_[i]->Handler()) {
|
| + handler->BreakConnection();
|
| + removables[i] = true;
|
| + remove_count++;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Copy over the surviving active nodes.
|
| + HeapVector<Member<AudioNode>> actives;
|
| + actives.ReserveInitialCapacity(active_source_nodes_.size() - remove_count);
|
| + for (unsigned i = 0; i < removables.size(); ++i) {
|
| + if (!removables[i])
|
| + actives.push_back(active_source_nodes_[i]);
|
| + }
|
| + active_source_nodes_.swap(actives);
|
| + }
|
| + 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 +883,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();
|
| }
|
| }
|
|
|
|
|