Loading services/audioflinger/IAfThread.h +6 −0 Original line number Diff line number Diff line Loading @@ -386,6 +386,12 @@ public: const effect_uuid_t* type, bool suspend, audio_session_t sessionId) REQUIRES(mutex()) = 0; // Wait while the Thread is busy. This is done to ensure that // the Thread is not busy releasing the Tracks, during which the Thread mutex // may be temporarily unlocked. Some Track methods will use this method to // avoid races. virtual void waitWhileThreadBusy_l(audio_utils::unique_lock& ul) REQUIRES(mutex()) = 0; // Dynamic cast to derived interface virtual sp<IAfDirectOutputThread> asIAfDirectOutputThread() { return nullptr; } virtual sp<IAfDuplicatingThread> asIAfDuplicatingThread() { return nullptr; } Loading services/audioflinger/Threads.cpp +33 −6 Original line number Diff line number Diff line Loading @@ -2849,6 +2849,8 @@ status_t PlaybackThread::addTrack_l(const sp<IAfTrack>& track) // effectively get the latency it requested. if (track->isExternalTrack()) { IAfTrackBase::track_state state = track->state(); // Because the track is not on the ActiveTracks, // at this point, only the TrackHandle will be adding the track. mutex().unlock(); status = AudioSystem::startOutput(track->portId()); mutex().lock(); Loading Loading @@ -2929,7 +2931,12 @@ status_t PlaybackThread::addTrack_l(const sp<IAfTrack>& track) track->setResetDone(false); track->resetPresentationComplete(); // Do not release the ThreadBase mutex after the track is added to mActiveTracks unless // all key changes are complete. It is possible that the threadLoop will begin // processing the added track immediately after the ThreadBase mutex is released. mActiveTracks.add(track); if (chain != 0) { ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId()); Loading Loading @@ -4704,8 +4711,12 @@ void PlaybackThread::collectTimestamps_l() void PlaybackThread::removeTracks_l(const Vector<sp<IAfTrack>>& tracksToRemove) NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex() { if (tracksToRemove.empty()) return; // Block all incoming TrackHandle requests until we are finished with the release. setThreadBusy_l(true); for (const auto& track : tracksToRemove) { mActiveTracks.remove(track); ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId()); sp<IAfEffectChain> chain = getEffectChain_l(track->sessionId()); if (chain != 0) { Loading @@ -4713,17 +4724,16 @@ NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex() __func__, track->id(), chain.get(), track->sessionId()); chain->decActiveTrackCnt(); } // If an external client track, inform APM we're no longer active, and remove if needed. // We do this under lock so that the state is consistent if the Track is destroyed. // Since the track is active, we do it here instead of TrackBase::destroy(). if (track->isExternalTrack()) { mutex().unlock(); AudioSystem::stopOutput(track->portId()); if (track->isTerminated()) { AudioSystem::releaseOutput(track->portId()); } } if (track->isTerminated()) { // remove from our tracks vector removeTrack_l(track); mutex().lock(); } if (mHapticChannelCount > 0 && ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE Loading @@ -4740,7 +4750,24 @@ NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex() chain->setHapticIntensity_l(track->id(), os::HapticScale::MUTE); } } // Under lock, the track is removed from the active tracks list. // // Once the track is no longer active, the TrackHandle may directly // modify it as the threadLoop() is no longer responsible for its maintenance. // Do not modify the track from threadLoop after the mutex is unlocked // if it is not active. mActiveTracks.remove(track); if (track->isTerminated()) { // remove from our tracks vector removeTrack_l(track); } } // Allow incoming TrackHandle requests. We still hold the mutex, // so pending TrackHandle requests will occur after we unlock it. setThreadBusy_l(false); } status_t PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp) Loading services/audioflinger/Threads.h +37 −1 Original line number Diff line number Diff line Loading @@ -599,6 +599,35 @@ protected: // check if some effects must be suspended when an effect chain is added void checkSuspendOnAddEffectChain_l(const sp<IAfEffectChain>& chain) REQUIRES(mutex()); /** * waitWhileThreadBusy_l() serves as a mutex gate, which does not allow * progress beyond the method while the PlaybackThread is busy (see setThreadBusy_l()). * During the wait, the ThreadBase_Mutex is temporarily unlocked. * * This implementation uses a condition variable. Alternative methods to gate * the thread may use a second mutex (i.e. entry based on scoped_lock(mutex, gating_mutex)), * but those have less flexibility and more lock order issues. * * Current usage by Track::destroy(), Track::start(), Track::stop(), Track::pause(), * and Track::flush() block this way, and the primary caller is through TrackHandle * with no other mutexes held. * * Special tracks like PatchTrack and OutputTrack may also hold the another thread's * ThreadBase_Mutex during this time. No other mutex is held. */ void waitWhileThreadBusy_l(audio_utils::unique_lock& ul) final REQUIRES(mutex()) { // the wait returns immediately if the predicate is satisfied. mThreadBusyCv.wait(ul, [&]{ return mThreadBusy == false;}); } void setThreadBusy_l(bool busy) REQUIRES(mutex()) { if (busy == mThreadBusy) return; mThreadBusy = busy; if (busy == true) return; // no need to wake threads if we become busy. mThreadBusyCv.notify_all(); } // sends the metadata of the active tracks to the HAL struct MetadataUpdate { std::vector<playback_track_metadata_v7_t> playbackMetadataUpdate; Loading Loading @@ -641,6 +670,13 @@ protected: ThreadMetrics mThreadMetrics; const bool mIsOut; // mThreadBusy is checked under the ThreadBase_Mutex to ensure that // TrackHandle operations do not proceed while the ThreadBase is busy // with the track. mThreadBusy is only true if the track is active. // bool mThreadBusy = false; // GUARDED_BY(ThreadBase_Mutex) but read in lambda. audio_utils::condition_variable mThreadBusyCv; // updated by PlaybackThread::readOutputParameters_l() or // RecordThread::readInputParameters_l() uint32_t mSampleRate; Loading services/audioflinger/Tracks.cpp +18 −5 Original line number Diff line number Diff line Loading @@ -890,12 +890,17 @@ void Track::destroy() bool wasActive = false; const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); auto* const playbackThread = thread->asIAfPlaybackThread().get(); wasActive = playbackThread->destroyTrack_l(this); forEachTeePatchTrack_l([](const auto& patchTrack) { patchTrack->destroy(); }); } if (isExternalTrack() && !wasActive) { // If the track is not active, the TrackHandle is responsible for // releasing the port id, not the ThreadBase::threadLoop(). // At this point, there is no concurrency issue as the track is going away. AudioSystem::releaseOutput(mPortId); } } Loading Loading @@ -1181,7 +1186,9 @@ status_t Track::start(AudioSystem::sync_event_t event __unused, return PERMISSION_DENIED; } } audio_utils::lock_guard _lth(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); track_state state = mState; // here the track could be either new, or restarted // in both cases "unstop" the track Loading Loading @@ -1306,7 +1313,9 @@ void Track::stop() ALOGV("%s(%d): calling pid %d", __func__, mId, IPCThreadState::self()->getCallingPid()); const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); track_state state = mState; if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) { // If the track is not active (PAUSED and buffers full), flush buffers Loading Loading @@ -1341,7 +1350,9 @@ void Track::pause() ALOGV("%s(%d): calling pid %d", __func__, mId, IPCThreadState::self()->getCallingPid()); const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); auto* const playbackThread = thread->asIAfPlaybackThread().get(); switch (mState) { case STOPPING_1: Loading Loading @@ -1378,7 +1389,9 @@ void Track::flush() ALOGV("%s(%d)", __func__, mId); const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); auto* const playbackThread = thread->asIAfPlaybackThread().get(); // Flush the ring buffer now if the track is not active in the PlaybackThread. Loading Loading
services/audioflinger/IAfThread.h +6 −0 Original line number Diff line number Diff line Loading @@ -386,6 +386,12 @@ public: const effect_uuid_t* type, bool suspend, audio_session_t sessionId) REQUIRES(mutex()) = 0; // Wait while the Thread is busy. This is done to ensure that // the Thread is not busy releasing the Tracks, during which the Thread mutex // may be temporarily unlocked. Some Track methods will use this method to // avoid races. virtual void waitWhileThreadBusy_l(audio_utils::unique_lock& ul) REQUIRES(mutex()) = 0; // Dynamic cast to derived interface virtual sp<IAfDirectOutputThread> asIAfDirectOutputThread() { return nullptr; } virtual sp<IAfDuplicatingThread> asIAfDuplicatingThread() { return nullptr; } Loading
services/audioflinger/Threads.cpp +33 −6 Original line number Diff line number Diff line Loading @@ -2849,6 +2849,8 @@ status_t PlaybackThread::addTrack_l(const sp<IAfTrack>& track) // effectively get the latency it requested. if (track->isExternalTrack()) { IAfTrackBase::track_state state = track->state(); // Because the track is not on the ActiveTracks, // at this point, only the TrackHandle will be adding the track. mutex().unlock(); status = AudioSystem::startOutput(track->portId()); mutex().lock(); Loading Loading @@ -2929,7 +2931,12 @@ status_t PlaybackThread::addTrack_l(const sp<IAfTrack>& track) track->setResetDone(false); track->resetPresentationComplete(); // Do not release the ThreadBase mutex after the track is added to mActiveTracks unless // all key changes are complete. It is possible that the threadLoop will begin // processing the added track immediately after the ThreadBase mutex is released. mActiveTracks.add(track); if (chain != 0) { ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId()); Loading Loading @@ -4704,8 +4711,12 @@ void PlaybackThread::collectTimestamps_l() void PlaybackThread::removeTracks_l(const Vector<sp<IAfTrack>>& tracksToRemove) NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex() { if (tracksToRemove.empty()) return; // Block all incoming TrackHandle requests until we are finished with the release. setThreadBusy_l(true); for (const auto& track : tracksToRemove) { mActiveTracks.remove(track); ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId()); sp<IAfEffectChain> chain = getEffectChain_l(track->sessionId()); if (chain != 0) { Loading @@ -4713,17 +4724,16 @@ NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex() __func__, track->id(), chain.get(), track->sessionId()); chain->decActiveTrackCnt(); } // If an external client track, inform APM we're no longer active, and remove if needed. // We do this under lock so that the state is consistent if the Track is destroyed. // Since the track is active, we do it here instead of TrackBase::destroy(). if (track->isExternalTrack()) { mutex().unlock(); AudioSystem::stopOutput(track->portId()); if (track->isTerminated()) { AudioSystem::releaseOutput(track->portId()); } } if (track->isTerminated()) { // remove from our tracks vector removeTrack_l(track); mutex().lock(); } if (mHapticChannelCount > 0 && ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE Loading @@ -4740,7 +4750,24 @@ NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex() chain->setHapticIntensity_l(track->id(), os::HapticScale::MUTE); } } // Under lock, the track is removed from the active tracks list. // // Once the track is no longer active, the TrackHandle may directly // modify it as the threadLoop() is no longer responsible for its maintenance. // Do not modify the track from threadLoop after the mutex is unlocked // if it is not active. mActiveTracks.remove(track); if (track->isTerminated()) { // remove from our tracks vector removeTrack_l(track); } } // Allow incoming TrackHandle requests. We still hold the mutex, // so pending TrackHandle requests will occur after we unlock it. setThreadBusy_l(false); } status_t PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp) Loading
services/audioflinger/Threads.h +37 −1 Original line number Diff line number Diff line Loading @@ -599,6 +599,35 @@ protected: // check if some effects must be suspended when an effect chain is added void checkSuspendOnAddEffectChain_l(const sp<IAfEffectChain>& chain) REQUIRES(mutex()); /** * waitWhileThreadBusy_l() serves as a mutex gate, which does not allow * progress beyond the method while the PlaybackThread is busy (see setThreadBusy_l()). * During the wait, the ThreadBase_Mutex is temporarily unlocked. * * This implementation uses a condition variable. Alternative methods to gate * the thread may use a second mutex (i.e. entry based on scoped_lock(mutex, gating_mutex)), * but those have less flexibility and more lock order issues. * * Current usage by Track::destroy(), Track::start(), Track::stop(), Track::pause(), * and Track::flush() block this way, and the primary caller is through TrackHandle * with no other mutexes held. * * Special tracks like PatchTrack and OutputTrack may also hold the another thread's * ThreadBase_Mutex during this time. No other mutex is held. */ void waitWhileThreadBusy_l(audio_utils::unique_lock& ul) final REQUIRES(mutex()) { // the wait returns immediately if the predicate is satisfied. mThreadBusyCv.wait(ul, [&]{ return mThreadBusy == false;}); } void setThreadBusy_l(bool busy) REQUIRES(mutex()) { if (busy == mThreadBusy) return; mThreadBusy = busy; if (busy == true) return; // no need to wake threads if we become busy. mThreadBusyCv.notify_all(); } // sends the metadata of the active tracks to the HAL struct MetadataUpdate { std::vector<playback_track_metadata_v7_t> playbackMetadataUpdate; Loading Loading @@ -641,6 +670,13 @@ protected: ThreadMetrics mThreadMetrics; const bool mIsOut; // mThreadBusy is checked under the ThreadBase_Mutex to ensure that // TrackHandle operations do not proceed while the ThreadBase is busy // with the track. mThreadBusy is only true if the track is active. // bool mThreadBusy = false; // GUARDED_BY(ThreadBase_Mutex) but read in lambda. audio_utils::condition_variable mThreadBusyCv; // updated by PlaybackThread::readOutputParameters_l() or // RecordThread::readInputParameters_l() uint32_t mSampleRate; Loading
services/audioflinger/Tracks.cpp +18 −5 Original line number Diff line number Diff line Loading @@ -890,12 +890,17 @@ void Track::destroy() bool wasActive = false; const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); auto* const playbackThread = thread->asIAfPlaybackThread().get(); wasActive = playbackThread->destroyTrack_l(this); forEachTeePatchTrack_l([](const auto& patchTrack) { patchTrack->destroy(); }); } if (isExternalTrack() && !wasActive) { // If the track is not active, the TrackHandle is responsible for // releasing the port id, not the ThreadBase::threadLoop(). // At this point, there is no concurrency issue as the track is going away. AudioSystem::releaseOutput(mPortId); } } Loading Loading @@ -1181,7 +1186,9 @@ status_t Track::start(AudioSystem::sync_event_t event __unused, return PERMISSION_DENIED; } } audio_utils::lock_guard _lth(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); track_state state = mState; // here the track could be either new, or restarted // in both cases "unstop" the track Loading Loading @@ -1306,7 +1313,9 @@ void Track::stop() ALOGV("%s(%d): calling pid %d", __func__, mId, IPCThreadState::self()->getCallingPid()); const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); track_state state = mState; if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) { // If the track is not active (PAUSED and buffers full), flush buffers Loading Loading @@ -1341,7 +1350,9 @@ void Track::pause() ALOGV("%s(%d): calling pid %d", __func__, mId, IPCThreadState::self()->getCallingPid()); const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); auto* const playbackThread = thread->asIAfPlaybackThread().get(); switch (mState) { case STOPPING_1: Loading Loading @@ -1378,7 +1389,9 @@ void Track::flush() ALOGV("%s(%d)", __func__, mId); const sp<IAfThreadBase> thread = mThread.promote(); if (thread != 0) { audio_utils::lock_guard _l(thread->mutex()); audio_utils::unique_lock ul(thread->mutex()); thread->waitWhileThreadBusy_l(ul); auto* const playbackThread = thread->asIAfPlaybackThread().get(); // Flush the ring buffer now if the track is not active in the PlaybackThread. Loading