Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef476be2 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "AudioFlinger: Update Track / Thread mutex handling" into main

parents 985a8b39 6c498e9c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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; }
+33 −6
Original line number Diff line number Diff line
@@ -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();
@@ -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());
@@ -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) {
@@ -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
@@ -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)
+37 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+18 −5
Original line number Diff line number Diff line
@@ -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);
        }
    }
@@ -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
@@ -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
@@ -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:
@@ -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.