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

Commit ea14359e authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 7295815 from 99594717 to sc-release

Change-Id: If0afd326d64f7a81c41ec538692daeacb2a41e9c
parents c14b9443 99594717
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -308,11 +308,19 @@ aaudio_result_t AudioStreamRecord::release_l() {
}

void AudioStreamRecord::close_l() {
    // The callbacks are normally joined in the AudioRecord destructor.
    // But if another object has a reference to the AudioRecord then
    // it will not get deleted here.
    // So we should join callbacks explicitly before returning.
    // Unlock around the join to avoid deadlocks if the callback tries to lock.
    // This can happen if the callback returns AAUDIO_CALLBACK_RESULT_STOP
    mStreamLock.unlock();
    mAudioRecord->stopAndJoinCallbacks();
    mStreamLock.lock();

    mAudioRecord.clear();
    // Do not close mFixedBlockWriter because a data callback
    // thread might still be running if someone else has a reference
    // to mAudioRecord.
    // It has a unique_ptr to its buffer so it will clean up by itself.
    // Do not close mFixedBlockReader. It has a unique_ptr to its buffer
    // so it will clean up by itself.
    AudioStream::close_l();
}

+11 −5
Original line number Diff line number Diff line
@@ -259,12 +259,18 @@ aaudio_result_t AudioStreamTrack::release_l() {
}

void AudioStreamTrack::close_l() {
    // Stop callbacks before deleting mFixedBlockReader memory.
    // The callbacks are normally joined in the AudioTrack destructor.
    // But if another object has a reference to the AudioTrack then
    // it will not get deleted here.
    // So we should join callbacks explicitly before returning.
    // Unlock around the join to avoid deadlocks if the callback tries to lock.
    // This can happen if the callback returns AAUDIO_CALLBACK_RESULT_STOP
    mStreamLock.unlock();
    mAudioTrack->stopAndJoinCallbacks();
    mStreamLock.lock();
    mAudioTrack.clear();
    // Do not close mFixedBlockReader because a data callback
    // thread might still be running if someone else has a reference
    // to mAudioRecord.
    // It has a unique_ptr to its buffer so it will clean up by itself.
    // Do not close mFixedBlockReader. It has a unique_ptr to its buffer
    // so it will clean up by itself.
    AudioStream::close_l();
}

+2 −2
Original line number Diff line number Diff line
@@ -209,9 +209,9 @@ cc_test {
}

cc_test {
    name: "test_stop_hang",
    name: "test_callback_race",
    defaults: ["libaaudio_tests_defaults"],
    srcs: ["test_stop_hang.cpp"],
    srcs: ["test_callback_race.cpp"],
    shared_libs: [
        "libaaudio",
        "libbinder",
+209 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Test whether the callback is joined before the close finishes.
 *
 * Start a stream with a callback.
 * The callback just sleeps for a long time.
 * While the callback is sleeping, close() the stream from the main thread.
 * Then check to make sure the callback was joined before the close() returns.
 *
 * This can hang if there are deadlocks. So make sure you get a PASSED result.
 */

#include <atomic>
#include <stdio.h>
#include <unistd.h>

#include <gtest/gtest.h>

#include <aaudio/AAudio.h>

// Sleep long enough that the foreground has a change to call close.
static constexpr int kCallbackSleepMicros = 600 * 1000;

class AudioEngine {
public:

    // Check for a crash or late callback if we close without stopping.
    void checkCloseJoins(aaudio_direction_t direction,
                             aaudio_performance_mode_t perfMode,
                             aaudio_data_callback_result_t callbackResult) {

        // Make printf print immediately so that debug info is not stuck
        // in a buffer if we hang or crash.
        setvbuf(stdout, nullptr, _IONBF, (size_t) 0);

        mCallbackResult = callbackResult;
        startStreamForStall(direction, perfMode);
        // When the callback starts it will go to sleep.
        waitForCallbackToStart();

        printf("call AAudioStream_close()\n");
        ASSERT_FALSE(mCallbackFinished); // Still sleeping?
        aaudio_result_t result = AAudioStream_close(mStream); // May hang here!
        ASSERT_TRUE(mCallbackFinished);
        ASSERT_EQ(AAUDIO_OK, result);
        printf("AAudioStream_close() returned %d\n", result);

        ASSERT_EQ(AAUDIO_OK, mError.load());
        // Did calling stop() from callback fail? It should have.
        ASSERT_NE(AAUDIO_OK, mStopResult.load());
    }

private:
    void startStreamForStall(aaudio_direction_t direction,
                             aaudio_performance_mode_t perfMode) {
        AAudioStreamBuilder* builder = nullptr;
        aaudio_result_t result = AAUDIO_OK;

        // Use an AAudioStreamBuilder to contain requested parameters.
        result = AAudio_createStreamBuilder(&builder);
        ASSERT_EQ(AAUDIO_OK, result);

        // Request stream properties.
        AAudioStreamBuilder_setDirection(builder, direction);
        AAudioStreamBuilder_setPerformanceMode(builder, perfMode);
        AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
        AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);

        // Create an AAudioStream using the Builder.
        result = AAudioStreamBuilder_openStream(builder, &mStream);
        AAudioStreamBuilder_delete(builder);
        ASSERT_EQ(AAUDIO_OK, result);

        // Check to see what kind of stream we actually got.
        int32_t deviceId = AAudioStream_getDeviceId(mStream);
        aaudio_performance_mode_t
            actualPerfMode = AAudioStream_getPerformanceMode(mStream);
        printf("-------- opened: deviceId = %3d, perfMode = %d\n",
               deviceId,
               actualPerfMode);

        // Start stream.
        result = AAudioStream_requestStart(mStream);
        ASSERT_EQ(AAUDIO_OK, result);
    }

    void waitForCallbackToStart() {
        // Wait for callback to say it has been called.
        int countDownMillis = 2000;
        constexpr int countDownPeriodMillis = 50;
        while (!mCallbackStarted && countDownMillis > 0) {
            printf("Waiting for callback to start, %d\n", countDownMillis);
            usleep(countDownPeriodMillis * 1000);
            countDownMillis -= countDownPeriodMillis;
        }
        ASSERT_LT(0, countDownMillis);
        ASSERT_TRUE(mCallbackStarted);
    }

// Callback function that fills the audio output buffer.
    static aaudio_data_callback_result_t s_myDataCallbackProc(
            AAudioStream *stream,
            void *userData,
            void * /*audioData */,
            int32_t /* numFrames */
    ) {
        AudioEngine* engine = (AudioEngine*) userData;
        engine->mCallbackStarted = true;
        usleep(kCallbackSleepMicros);
        // it is illegal to call stop() from the callback. It should
        // return an error and not hang.
        engine->mStopResult = AAudioStream_requestStop(stream);
        engine->mCallbackFinished = true;
        return engine->mCallbackResult;
    }

    static void s_myErrorCallbackProc(
                AAudioStream * /* stream */,
                void *userData,
                aaudio_result_t error) {
        AudioEngine *engine = (AudioEngine *)userData;
        engine->mError = error;
    }

    AAudioStream* mStream = nullptr;

    std::atomic<aaudio_result_t> mError{AAUDIO_OK}; // written by error callback
    std::atomic<bool> mCallbackStarted{false};   // written by data callback
    std::atomic<bool> mCallbackFinished{false};  // written by data callback
    std::atomic<aaudio_data_callback_result_t> mCallbackResult{AAUDIO_CALLBACK_RESULT_CONTINUE};
    std::atomic<aaudio_result_t> mStopResult{AAUDIO_OK};
};

/*********************************************************************/
// Tell the callback to return AAUDIO_CALLBACK_RESULT_CONTINUE.

TEST(test_close_timing, aaudio_close_joins_input_none) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
        AAUDIO_PERFORMANCE_MODE_NONE,
        AAUDIO_CALLBACK_RESULT_CONTINUE);
}

TEST(test_close_timing, aaudio_close_joins_output_none) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
        AAUDIO_PERFORMANCE_MODE_NONE,
        AAUDIO_CALLBACK_RESULT_CONTINUE);
}

TEST(test_close_timing, aaudio_close_joins_input_lowlat) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
        AAUDIO_CALLBACK_RESULT_CONTINUE);
}

TEST(test_close_timing, aaudio_close_joins_output_lowlat) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
        AAUDIO_CALLBACK_RESULT_CONTINUE);
}

/*********************************************************************/
// Tell the callback to return AAUDIO_CALLBACK_RESULT_STOP.

TEST(test_close_timing, aaudio_close_joins_input_lowlat_stop) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
        AAUDIO_CALLBACK_RESULT_STOP);
}

TEST(test_close_timing, aaudio_close_joins_output_lowlat_stop) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
        AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
        AAUDIO_CALLBACK_RESULT_STOP);
}

TEST(test_close_timing, aaudio_close_joins_output_none_stop) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
        AAUDIO_PERFORMANCE_MODE_NONE,
        AAUDIO_CALLBACK_RESULT_STOP);
}

TEST(test_close_timing, aaudio_close_joins_input_none_stop) {
    AudioEngine engine;
    engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
        AAUDIO_PERFORMANCE_MODE_NONE,
        AAUDIO_CALLBACK_RESULT_STOP);
}
+0 −159
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Return stop from the callback
 * and then close the stream immediately.
 */

#include <atomic>
#include <mutex>
#include <stdio.h>
#include <thread>
#include <unistd.h>

#include <aaudio/AAudio.h>

#define DURATION_SECONDS   5

struct AudioEngine {
    AAudioStreamBuilder *builder = nullptr;
    AAudioStream        *stream = nullptr;
    std::thread         *thread = nullptr;

    std::atomic<bool>   started{false};
    std::mutex          doneLock; // Use a mutex so we can sleep on it while join()ing.
    std::atomic<bool>   done{false};

    aaudio_result_t join() {
        aaudio_result_t result = AAUDIO_ERROR_INVALID_STATE;
        if (stream != nullptr) {
            while (true) {
                {
                    // Will block if the thread is running.
                    // This mutex is used to close() immediately after the callback returns
                    // and before the requestStop_l() is called.
                    std::lock_guard<std::mutex> lock(doneLock);
                    if (done) break;
                }
                printf("join() got mutex but stream not done!");
                usleep(10 * 1000); // sleep then check again
            }
            result = AAudioStream_close(stream);
            stream = nullptr;
        }
        return result;
    }
};

// Callback function that fills the audio output buffer.
static aaudio_data_callback_result_t s_myDataCallbackProc(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames
) {
    (void) stream;
    (void) audioData;
    (void) numFrames;
    AudioEngine *engine = (struct AudioEngine *)userData;
    std::lock_guard<std::mutex> lock(engine->doneLock);
    engine->started = true;
    usleep(DURATION_SECONDS * 1000 * 1000); // Mimic SynthMark procedure.
    engine->done = true;
    return AAUDIO_CALLBACK_RESULT_STOP;
}

static void s_myErrorCallbackProc(
    AAudioStream *stream __unused,
    void *userData __unused,
    aaudio_result_t error) {
    printf("%s() - error = %d\n", __func__, error);
}

static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine) {
    // Use an AAudioStreamBuilder to contain requested parameters.
    aaudio_result_t result = AAudio_createStreamBuilder(&engine->builder);
    if (result != AAUDIO_OK) {
        printf("AAudio_createStreamBuilder returned %s",
               AAudio_convertResultToText(result));
        return result;
    }

    // Request stream properties.
    AAudioStreamBuilder_setPerformanceMode(engine->builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
    AAudioStreamBuilder_setDataCallback(engine->builder, s_myDataCallbackProc, engine);
    AAudioStreamBuilder_setErrorCallback(engine->builder, s_myErrorCallbackProc, engine);

    // Create an AAudioStream using the Builder.
    result = AAudioStreamBuilder_openStream(engine->builder, &engine->stream);
    if (result != AAUDIO_OK) {
        printf("AAudioStreamBuilder_openStream returned %s",
               AAudio_convertResultToText(result));
        return result;
    }

    return result;
}

int main(int argc, char **argv) {
    (void) argc;
    (void) argv;
    struct AudioEngine engine;
    aaudio_result_t result = AAUDIO_OK;
    int errorCount = 0;

    // Make printf print immediately so that debug info is not stuck
    // in a buffer if we hang or crash.
    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);

    printf("Test Return Stop Hang V1.0\n");

    result = s_OpenAudioStream(&engine);
    if (result != AAUDIO_OK) {
        printf("s_OpenAudioStream returned %s\n",
               AAudio_convertResultToText(result));
        errorCount++;
    }

    // Check to see what kind of stream we actually got.
    int32_t deviceId = AAudioStream_getDeviceId(engine.stream);
    aaudio_performance_mode_t actualPerfMode = AAudioStream_getPerformanceMode(engine.stream);
    printf("-------- opened: deviceId = %3d, perfMode = %d\n", deviceId, actualPerfMode);

    // Start stream.
    result = AAudioStream_requestStart(engine.stream);
    printf("AAudioStream_requestStart() returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
    if (result != AAUDIO_OK) {
        errorCount++;
    } else {
        int counter = 0;
        while (!engine.started) {
            printf("Waiting for stream to start, %d\n", counter++);
            usleep(5 * 1000);
        }
        printf("You should see more messages %d seconds after this. If not then the test failed!\n",
               DURATION_SECONDS);
        result = engine.join(); // This might hang!
        AAudioStreamBuilder_delete(engine.builder);
        engine.builder = nullptr;
    }

    printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
    printf("test %s\n", errorCount ? "FAILED" : "PASSED");

    return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
}
Loading