blob: 716d8638379ce0153942721f1b097ac77fb6a39a [file] [log] [blame]
/*
* Copyright (C) 2010, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "modules/webaudio/DeferredTaskHandler.h"
#include "modules/webaudio/AudioNode.h"
#include "modules/webaudio/AudioNodeOutput.h"
#include "modules/webaudio/OfflineAudioContext.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/WebTaskRunner.h"
#include "public/platform/Platform.h"
namespace blink {
void DeferredTaskHandler::lock() {
// Don't allow regular lock in real-time audio thread.
DCHECK(!IsAudioThread());
context_graph_mutex_.lock();
}
bool DeferredTaskHandler::TryLock() {
// Try to catch cases of using try lock on main thread
// - it should use regular lock.
DCHECK(IsAudioThread());
if (!IsAudioThread()) {
// In release build treat tryLock() as lock() (since above
// DCHECK(isAudioThread) never fires) - this is the best we can do.
lock();
return true;
}
return context_graph_mutex_.TryLock();
}
void DeferredTaskHandler::unlock() {
context_graph_mutex_.unlock();
}
void DeferredTaskHandler::OfflineLock() {
// CHECK is here to make sure to explicitly crash if this is called from
// other than the offline render thread, which is considered as the audio
// thread in OfflineAudioContext.
CHECK(IsAudioThread()) << "DeferredTaskHandler::offlineLock() must be called "
"within the offline audio thread.";
context_graph_mutex_.lock();
}
bool DeferredTaskHandler::IsGraphOwner() {
#if DCHECK_IS_ON()
return context_graph_mutex_.Locked();
#else
// The method is only used inside of DCHECK() so it must be no-op in the
// release build. Returning false so we can catch when it happens.
return false;
#endif
}
void DeferredTaskHandler::AddDeferredBreakConnection(AudioHandler& node) {
DCHECK(IsAudioThread());
deferred_break_connection_list_.push_back(&node);
}
void DeferredTaskHandler::BreakConnections() {
DCHECK(IsAudioThread());
DCHECK(IsGraphOwner());
for (unsigned i = 0; i < deferred_break_connection_list_.size(); ++i)
deferred_break_connection_list_[i]->BreakConnectionWithLock();
deferred_break_connection_list_.clear();
}
void DeferredTaskHandler::MarkSummingJunctionDirty(
AudioSummingJunction* summing_junction) {
DCHECK(IsGraphOwner());
dirty_summing_junctions_.insert(summing_junction);
}
void DeferredTaskHandler::RemoveMarkedSummingJunction(
AudioSummingJunction* summing_junction) {
DCHECK(IsMainThread());
GraphAutoLocker locker(*this);
dirty_summing_junctions_.erase(summing_junction);
}
void DeferredTaskHandler::MarkAudioNodeOutputDirty(AudioNodeOutput* output) {
DCHECK(IsGraphOwner());
DCHECK(IsMainThread());
dirty_audio_node_outputs_.insert(output);
}
void DeferredTaskHandler::RemoveMarkedAudioNodeOutput(AudioNodeOutput* output) {
DCHECK(IsGraphOwner());
DCHECK(IsMainThread());
dirty_audio_node_outputs_.erase(output);
}
void DeferredTaskHandler::HandleDirtyAudioSummingJunctions() {
DCHECK(IsGraphOwner());
for (AudioSummingJunction* junction : dirty_summing_junctions_)
junction->UpdateRenderingState();
dirty_summing_junctions_.clear();
}
void DeferredTaskHandler::HandleDirtyAudioNodeOutputs() {
DCHECK(IsGraphOwner());
HashSet<AudioNodeOutput*> dirty_outputs;
dirty_audio_node_outputs_.swap(dirty_outputs);
// Note: the updating of rendering state may cause output nodes
// further down the chain to be marked as dirty. These will not
// be processed in this render quantum.
for (AudioNodeOutput* output : dirty_outputs)
output->UpdateRenderingState();
}
void DeferredTaskHandler::AddAutomaticPullNode(AudioHandler* node) {
DCHECK(IsGraphOwner());
if (!automatic_pull_nodes_.Contains(node)) {
automatic_pull_nodes_.insert(node);
automatic_pull_nodes_need_updating_ = true;
}
}
void DeferredTaskHandler::RemoveAutomaticPullNode(AudioHandler* node) {
DCHECK(IsGraphOwner());
if (automatic_pull_nodes_.Contains(node)) {
automatic_pull_nodes_.erase(node);
automatic_pull_nodes_need_updating_ = true;
}
}
void DeferredTaskHandler::UpdateAutomaticPullNodes() {
DCHECK(IsGraphOwner());
if (automatic_pull_nodes_need_updating_) {
CopyToVector(automatic_pull_nodes_, rendering_automatic_pull_nodes_);
automatic_pull_nodes_need_updating_ = false;
}
}
void DeferredTaskHandler::ProcessAutomaticPullNodes(size_t frames_to_process) {
DCHECK(IsAudioThread());
for (unsigned i = 0; i < rendering_automatic_pull_nodes_.size(); ++i)
rendering_automatic_pull_nodes_[i]->ProcessIfNecessary(frames_to_process);
}
void DeferredTaskHandler::AddChangedChannelCountMode(AudioHandler* node) {
DCHECK(IsGraphOwner());
DCHECK(IsMainThread());
deferred_count_mode_change_.insert(node);
}
void DeferredTaskHandler::RemoveChangedChannelCountMode(AudioHandler* node) {
DCHECK(IsGraphOwner());
deferred_count_mode_change_.erase(node);
}
void DeferredTaskHandler::AddChangedChannelInterpretation(AudioHandler* node) {
DCHECK(IsGraphOwner());
DCHECK(IsMainThread());
deferred_channel_interpretation_change_.insert(node);
}
void DeferredTaskHandler::RemoveChangedChannelInterpretation(
AudioHandler* node) {
DCHECK(IsGraphOwner());
deferred_channel_interpretation_change_.erase(node);
}
void DeferredTaskHandler::UpdateChangedChannelCountMode() {
DCHECK(IsGraphOwner());
for (AudioHandler* node : deferred_count_mode_change_)
node->UpdateChannelCountMode();
deferred_count_mode_change_.clear();
}
void DeferredTaskHandler::UpdateChangedChannelInterpretation() {
DCHECK(IsGraphOwner());
for (AudioHandler* node : deferred_channel_interpretation_change_)
node->UpdateChannelInterpretation();
deferred_channel_interpretation_change_.clear();
}
DeferredTaskHandler::DeferredTaskHandler()
: automatic_pull_nodes_need_updating_(false), audio_thread_(0) {}
scoped_refptr<DeferredTaskHandler> DeferredTaskHandler::Create() {
return base::AdoptRef(new DeferredTaskHandler());
}
DeferredTaskHandler::~DeferredTaskHandler() {
DCHECK(!automatic_pull_nodes_.size());
if (automatic_pull_nodes_need_updating_)
rendering_automatic_pull_nodes_.resize(automatic_pull_nodes_.size());
DCHECK(!rendering_automatic_pull_nodes_.size());
}
void DeferredTaskHandler::HandleDeferredTasks() {
UpdateChangedChannelCountMode();
UpdateChangedChannelInterpretation();
HandleDirtyAudioSummingJunctions();
HandleDirtyAudioNodeOutputs();
UpdateAutomaticPullNodes();
}
void DeferredTaskHandler::ContextWillBeDestroyed() {
for (auto& handler : rendering_orphan_handlers_)
handler->ClearContext();
for (auto& handler : deletable_orphan_handlers_)
handler->ClearContext();
ClearHandlersToBeDeleted();
// Some handlers might live because of their cross thread tasks.
}
DeferredTaskHandler::GraphAutoLocker::GraphAutoLocker(BaseAudioContext* context)
: handler_(context->GetDeferredTaskHandler()) {
handler_.lock();
}
DeferredTaskHandler::OfflineGraphAutoLocker::OfflineGraphAutoLocker(
OfflineAudioContext* context)
: handler_(context->GetDeferredTaskHandler()) {
handler_.OfflineLock();
}
void DeferredTaskHandler::AddRenderingOrphanHandler(
scoped_refptr<AudioHandler> handler) {
DCHECK(handler);
DCHECK(!rendering_orphan_handlers_.Contains(handler));
rendering_orphan_handlers_.push_back(std::move(handler));
}
void DeferredTaskHandler::RequestToDeleteHandlersOnMainThread() {
DCHECK(IsGraphOwner());
DCHECK(IsAudioThread());
if (rendering_orphan_handlers_.IsEmpty())
return;
deletable_orphan_handlers_.AppendVector(rendering_orphan_handlers_);
rendering_orphan_handlers_.clear();
PostCrossThreadTask(
*Platform::Current()->MainThread()->GetWebTaskRunner(), FROM_HERE,
CrossThreadBind(&DeferredTaskHandler::DeleteHandlersOnMainThread,
scoped_refptr<DeferredTaskHandler>(this)));
}
void DeferredTaskHandler::DeleteHandlersOnMainThread() {
DCHECK(IsMainThread());
GraphAutoLocker locker(*this);
deletable_orphan_handlers_.clear();
}
void DeferredTaskHandler::ClearHandlersToBeDeleted() {
DCHECK(IsMainThread());
GraphAutoLocker locker(*this);
rendering_orphan_handlers_.clear();
deletable_orphan_handlers_.clear();
}
void DeferredTaskHandler::SetAudioThreadToCurrentThread() {
DCHECK(!IsMainThread());
ThreadIdentifier thread = CurrentThread();
ReleaseStore(&audio_thread_, thread);
}
} // namespace blink