Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oboe: use shared_ptr for callbacks #1626

Merged
merged 2 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions apps/OboeTester/app/src/main/cpp/TestErrorCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ using namespace oboe;

oboe::Result TestErrorCallback::open() {
mCallbackMagic = 0;
mDataCallback = std::make_unique<MyDataCallback>();
mErrorCallback = std::make_unique<MyErrorCallback>(this);
mDataCallback = std::make_shared<MyDataCallback>();
mErrorCallback = std::make_shared<MyErrorCallback>(this);
AudioStreamBuilder builder;
oboe::Result result = builder.setSharingMode(oboe::SharingMode::Exclusive)
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
->setFormat(oboe::AudioFormat::Float)
->setChannelCount(kChannelCount)
#if 0
->setDataCallback(mDataCallback.get())
->setErrorCallback(mErrorCallback.get())
->setErrorCallback(mErrorCallback.get()) // This can lead to a crash or FAIL.
#else
->setDataCallback(mDataCallback)
->setErrorCallback(mErrorCallback) // shared_ptr avoids a crash
#endif
->openStream(mStream);
return result;
}
Expand Down
10 changes: 6 additions & 4 deletions apps/OboeTester/app/src/main/cpp/TestErrorCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ class TestErrorCallback {
private:

void cleanup() {
mDataCallback.reset();
mErrorCallback.reset();
mStream.reset();
}

class MyDataCallback : public oboe::AudioStreamDataCallback { public:
Expand Down Expand Up @@ -72,11 +74,11 @@ class TestErrorCallback {
void onErrorBeforeClose(oboe::AudioStream *oboeStream, oboe::Result error) override {
LOGE("%s() - error = %s, parent = %p",
__func__, oboe::convertToText(error), &mParent);
// Trigger a crash by deleting this callback object while in use!
// Trigger a crash by "deleting" this callback object while in use!
// Do not try this at home. We are just trying to reproduce the crash
// reported in #1603.
std::thread t([this]() {
this->mParent->cleanup();
this->mParent->cleanup(); // Possibly delete stream and callback objects.
LOGE("onErrorBeforeClose called cleanup!");
});
t.detach();
Expand All @@ -102,8 +104,8 @@ class TestErrorCallback {
};

std::shared_ptr<oboe::AudioStream> mStream;
std::unique_ptr<MyDataCallback> mDataCallback;
std::unique_ptr<MyErrorCallback> mErrorCallback;
std::shared_ptr<MyDataCallback> mDataCallback;
std::shared_ptr<MyErrorCallback> mErrorCallback;

static constexpr int kChannelCount = 2;
};
Expand Down
2 changes: 2 additions & 0 deletions include/oboe/AudioStreamBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,11 @@ class AudioStreamBase {
protected:
/** The callback which will be fired when new data is ready to be read/written. **/
AudioStreamDataCallback *mDataCallback = nullptr;
std::shared_ptr<AudioStreamDataCallback> mSharedDataCallback;

/** The callback which will be fired when an error or a disconnect occurs. **/
AudioStreamErrorCallback *mErrorCallback = nullptr;
std::shared_ptr<AudioStreamErrorCallback> mSharedErrorCallback;

/** Number of audio frames which will be requested in each callback */
int32_t mFramesPerCallback = kUnspecified;
Expand Down
47 changes: 44 additions & 3 deletions include/oboe/AudioStreamBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,31 @@ class AudioStreamBuilder : public AudioStreamBase {
* <strong>Important: See AudioStreamCallback for restrictions on what may be called
* from the callback methods.</strong>
*
* We pass a shared_ptr so that the sharedDataCallback object cannot be deleted
* before the stream is deleted.
*
* @param dataCallback
* @return pointer to the builder so calls can be chained
*/
AudioStreamBuilder *setDataCallback(oboe::AudioStreamDataCallback *dataCallback) {
AudioStreamBuilder *setDataCallback(std::shared_ptr<AudioStreamDataCallback> sharedDataCallback) {
// Use this raw pointer in the rest of the code to retain backwards compatibility.
mDataCallback = sharedDataCallback.get();
philburk marked this conversation as resolved.
Show resolved Hide resolved
// Hold a shared_ptr to protect the raw pointer for the lifetime of the stream.
mSharedDataCallback = sharedDataCallback;
return this;
}

/**
* Pass a raw pointer to a data callback. This is not recommended because the dataCallback
* object might get deleted by the app while it is being used.
*
* @deprecated Call setDataCallback(std::shared_ptr<AudioStreamDataCallback>) instead.
* @param dataCallback
* @return pointer to the builder so calls can be chained
*/
AudioStreamBuilder *setDataCallback(AudioStreamDataCallback *dataCallback) {
philburk marked this conversation as resolved.
Show resolved Hide resolved
mDataCallback = dataCallback;
mSharedDataCallback = nullptr;
return this;
}

Expand All @@ -374,11 +394,32 @@ class AudioStreamBuilder : public AudioStreamBase {
* <strong>When an error callback occurs, the associated stream must be stopped and closed
* in a separate thread.</strong>
*
* @param errorCallback
* We pass a shared_ptr so that the errorCallback object cannot be deleted before the stream is deleted.
* If the stream was created using a shared_ptr then the stream cannot be deleted before the
* error callback has finished running.
*
* @param sharedErrorCallback
* @return pointer to the builder so calls can be chained
*/
AudioStreamBuilder *setErrorCallback(oboe::AudioStreamErrorCallback *errorCallback) {
AudioStreamBuilder *setErrorCallback(std::shared_ptr<AudioStreamErrorCallback> sharedErrorCallback) {
// Use this raw pointer in the rest of the code to retain backwards compatibility.
mErrorCallback = sharedErrorCallback.get();
// Hold a shared_ptr to protect the raw pointer for the lifetime of the stream.
mSharedErrorCallback = sharedErrorCallback;
return this;
}

/**
* Pass a raw pointer to an error callback. This is not recommended because the errorCallback
* object might get deleted by the app while it is being used.
*
* @deprecated Call setErrorCallback(std::shared_ptr<AudioStreamErrorCallback>) instead.
* @param errorCallback
* @return pointer to the builder so calls can be chained
*/
AudioStreamBuilder *setErrorCallback(AudioStreamErrorCallback *errorCallback) {
mErrorCallback = errorCallback;
mSharedErrorCallback = nullptr;
return this;
}

Expand Down