| /* |
| * Copyright 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef OBOETESTER_TEST_ERROR_CALLBACK_H |
| #define OBOETESTER_TEST_ERROR_CALLBACK_H |
| |
| #include "oboe/Oboe.h" |
| #include <thread> |
| |
| /** |
| * This code is an experiment to see if we can cause a crash from the ErrorCallback. |
| */ |
| class TestErrorCallback { |
| public: |
| |
| oboe::Result open(); |
| oboe::Result start(); |
| oboe::Result stop(); |
| oboe::Result close(); |
| |
| int test(); |
| |
| int32_t getCallbackMagic() { |
| return mCallbackMagic.load(); |
| } |
| |
| protected: |
| |
| std::atomic<int32_t> mCallbackMagic{0}; |
| |
| private: |
| |
| void cleanup() { |
| mDataCallback.reset(); |
| mErrorCallback.reset(); |
| mStream.reset(); |
| } |
| |
| class MyDataCallback : public oboe::AudioStreamDataCallback { public: |
| |
| oboe::DataCallbackResult onAudioReady( |
| oboe::AudioStream *audioStream, |
| void *audioData, |
| int32_t numFrames) override; |
| |
| }; |
| |
| class MyErrorCallback : public oboe::AudioStreamErrorCallback { |
| public: |
| |
| MyErrorCallback(TestErrorCallback *parent): mParent(parent) {} |
| |
| virtual ~MyErrorCallback() { |
| // If the delete occurs before onErrorAfterClose() then this bad magic |
| // value will be seen by the Java test code, causing a failure. |
| // It is also possible that this code will just cause OboeTester to crash! |
| mMagic = 0xdeadbeef; |
| LOGE("%s() called", __func__); |
| } |
| |
| 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! |
| // Do not try this at home. We are just trying to reproduce the crash |
| // reported in #1603. |
| std::thread t([this]() { |
| this->mParent->cleanup(); // Possibly delete stream and callback objects. |
| LOGE("onErrorBeforeClose called cleanup!"); |
| }); |
| t.detach(); |
| // There is a race condition between the deleting thread and this thread. |
| // We do not want to add synchronization because the object is getting deleted |
| // and cannot be relied on. |
| // So we sleep here to give the deleting thread a chance to win the race. |
| usleep(10 * 1000); |
| } |
| |
| void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result error) override { |
| // The callback was probably deleted by now. |
| LOGE("%s() - error = %s, mMagic = 0x%08X", |
| __func__, oboe::convertToText(error), mMagic.load()); |
| mParent->mCallbackMagic = mMagic.load(); |
| } |
| |
| private: |
| TestErrorCallback *mParent; |
| // This must match the value in TestErrorCallbackActivity.java |
| static constexpr int32_t kMagicGood = 0x600DCAFE; |
| std::atomic<int32_t> mMagic{kMagicGood}; |
| }; |
| |
| std::shared_ptr<oboe::AudioStream> mStream; |
| std::shared_ptr<MyDataCallback> mDataCallback; |
| std::shared_ptr<MyErrorCallback> mErrorCallback; |
| |
| static constexpr int kChannelCount = 2; |
| }; |
| |
| #endif //OBOETESTER_TEST_ERROR_CALLBACK_H |