1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /**
18 * Test whether the callback is joined before the close finishes.
19 *
20 * Start a stream with a callback.
21 * The callback just sleeps for a long time.
22 * While the callback is sleeping, close() the stream from the main thread.
23 * Then check to make sure the callback was joined before the close() returns.
24 *
25 * This can hang if there are deadlocks. So make sure you get a PASSED result.
26 */
27
28 #include <atomic>
29 #include <stdio.h>
30 #include <unistd.h>
31
32 #include <gtest/gtest.h>
33
34 #include <aaudio/AAudio.h>
35
36 // Sleep long enough that the foreground has a change to call close.
37 static constexpr int kCallbackSleepMicros = 600 * 1000;
38
39 class AudioEngine {
40 public:
41
42 // Check for a crash or late callback if we close without stopping.
checkCloseJoins(aaudio_direction_t direction,aaudio_performance_mode_t perfMode,aaudio_data_callback_result_t callbackResult)43 void checkCloseJoins(aaudio_direction_t direction,
44 aaudio_performance_mode_t perfMode,
45 aaudio_data_callback_result_t callbackResult) {
46
47 // Make printf print immediately so that debug info is not stuck
48 // in a buffer if we hang or crash.
49 setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
50
51 mCallbackResult = callbackResult;
52 startStreamForStall(direction, perfMode);
53 // When the callback starts it will go to sleep.
54 waitForCallbackToStart();
55
56 printf("call AAudioStream_close()\n");
57 ASSERT_FALSE(mCallbackFinished); // Still sleeping?
58 aaudio_result_t result = AAudioStream_close(mStream); // May hang here!
59 ASSERT_TRUE(mCallbackFinished);
60 ASSERT_EQ(AAUDIO_OK, result);
61 printf("AAudioStream_close() returned %d\n", result);
62
63 ASSERT_EQ(AAUDIO_OK, mError.load());
64 // Did calling stop() from callback fail? It should have.
65 ASSERT_NE(AAUDIO_OK, mStopResult.load());
66 }
67
68 private:
startStreamForStall(aaudio_direction_t direction,aaudio_performance_mode_t perfMode)69 void startStreamForStall(aaudio_direction_t direction,
70 aaudio_performance_mode_t perfMode) {
71 AAudioStreamBuilder* builder = nullptr;
72 aaudio_result_t result = AAUDIO_OK;
73
74 // Use an AAudioStreamBuilder to contain requested parameters.
75 result = AAudio_createStreamBuilder(&builder);
76 ASSERT_EQ(AAUDIO_OK, result);
77
78 // Request stream properties.
79 AAudioStreamBuilder_setDirection(builder, direction);
80 AAudioStreamBuilder_setPerformanceMode(builder, perfMode);
81 AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, this);
82 AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, this);
83
84 // Create an AAudioStream using the Builder.
85 result = AAudioStreamBuilder_openStream(builder, &mStream);
86 AAudioStreamBuilder_delete(builder);
87 ASSERT_EQ(AAUDIO_OK, result);
88
89 // Check to see what kind of stream we actually got.
90 int32_t deviceId = AAudioStream_getDeviceId(mStream);
91 aaudio_performance_mode_t
92 actualPerfMode = AAudioStream_getPerformanceMode(mStream);
93 printf("-------- opened: deviceId = %3d, perfMode = %d\n",
94 deviceId,
95 actualPerfMode);
96
97 // Start stream.
98 result = AAudioStream_requestStart(mStream);
99 ASSERT_EQ(AAUDIO_OK, result);
100 }
101
waitForCallbackToStart()102 void waitForCallbackToStart() {
103 // Wait for callback to say it has been called.
104 int countDownMillis = 2000;
105 constexpr int countDownPeriodMillis = 50;
106 while (!mCallbackStarted && countDownMillis > 0) {
107 printf("Waiting for callback to start, %d\n", countDownMillis);
108 usleep(countDownPeriodMillis * 1000);
109 countDownMillis -= countDownPeriodMillis;
110 }
111 ASSERT_LT(0, countDownMillis);
112 ASSERT_TRUE(mCallbackStarted);
113 }
114
115 // Callback function that fills the audio output buffer.
s_myDataCallbackProc(AAudioStream * stream,void * userData,void *,int32_t)116 static aaudio_data_callback_result_t s_myDataCallbackProc(
117 AAudioStream *stream,
118 void *userData,
119 void * /*audioData */,
120 int32_t /* numFrames */
121 ) {
122 AudioEngine* engine = (AudioEngine*) userData;
123 engine->mCallbackStarted = true;
124 usleep(kCallbackSleepMicros);
125 // it is illegal to call stop() from the callback. It should
126 // return an error and not hang.
127 engine->mStopResult = AAudioStream_requestStop(stream);
128 engine->mCallbackFinished = true;
129 return engine->mCallbackResult;
130 }
131
s_myErrorCallbackProc(AAudioStream *,void * userData,aaudio_result_t error)132 static void s_myErrorCallbackProc(
133 AAudioStream * /* stream */,
134 void *userData,
135 aaudio_result_t error) {
136 AudioEngine *engine = (AudioEngine *)userData;
137 engine->mError = error;
138 }
139
140 AAudioStream* mStream = nullptr;
141
142 std::atomic<aaudio_result_t> mError{AAUDIO_OK}; // written by error callback
143 std::atomic<bool> mCallbackStarted{false}; // written by data callback
144 std::atomic<bool> mCallbackFinished{false}; // written by data callback
145 std::atomic<aaudio_data_callback_result_t> mCallbackResult{AAUDIO_CALLBACK_RESULT_CONTINUE};
146 std::atomic<aaudio_result_t> mStopResult{AAUDIO_OK};
147 };
148
149 /*********************************************************************/
150 // Tell the callback to return AAUDIO_CALLBACK_RESULT_CONTINUE.
151
TEST(test_close_timing,aaudio_close_joins_input_none)152 TEST(test_close_timing, aaudio_close_joins_input_none) {
153 AudioEngine engine;
154 engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
155 AAUDIO_PERFORMANCE_MODE_NONE,
156 AAUDIO_CALLBACK_RESULT_CONTINUE);
157 }
158
TEST(test_close_timing,aaudio_close_joins_output_none)159 TEST(test_close_timing, aaudio_close_joins_output_none) {
160 AudioEngine engine;
161 engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
162 AAUDIO_PERFORMANCE_MODE_NONE,
163 AAUDIO_CALLBACK_RESULT_CONTINUE);
164 }
165
TEST(test_close_timing,aaudio_close_joins_input_lowlat)166 TEST(test_close_timing, aaudio_close_joins_input_lowlat) {
167 AudioEngine engine;
168 engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
169 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
170 AAUDIO_CALLBACK_RESULT_CONTINUE);
171 }
172
TEST(test_close_timing,aaudio_close_joins_output_lowlat)173 TEST(test_close_timing, aaudio_close_joins_output_lowlat) {
174 AudioEngine engine;
175 engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
176 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
177 AAUDIO_CALLBACK_RESULT_CONTINUE);
178 }
179
180 /*********************************************************************/
181 // Tell the callback to return AAUDIO_CALLBACK_RESULT_STOP.
182
TEST(test_close_timing,aaudio_close_joins_input_lowlat_stop)183 TEST(test_close_timing, aaudio_close_joins_input_lowlat_stop) {
184 AudioEngine engine;
185 engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
186 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
187 AAUDIO_CALLBACK_RESULT_STOP);
188 }
189
TEST(test_close_timing,aaudio_close_joins_output_lowlat_stop)190 TEST(test_close_timing, aaudio_close_joins_output_lowlat_stop) {
191 AudioEngine engine;
192 engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
193 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
194 AAUDIO_CALLBACK_RESULT_STOP);
195 }
196
TEST(test_close_timing,aaudio_close_joins_output_none_stop)197 TEST(test_close_timing, aaudio_close_joins_output_none_stop) {
198 AudioEngine engine;
199 engine.checkCloseJoins(AAUDIO_DIRECTION_OUTPUT,
200 AAUDIO_PERFORMANCE_MODE_NONE,
201 AAUDIO_CALLBACK_RESULT_STOP);
202 }
203
TEST(test_close_timing,aaudio_close_joins_input_none_stop)204 TEST(test_close_timing, aaudio_close_joins_input_none_stop) {
205 AudioEngine engine;
206 engine.checkCloseJoins(AAUDIO_DIRECTION_INPUT,
207 AAUDIO_PERFORMANCE_MODE_NONE,
208 AAUDIO_CALLBACK_RESULT_STOP);
209 }
210