Merge "aaudio test: trigger race condition and hang" into qt-dev
am: 470213eb4e
Change-Id: Ieb73926a90a7d62f537db9288fb9282fac20bc5d
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 958bb2e..6101e99 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -198,6 +198,18 @@
}
cc_test {
+ name: "test_stop_hang",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_stop_hang.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
+
+cc_test {
name: "test_full_queue",
defaults: ["libaaudio_tests_defaults"],
srcs: ["test_full_queue.cpp"],
diff --git a/media/libaaudio/tests/test_stop_hang.cpp b/media/libaaudio/tests/test_stop_hang.cpp
new file mode 100644
index 0000000..2397b6c
--- /dev/null
+++ b/media/libaaudio/tests/test_stop_hang.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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() 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;
+}