Revert "Revert "Lock waitForStateChange()""
diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp
index b8c4394..bfbf3ee 100644
--- a/src/aaudio/AudioStreamAAudio.cpp
+++ b/src/aaudio/AudioStreamAAudio.cpp
@@ -20,6 +20,7 @@
#include "aaudio/AAudioLoader.h"
#include "aaudio/AudioStreamAAudio.h"
+#include "common/AudioClock.h"
#include "common/OboeDebug.h"
#include "oboe/Utilities.h"
@@ -370,25 +371,69 @@
}
}
+
+// AAudioStream_waitForStateChange() can crash if it is waiting on a stream and that stream
+// is closed from another thread. We do not want to lock the stream for the duration of the call.
+// So we call AAudioStream_waitForStateChange() with a timeout of zero so that it will not block.
+// Then we can do our own sleep with the lock unlocked.
Result AudioStreamAAudio::waitForStateChange(StreamState currentState,
StreamState *nextState,
int64_t timeoutNanoseconds) {
- AAudioStream *stream = mAAudioStream.load();
- if (stream != nullptr) {
+ Result oboeResult = Result::ErrorTimeout;
+ int64_t durationNanos = 20 * kNanosPerMillisecond; // arbitrary
+ aaudio_stream_state_t currentAAudioState = static_cast<aaudio_stream_state_t>(currentState);
- aaudio_stream_state_t aaudioNextState;
- aaudio_result_t result = mLibLoader->stream_waitForStateChange(
- mAAudioStream,
- static_cast<aaudio_stream_state_t>(currentState),
- &aaudioNextState,
- timeoutNanoseconds);
- *nextState = static_cast<StreamState>(aaudioNextState);
- return static_cast<Result>(result);
- } else {
- *nextState = StreamState::Closed;
+ aaudio_stream_state_t aaudioNextState;
+ aaudio_result_t result = AAUDIO_OK;
+ int64_t timeLeftNanos = timeoutNanoseconds;
+
+ mLock.lock();
+ while (true) {
+ // Do we still have an AAudio stream? If not then stream must have been closed.
+ AAudioStream *stream = mAAudioStream.load();
+ if (stream == nullptr) {
+ if (nextState != nullptr) {
+ *nextState = StreamState::Closed;
+ }
+ oboeResult = Result::ErrorClosed;
+ break;
+ }
+
+ // Update and query state change with no blocking.
+ result = mLibLoader->stream_waitForStateChange(
+ mAAudioStream,
+ currentAAudioState,
+ &aaudioNextState,
+ 0); // timeout=0 for non-blocking
+ if (nextState != nullptr) {
+ *nextState = static_cast<StreamState>(aaudioNextState);
+ }
+ if (result != AAUDIO_OK) {
+ oboeResult = static_cast<Result>(result);
+ break;
+ }
+ if (currentAAudioState != aaudioNextState) { // state changed?
+ oboeResult = Result::OK;
+ break;
+ }
+
+ // Did we timeout or did user ask for non-blocking?
+ if (timeLeftNanos <= 0) {
+ break;
+ }
+
+ // No change yet so sleep.
+ mLock.unlock(); // Don't sleep while locked.
+ if (durationNanos > timeLeftNanos) {
+ durationNanos = timeLeftNanos; // last little bit
+ }
+ AudioClock::sleepForNanos(durationNanos);
+ timeLeftNanos -= durationNanos;
+ mLock.lock();
}
- return (currentState != *nextState) ? Result::OK : Result::ErrorTimeout;
+ mLock.unlock();
+ return oboeResult;
}
ResultWithValue<int32_t> AudioStreamAAudio::setBufferSizeInFrames(int32_t requestedFrames) {
diff --git a/src/common/AudioStream.cpp b/src/common/AudioStream.cpp
index 5dfafb1..63f3c44 100644
--- a/src/common/AudioStream.cpp
+++ b/src/common/AudioStream.cpp
@@ -75,9 +75,13 @@
StreamState endingState,
int64_t timeoutNanoseconds)
{
- StreamState state = getState();
- if (state == StreamState::Closed) {
- return Result::ErrorClosed;
+ StreamState state;
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ state = getState();
+ if (state == StreamState::Closed || state == StreamState::Disconnected) {
+ return Result::ErrorClosed;
+ }
}
StreamState nextState = state;
@@ -87,6 +91,7 @@
return result;
}
}
+
if (nextState != endingState) {
return Result::ErrorInvalidState;
} else {
@@ -98,6 +103,7 @@
{
Result result = requestStart();
if (result != Result::OK) return result;
+ if (timeoutNanoseconds <= 0) return result;
return waitForStateTransition(StreamState::Starting,
StreamState::Started, timeoutNanoseconds);
}
@@ -106,6 +112,7 @@
{
Result result = requestPause();
if (result != Result::OK) return result;
+ if (timeoutNanoseconds <= 0) return result;
return waitForStateTransition(StreamState::Pausing,
StreamState::Paused, timeoutNanoseconds);
}
@@ -114,6 +121,7 @@
{
Result result = requestFlush();
if (result != Result::OK) return result;
+ if (timeoutNanoseconds <= 0) return result;
return waitForStateTransition(StreamState::Flushing,
StreamState::Flushed, timeoutNanoseconds);
}
@@ -122,6 +130,7 @@
{
Result result = requestStop();
if (result != Result::OK) return result;
+ if (timeoutNanoseconds <= 0) return result;
return waitForStateTransition(StreamState::Stopping,
StreamState::Stopped, timeoutNanoseconds);
}
diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp
index 7f9ace0..cfdb1bd 100644
--- a/src/opensles/AudioInputStreamOpenSLES.cpp
+++ b/src/opensles/AudioInputStreamOpenSLES.cpp
@@ -209,7 +209,7 @@
LOGD("AudioInputStreamOpenSLES::%s()", __func__);
mLock.lock();
Result result = Result::OK;
- if (mState == StreamState::Closed){
+ if (getState() == StreamState::Closed){
result = Result::ErrorClosed;
} else {
mLock.unlock(); // avoid recursive lock
diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp
index 3c9e27b..2b785ba 100644
--- a/src/opensles/AudioOutputStreamOpenSLES.cpp
+++ b/src/opensles/AudioOutputStreamOpenSLES.cpp
@@ -240,7 +240,7 @@
Result AudioOutputStreamOpenSLES::close() {
mLock.lock();
Result result = Result::OK;
- if (mState == StreamState::Closed){
+ if (getState() == StreamState::Closed){
result = Result::ErrorClosed;
} else {
mLock.unlock(); // avoid recursive lock
diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp
index 93ad431..20c02af 100644
--- a/src/opensles/AudioStreamOpenSLES.cpp
+++ b/src/opensles/AudioStreamOpenSLES.cpp
@@ -234,7 +234,7 @@
mSimpleBufferQueueInterface = nullptr;
EngineOpenSLES::getInstance().close();
- mState = StreamState::Closed;
+ setState(StreamState::Closed);
return Result::OK;
}
}
@@ -311,23 +311,30 @@
Result AudioStreamOpenSLES::waitForStateChange(StreamState currentState,
StreamState *nextState,
int64_t timeoutNanoseconds) {
- LOGD("AudioStreamOpenSLES::waitForStateChange()");
-
+ Result oboeResult = (timeoutNanoseconds <= 0) ? Result::OK : Result::ErrorTimeout;
int64_t durationNanos = 20 * kNanosPerMillisecond; // arbitrary
- StreamState state = getState();
- while (state == currentState && timeoutNanoseconds > 0){
+ while (true) {
+ const StreamState state = getState(); // this does not require a lock
+ if (nextState != nullptr) {
+ *nextState = state;
+ }
+ if (currentState != state) { // state changed?
+ oboeResult = Result::OK;
+ break;
+ }
+
+ // Did we timeout or did user ask for non-blocking?
+ if (timeoutNanoseconds <= 0) {
+ break;
+ }
+
if (durationNanos > timeoutNanoseconds){
durationNanos = timeoutNanoseconds;
}
AudioClock::sleepForNanos(durationNanos);
timeoutNanoseconds -= durationNanos;
-
- state = getState();
- }
- if (nextState != nullptr) {
- *nextState = state;
}
- return (state == currentState) ? Result::ErrorTimeout : Result::OK;
+ return oboeResult;
}
diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h
index c49fd35..3d6996a 100644
--- a/src/opensles/AudioStreamOpenSLES.h
+++ b/src/opensles/AudioStreamOpenSLES.h
@@ -55,7 +55,7 @@
*
* @return state or a negative error.
*/
- StreamState getState() override { return mState; }
+ StreamState getState() override { return mState.load(); }
int32_t getFramesPerBurst() override;
@@ -101,7 +101,7 @@
* Use this instead of directly setting the internal state variable.
*/
void setState(StreamState state) {
- mState = state;
+ mState.store(state);
}
int64_t getFramesProcessedByServer() const;
@@ -110,11 +110,13 @@
SLObjectItf mObjectInterface = nullptr;
SLAndroidSimpleBufferQueueItf mSimpleBufferQueueInterface = nullptr;
- uint8_t *mCallbackBuffer = nullptr;
- int32_t mBytesPerCallback = oboe::kUnspecified;
- StreamState mState = StreamState::Uninitialized;
+ uint8_t *mCallbackBuffer = nullptr;
+ int32_t mBytesPerCallback = oboe::kUnspecified;
+ MonotonicCounter mPositionMillis; // for tracking OpenSL ES service position
- MonotonicCounter mPositionMillis; // for tracking OpenSL ES service position
+private:
+ std::atomic<StreamState> mState{StreamState::Uninitialized};
+
};
} // namespace oboe