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

Commit 3da128d7 authored by Robert Wu's avatar Robert Wu
Browse files

AAudio: Use futex for waitForStateChange

WaitForStateChange currently waits in twenty millisecond intervals
for state changes. This is clearly inefficient and if apps use this
consistently, much extra latency is added. This CL adds futexes so
the callback fires almost immediately after a state change.

This CL also changes updateStateMachine so updates from non-callbacks
do not happen if a callback thread is running.

Bug: 70722092
Test: atest AAudioTests
Test: Oboe automated tests (./run_tests.sh)
Test: OboeTester Plug Latency Test
Change-Id: I186dc9ddac7f3897d82a5ed0043a5f45f1c19fe3
parent 059aa2d7
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -605,13 +605,6 @@ aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t /*clockId*/,
    return AAUDIO_ERROR_INVALID_STATE;
}

aaudio_result_t AudioStreamInternal::updateStateMachine() {
    if (isDataCallbackActive()) {
        return AAUDIO_OK; // state is getting updated by the callback thread read/write call
    }
    return processCommands();
}

void AudioStreamInternal::logTimestamp(AAudioServiceMessage &command) {
    static int64_t oldPosition = 0;
    static int64_t oldTime = 0;
+1 −3
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ public:
                                       int64_t *framePosition,
                                       int64_t *timeNanoseconds) override;

    virtual aaudio_result_t updateStateMachine() override;
    virtual aaudio_result_t processCommands() override;

    aaudio_result_t open(const AudioStreamBuilder &builder) override;

@@ -110,8 +110,6 @@ protected:

    aaudio_result_t drainTimestampsFromService();

    aaudio_result_t processCommands();

    aaudio_result_t stopCallback_l();

    virtual void prepareBuffersForStart() {}
+20 −9
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@
#include <atomic>
#include <stdint.h>

#include <linux/futex.h>
#include <media/MediaMetricsItem.h>
#include <sys/syscall.h>

#include <aaudio/AAudio.h>

@@ -362,34 +364,37 @@ void AudioStream::close_l() {
}

void AudioStream::setState(aaudio_stream_state_t state) {
    ALOGD("%s(s#%d) from %d to %d", __func__, getId(), mState, state);
    if (state == mState) {
    aaudio_stream_state_t oldState = mState.load();
    ALOGD("%s(s#%d) from %d to %d", __func__, getId(), oldState, state);
    if (state == oldState) {
        return; // no change
    }
    // Track transition to DISCONNECTED state.
    if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
        android::mediametrics::LogItem(mMetricsId)
                .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
                .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
                .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(oldState))
                .record();
    }
    // CLOSED is a final state
    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
    if (oldState == AAUDIO_STREAM_STATE_CLOSED) {
        ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);

    // Once CLOSING, we can only move to CLOSED state.
    } else if (mState == AAUDIO_STREAM_STATE_CLOSING
    } else if (oldState == AAUDIO_STREAM_STATE_CLOSING
               && state != AAUDIO_STREAM_STATE_CLOSED) {
        ALOGW("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);

    // Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
    } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
    } else if (oldState == AAUDIO_STREAM_STATE_DISCONNECTED
               && !(state == AAUDIO_STREAM_STATE_CLOSING
                   || state == AAUDIO_STREAM_STATE_CLOSED)) {
        ALOGW("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);

    } else {
        mState = state;
        mState.store(state);
        // Wake up a wakeForStateChange thread if it exists.
        syscall(SYS_futex, &mState, FUTEX_WAKE_PRIVATE, INT_MAX, NULL, NULL, 0);
    }
}

@@ -408,9 +413,15 @@ aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentSta
        if (durationNanos > timeoutNanoseconds) {
            durationNanos = timeoutNanoseconds;
        }
        AudioClock::sleepForNanos(durationNanos);
        timeoutNanoseconds -= durationNanos;
        struct timespec time;
        time.tv_sec = durationNanos / AAUDIO_NANOS_PER_SECOND;
        // Add the fractional nanoseconds.
        time.tv_nsec = durationNanos - (time.tv_sec * AAUDIO_NANOS_PER_SECOND);

        // Sleep for durationNanos. If mState changes from the callback
        // thread, this thread will wake up earlier.
        syscall(SYS_futex, &mState, FUTEX_WAIT_PRIVATE, currentState, &time, NULL, 0);
        timeoutNanoseconds -= durationNanos;
        aaudio_result_t result = updateStateMachine();
        if (result != AAUDIO_OK) {
            return result;
+13 −5
Original line number Diff line number Diff line
@@ -100,10 +100,17 @@ public:
                                       int64_t *timeNanoseconds) = 0;

    /**
     * Update state machine.()
     * @return
     * Update state machine.
     * @return result of the operation.
     */
    virtual aaudio_result_t updateStateMachine() = 0;
    aaudio_result_t updateStateMachine() {
        if (isDataCallbackActive()) {
            return AAUDIO_OK; // state is getting updated by the callback thread read/write call
        }
        return processCommands();
    };

    virtual aaudio_result_t processCommands() = 0;

    // =========== End ABSTRACT methods ===========================

@@ -184,7 +191,7 @@ public:
    // ============== Queries ===========================

    aaudio_stream_state_t getState() const {
        return mState;
        return mState.load();
    }

    virtual int32_t getBufferSize() const {
@@ -674,6 +681,8 @@ private:

    const android::sp<MyPlayerBase>   mPlayerBase;

    std::atomic<aaudio_stream_state_t>          mState{AAUDIO_STREAM_STATE_UNINITIALIZED};

    // These do not change after open().
    int32_t                     mSamplesPerFrame = AAUDIO_UNSPECIFIED;
    aaudio_channel_mask_t       mChannelMask = AAUDIO_UNSPECIFIED;
@@ -682,7 +691,6 @@ private:
    aaudio_sharing_mode_t       mSharingMode = AAUDIO_SHARING_MODE_SHARED;
    bool                        mSharingModeMatchRequired = false; // must match sharing mode requested
    audio_format_t              mFormat = AUDIO_FORMAT_DEFAULT;
    aaudio_stream_state_t       mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
    aaudio_performance_mode_t   mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
    int32_t                     mFramesPerBurst = 0;
    int32_t                     mBufferCapacity = 0;
+2 −2
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ size_t AudioStreamLegacy::onMoreData(const android::AudioTrack::Buffer& buffer)
            mCallbackEnabled.store(false);
        }

        if (updateStateMachine() != AAUDIO_OK) {
        if (processCommands() != AAUDIO_OK) {
            forceDisconnect();
            mCallbackEnabled.store(false);
        }
@@ -192,7 +192,7 @@ size_t AudioStreamLegacy::onMoreData(const android::AudioRecord::Buffer& buffer)
            mCallbackEnabled.store(false);
        }

        if (updateStateMachine() != AAUDIO_OK) {
        if (processCommands() != AAUDIO_OK) {
            forceDisconnect();
            mCallbackEnabled.store(false);
        }
Loading