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 m_contextGraphMutex.tryLock(); }
void AudioContext::handlePostRenderTasks() { ASSERT(isAudioThread()); // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. // The worst that can happen 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 mustReleaseLock; if (tryLock(mustReleaseLock)) { // Take care of finishing any derefs where the tryLock() failed previously. handleDeferredFinishDerefs(); // Dynamically clean up nodes which are no longer needed. derefFinishedSourceNodes(); // Finally actually delete. deleteMarkedNodes(); // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. handleDirtyAudioNodeInputs(); handleDirtyAudioNodeOutputs(); if (mustReleaseLock) unlock(); } }
void AudioContext::processAutomaticPullNodes(size_t framesToProcess) { ASSERT(isAudioThread()); for (auto& node : m_renderingAutomaticPullNodes) node->processIfNecessary(framesToProcess); }
void AudioContext::processAutomaticPullNodes(size_t framesToProcess) { ASSERT(isAudioThread()); for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i) m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess); }
size_t AudioContext::currentSampleFrame() const { if (isAudioThread()) return m_destinationNode ? m_destinationNode->audioDestinationHandler().currentSampleFrame() : 0; return m_cachedSampleFrame; }
double AudioContext::currentTime() const { if (isAudioThread()) return m_destinationNode ? m_destinationNode->audioDestinationHandler().currentTime() : 0; return m_cachedSampleFrame / static_cast<double>(sampleRate()); }
void AudioContext::handlePostRenderTasks() { ASSERT(isAudioThread()); // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. // The worst that can happen 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 mustReleaseLock; if (tryLock(mustReleaseLock)) { // Take care of finishing any derefs where the tryLock() failed previously. handleDeferredFinishDerefs(); // Dynamically clean up nodes which are no longer needed. derefFinishedSourceNodes(); // Don't delete in the real-time thread. Let the main thread do it. // Ref-counted objects held by certain AudioNodes may not be thread-safe. scheduleNodeDeletion(); // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs. handleDirtyAudioSummingJunctions(); handleDirtyAudioNodeOutputs(); updateAutomaticPullNodes(); if (mustReleaseLock) unlock(); } }
void AudioContext::handleDeferredFinishDerefs() { ASSERT(isAudioThread() && isGraphOwner()); for (auto& node : m_deferredFinishDerefList) node->finishDeref(AudioNode::RefTypeConnection); m_deferredFinishDerefList.clear(); }
void DeferredTaskHandler::breakConnections() { DCHECK(isAudioThread()); ASSERT(isGraphOwner()); for (unsigned i = 0; i < m_deferredBreakConnectionList.size(); ++i) m_deferredBreakConnectionList[i]->breakConnectionWithLock(); m_deferredBreakConnectionList.clear(); }
void AudioContext::derefFinishedSourceNodes() { ASSERT(isGraphOwner()); ASSERT(isAudioThread() || isAudioThreadFinished()); for (auto& node : m_finishedNodes) derefNode(node); m_finishedNodes.clear(); }
void AudioContext::derefFinishedSourceNodes() { ASSERT(isGraphOwner()); ASSERT(isAudioThread() || isAudioThreadFinished()); for (unsigned i = 0; i < m_finishedNodes.size(); i++) derefNode(m_finishedNodes[i]); m_finishedNodes.clear(); }
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."; m_contextGraphMutex.lock(); }
void DeferredTaskHandler::offlineLock() { // RELEASE_ASSERT 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. RELEASE_ASSERT_WITH_MESSAGE(isAudioThread(), "DeferredTaskHandler::offlineLock() must be called within the offline audio thread."); m_contextGraphMutex.lock(); }
void DeferredTaskHandler::requestToDeleteHandlersOnMainThread() { ASSERT(isGraphOwner()); ASSERT(isAudioThread()); if (m_renderingOrphanHandlers.isEmpty()) return; m_deletableOrphanHandlers.appendVector(m_renderingOrphanHandlers); m_renderingOrphanHandlers.clear(); Platform::current()->mainThread()->taskRunner()->postTask(BLINK_FROM_HERE, threadSafeBind(&DeferredTaskHandler::deleteHandlersOnMainThread, PassRefPtr<DeferredTaskHandler>(this))); }
void AudioContext::handleDeferredFinishDerefs() { ASSERT(isAudioThread() && isGraphOwner()); for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) { AudioNode* node = m_deferredFinishDerefList[i]; node->finishDeref(AudioNode::RefTypeConnection); } m_deferredFinishDerefList.clear(); }
void AudioContext::resolvePromisesForSuspend() { // This runs inside the AudioContext's lock when handling pre-render tasks. ASSERT(isAudioThread()); ASSERT(isGraphOwner()); // Resolve any pending promises created by suspend() if (m_suspendResolvers.size() > 0) Platform::current()->mainThread()->postTask(FROM_HERE, bind(&AudioContext::resolvePromisesForSuspendOnMainThread, this)); }
bool OfflineAudioContext::shouldSuspend() { ASSERT(isAudioThread()); // Note that the GraphLock is required before this check. Since this needs // to run on the audio thread, OfflineGraphAutoLocker must be used. if (m_scheduledSuspends.contains(currentSampleFrame())) return true; return false; }
void OfflineAudioContext::handlePostOfflineRenderTasks() { ASSERT(isAudioThread()); // OfflineGraphAutoLocker here locks the audio graph for the same reason // above in |handlePreOfflineRenderTasks|. OfflineGraphAutoLocker locker(this); deferredTaskHandler().breakConnections(); releaseFinishedSourceNodes(); deferredTaskHandler().handleDeferredTasks(); deferredTaskHandler().requestToDeleteHandlersOnMainThread(); }
void AudioContext::resolvePromisesForResume() { // This runs inside the AudioContext's lock when handling pre-render tasks. ASSERT(isAudioThread()); ASSERT(isGraphOwner()); // Resolve any pending promises created by resume(). Only do this if we haven't already started // resolving these promises. This gets called very often and it takes some time to resolve the // promises in the main thread. if (!m_isResolvingResumePromises && m_resumeResolvers.size() > 0) { m_isResolvingResumePromises = true; Platform::current()->mainThread()->postTask(FROM_HERE, bind(&AudioContext::resolvePromisesForResumeOnMainThread, this)); } }
bool OfflineAudioContext::handlePreOfflineRenderTasks() { ASSERT(isAudioThread()); // OfflineGraphAutoLocker here locks the audio graph for this scope. Note // that this locker does not use tryLock() inside because the timing of // suspension MUST NOT be delayed. OfflineGraphAutoLocker locker(this); deferredTaskHandler().handleDeferredTasks(); handleStoppableSourceNodes(); return shouldSuspend(); }
void AbstractAudioContext::releaseFinishedSourceNodes() { ASSERT(isGraphOwner()); ASSERT(isAudioThread()); for (AudioHandler* handler : m_finishedSourceHandlers) { for (unsigned i = 0; i < m_activeSourceNodes.size(); ++i) { if (handler == &m_activeSourceNodes[i]->handler()) { handler->breakConnection(); m_activeSourceNodes.remove(i); break; } } } m_finishedSourceHandlers.clear(); }
void AudioContext::handlePreRenderTasks() { ASSERT(isAudioThread()); // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes). // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes. bool mustReleaseLock; if (tryLock(mustReleaseLock)) { // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. handleDirtyAudioNodeInputs(); handleDirtyAudioNodeOutputs(); if (mustReleaseLock) unlock(); } }
void AbstractAudioContext::handlePreRenderTasks() { ASSERT(isAudioThread()); // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes). // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes. if (tryLock()) { deferredTaskHandler().handleDeferredTasks(); resolvePromisesForResume(); // Check to see if source nodes can be stopped because the end time has passed. handleStoppableSourceNodes(); unlock(); } }
void AbstractAudioContext::handlePostRenderTasks() { ASSERT(isAudioThread()); // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. // The worst that can happen 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). if (tryLock()) { // Take care of AudioNode tasks where the tryLock() failed previously. deferredTaskHandler().breakConnections(); // Dynamically clean up nodes which are no longer needed. releaseFinishedSourceNodes(); deferredTaskHandler().handleDeferredTasks(); deferredTaskHandler().requestToDeleteHandlersOnMainThread(); unlock(); } }
void AudioContext::addDeferredFinishDeref(AudioNode* node, AudioNode::RefType refType) { ASSERT(isAudioThread()); m_deferredFinishDerefList.append(AudioContext::RefInfo(node, refType)); }
void AudioContext::addDeferredFinishDeref(AudioNode* node) { ASSERT(isAudioThread()); m_deferredFinishDerefList.append(node); }
void DeferredTaskHandler::lock() { // Don't allow regular lock in real-time audio thread. ASSERT(!isAudioThread()); m_contextGraphMutex.lock(); }
void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) { ASSERT(isAudioThread()); m_finishedNodes.append(node); }
void DeferredTaskHandler::addDeferredBreakConnection(AudioHandler& node) { ASSERT(isAudioThread()); m_deferredBreakConnectionList.append(&node); }
void DeferredTaskHandler::processAutomaticPullNodes(size_t framesToProcess) { DCHECK(isAudioThread()); for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i) m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess); }