aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Burk <philburk@mobileer.com>2020-04-15 13:49:11 -0700
committerPhil Burk <philburk@mobileer.com>2020-04-15 13:49:11 -0700
commit68f7df0d535e8d282cd7bc5feeca9ac5a77fc081 (patch)
treedf31885caee2c2fd0dacc72db5b3475b874055a5
parent55d878a4e85e1994f2b5883366079b991500a25f (diff)
downloadoboe-68f7df0d535e8d282cd7bc5feeca9ac5a77fc081.tar.gz
oboe: add openSharedStream
To prevent race conditions with the onError callbacks that were resulting in the use of a deleted stream. For bug #820
-rw-r--r--include/oboe/AudioStream.h21
-rw-r--r--include/oboe/AudioStreamBuilder.h3
-rw-r--r--src/aaudio/AudioStreamAAudio.cpp20
-rw-r--r--src/common/AudioStreamBuilder.cpp10
4 files changed, 52 insertions, 2 deletions
diff --git a/include/oboe/AudioStream.h b/include/oboe/AudioStream.h
index d9b2046a..f2452585 100644
--- a/include/oboe/AudioStream.h
+++ b/include/oboe/AudioStream.h
@@ -418,6 +418,22 @@ public:
ResultWithValue<int32_t> waitForAvailableFrames(int32_t numFrames,
int64_t timeoutNanoseconds);
+ /*
+ * INTERNAL USE ONLY
+ * Set a weak_ptr to this stream from the shared_ptr so that we can
+ * later use a shared_ptr in the error callback.
+ */
+ void setWeakThis(std::shared_ptr<oboe::AudioStream> &sharedStream) {
+ mWeakThis = sharedStream;
+ }
+
+ /*
+ * Make a shared_ptr that will prevent this stream from being deleted.
+ */
+ std::shared_ptr<oboe::AudioStream> lockWeakThis() {
+ return mWeakThis.lock();
+ }
+
protected:
/**
@@ -497,17 +513,18 @@ protected:
std::mutex mLock; // for synchronizing start/stop/close
+
private:
int mPreviousScheduler = -1;
std::atomic<bool> mDataCallbackEnabled{false};
std::atomic<bool> mErrorCallbackCalled{false};
-
+ std::weak_ptr<AudioStream> mWeakThis;
};
/**
- * This struct is a stateless functor which closes a audiostream prior to its deletion.
+ * This struct is a stateless functor which closes an AudioStream prior to its deletion.
* This means it can be used to safely delete a smart pointer referring to an open stream.
*/
struct StreamDeleterFunctor {
diff --git a/include/oboe/AudioStreamBuilder.h b/include/oboe/AudioStreamBuilder.h
index b0f7de8e..636803a7 100644
--- a/include/oboe/AudioStreamBuilder.h
+++ b/include/oboe/AudioStreamBuilder.h
@@ -25,6 +25,7 @@ namespace oboe {
// This depends on AudioStream, so we use forward declaration, it will close and delete the stream
struct StreamDeleterFunctor;
using ManagedStream = std::unique_ptr<AudioStream, StreamDeleterFunctor>;
+
/**
* Factory class for an audio Stream.
*/
@@ -399,6 +400,8 @@ public:
*/
Result openManagedStream(ManagedStream &stream);
+ Result openSharedStream(std::shared_ptr<oboe::AudioStream> &sharedStream);
+
private:
/**
diff --git a/src/aaudio/AudioStreamAAudio.cpp b/src/aaudio/AudioStreamAAudio.cpp
index b19ec915..383e9f60 100644
--- a/src/aaudio/AudioStreamAAudio.cpp
+++ b/src/aaudio/AudioStreamAAudio.cpp
@@ -74,6 +74,17 @@ static void oboe_aaudio_error_thread_proc(AudioStreamAAudio *oboeStream,
LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__);
}
+// This runs in its own thread.
+// Only one of these threads will be launched from internalErrorCallback().
+// Prevents deletion of the stream if the app is using AudioStreamBuilder::openSharedStream()
+static void oboe_aaudio_error_thread_proc_shared(std::shared_ptr<AudioStream> sharedStream,
+ Result error) {
+ LOGD("%s() - entering >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", __func__);
+ AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(sharedStream.get());
+ oboe_aaudio_error_thread_proc(oboeStream, error);
+ LOGD("%s() - exiting <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", __func__);
+}
+
namespace oboe {
/*
@@ -101,12 +112,21 @@ void AudioStreamAAudio::internalErrorCallback(
void *userData,
aaudio_result_t error) {
AudioStreamAAudio *oboeStream = reinterpret_cast<AudioStreamAAudio*>(userData);
+
+ // Prevents deletion of the stream if the app is using AudioStreamBuilder::openSharedStream()
+ std::shared_ptr<AudioStream> sharedStream = oboeStream->lockWeakThis();
+
// These checks should be enough because we assume that the stream close()
// will join() any active callback threads and will not allow new callbacks.
if (oboeStream->wasErrorCallbackCalled()) { // block extra error callbacks
LOGE("%s() multiple error callbacks called!", __func__);
} else if (stream != oboeStream->getUnderlyingStream()) {
LOGD("%s() stream already closed", __func__); // can happen if there are bugs
+ } else if (sharedStream) {
+ // Handle error on a separate thread using shared pointer.
+ std::thread t(oboe_aaudio_error_thread_proc_shared, sharedStream,
+ static_cast<Result>(error));
+ t.detach();
} else {
// Handle error on a separate thread.
std::thread t(oboe_aaudio_error_thread_proc, oboeStream,
diff --git a/src/common/AudioStreamBuilder.cpp b/src/common/AudioStreamBuilder.cpp
index 9d446258..3ca3e103 100644
--- a/src/common/AudioStreamBuilder.cpp
+++ b/src/common/AudioStreamBuilder.cpp
@@ -186,4 +186,14 @@ Result AudioStreamBuilder::openManagedStream(oboe::ManagedStream &stream) {
return result;
}
+Result AudioStreamBuilder::openSharedStream(std::shared_ptr<oboe::AudioStream> &sharedStream) {
+ sharedStream.reset();
+ AudioStream *streamptr;
+ auto result = openStream(&streamptr);
+ sharedStream.reset(streamptr);
+ // Save a weak_ptr in the stream for use with callbacks.
+ streamptr->setWeakThis(sharedStream);
+ return result;
+}
+
} // namespace oboe \ No newline at end of file