1 /*
2 * Copyright 2024 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 #include <atomic>
18 #include <tuple>
19
20 #include <gtest/gtest.h>
21 #include <oboe/Oboe.h>
22 #include <thread>
23 #include <future>
24
25 // Test returning DataCallbackResult::Stop from a callback.
26 using namespace oboe;
27
28 // Test whether there is a deadlock when stopping streams.
29 // See Issue #2059
30
31 class TestReturnStopDeadlock : public ::testing::Test {
32 public:
33
34 void start(bool useOpenSL);
35 void stop();
36
getCycleCount()37 int32_t getCycleCount() {
38 return mCycleCount.load();
39 }
40
41 protected:
42 void TearDown() override;
43
44 private:
45
46 void cycleRapidly(bool useOpenSL);
47
48 class MyDataCallback : public oboe::AudioStreamDataCallback { public:
49
MyDataCallback()50 MyDataCallback() {}
51
52 oboe::DataCallbackResult onAudioReady(
53 oboe::AudioStream *audioStream,
54 void *audioData,
55 int32_t numFrames) override;
56
57 std::atomic<bool> returnStop = false;
58 std::atomic<int32_t> callbackCount{0};
59 };
60
61 std::shared_ptr<oboe::AudioStream> mStream;
62 std::shared_ptr<MyDataCallback> mDataCallback;
63 std::atomic<int32_t> mCycleCount{0};
64 std::atomic<bool> mThreadEnabled{false};
65 std::thread mCycleThread;
66
67 static constexpr int kChannelCount = 1;
68 static constexpr int kMaxSleepMicros = 25000;
69 };
70
71 // start a thread to cycle through stream tests
start(bool useOpenSL)72 void TestReturnStopDeadlock::start(bool useOpenSL) {
73 mThreadEnabled = true;
74 mCycleCount = 0;
75 mCycleThread = std::thread([this, useOpenSL]() {
76 cycleRapidly(useOpenSL);
77 });
78 }
79
stop()80 void TestReturnStopDeadlock::stop() {
81 mThreadEnabled = false;
82 // Terminate the thread with a timeout.
83 const int timeout = 1;
84 auto future = std::async(std::launch::async, &std::thread::join, &mCycleThread);
85 ASSERT_NE(future.wait_for(std::chrono::seconds(timeout)), std::future_status::timeout)
86 << " join() timed out! cycles = " << getCycleCount();
87 }
88
TearDown()89 void TestReturnStopDeadlock::TearDown() {
90 if (mStream) {
91 mStream->close();
92 }
93 }
94
cycleRapidly(bool useOpenSL)95 void TestReturnStopDeadlock::cycleRapidly(bool useOpenSL) {
96 while(mThreadEnabled) {
97 mCycleCount++;
98 mDataCallback = std::make_shared<MyDataCallback>();
99
100 AudioStreamBuilder builder;
101 oboe::Result result = builder.setFormat(oboe::AudioFormat::Float)
102 ->setAudioApi(useOpenSL ? oboe::AudioApi::OpenSLES : oboe::AudioApi::AAudio)
103 ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
104 ->setChannelCount(kChannelCount)
105 ->setDataCallback(mDataCallback)
106 ->setUsage(oboe::Usage::Notification)
107 ->openStream(mStream);
108 ASSERT_EQ(result, oboe::Result::OK);
109
110 mStream->setDelayBeforeCloseMillis(0);
111
112 result = mStream->requestStart();
113 ASSERT_EQ(result, oboe::Result::OK);
114
115 // Sleep for some random time.
116 int countdown = 100;
117 while ((mDataCallback->callbackCount < 4) && (--countdown > 0)) {
118 int32_t durationMicros = (int32_t)(drand48() * kMaxSleepMicros);
119 usleep(durationMicros);
120 }
121 mDataCallback->returnStop = true;
122 result = mStream->close();
123 ASSERT_EQ(result, oboe::Result::OK);
124 mStream = nullptr;
125 ASSERT_GT(mDataCallback->callbackCount, 1) << " cycleCount = " << mCycleCount;
126 }
127 }
128
129 // Callback that returns Continue or Stop
onAudioReady(AudioStream * audioStream,void * audioData,int32_t numFrames)130 DataCallbackResult TestReturnStopDeadlock::MyDataCallback::onAudioReady(
131 AudioStream *audioStream,
132 void *audioData,
133 int32_t numFrames) {
134 float *floatData = (float *) audioData;
135 const int numSamples = numFrames * kChannelCount;
136 callbackCount++;
137
138 // Fill buffer with white noise.
139 for (int i = 0; i < numSamples; i++) {
140 floatData[i] = ((float) drand48() - 0.5f) * 2 * 0.1f;
141 }
142 usleep(500); // half a millisecond
143 if (returnStop) {
144 usleep(20 * 1000);
145 return DataCallbackResult::Stop;
146 } else {
147 return DataCallbackResult::Continue;
148 }
149 }
150
TEST_F(TestReturnStopDeadlock,RapidCycleAAudio)151 TEST_F(TestReturnStopDeadlock, RapidCycleAAudio){
152 start(false);
153 usleep(3000 * 1000);
154 stop();
155 }
156
TEST_F(TestReturnStopDeadlock,RapidCycleOpenSL)157 TEST_F(TestReturnStopDeadlock, RapidCycleOpenSL){
158 start(true);
159 usleep(3000 * 1000);
160 stop();
161 }
162