• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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