diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index 9d13eac0..404c897d 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -35,7 +35,8 @@ set(VSTPLUGIN_HEADERS SfizzSettings.h X11RunLoop.h NativeHelpers.h - FileTrie.h) + FileTrie.h + WeakPtr.h) if(APPLE) set(VSTPLUGIN_MAC_SOURCES diff --git a/vst/SfizzVstController.cpp b/vst/SfizzVstController.cpp index 2e6854f1..9e5bf897 100644 --- a/vst/SfizzVstController.cpp +++ b/vst/SfizzVstController.cpp @@ -143,7 +143,6 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) withStateLock([this]() { _uiState = _editor->getCurrentUiState(); }); - _editor.reset(); } SfizzVstEditor* editor = new SfizzVstEditor(this); diff --git a/vst/SfizzVstEditor.h b/vst/SfizzVstEditor.h index e5fa0d8d..b23f7a4f 100644 --- a/vst/SfizzVstEditor.h +++ b/vst/SfizzVstEditor.h @@ -7,6 +7,7 @@ #pragma once #include "SfizzVstController.h" #include "editor/EditorController.h" +#include "WeakPtr.h" #include "public.sdk/source/vst/vstguieditor.h" #include class Editor; @@ -18,8 +19,11 @@ using namespace Steinberg; using namespace VSTGUI; class SfizzVstEditor : public Vst::VSTGUIEditor, - public EditorController { + public EditorController, + public Weakable { public: + using Self = SfizzVstEditor; + explicit SfizzVstEditor(SfizzVstController* controller); ~SfizzVstEditor(); @@ -41,6 +45,10 @@ public: SfizzUiState getCurrentUiState() const; void receiveMessage(const void* data, uint32_t size); + void remember() override { SfizzVstEditor::addRef(); } + void forget() override { SfizzVstEditor::release(); } + WEAKABLE_REFCOUNT_METHODS(SfizzVstEditor) + private: void processOscQueue(); diff --git a/vst/WeakPtr.h b/vst/WeakPtr.h new file mode 100644 index 00000000..55481e35 --- /dev/null +++ b/vst/WeakPtr.h @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "base/source/fobject.h" +#include +#include + +/** + * A weak reference implementation for Steinberg FObject. + * + * Implementation + * ============== + * + * This takes over the ordinary addRef() and release() methods. + * The variable `refCount` is accessed manually, under a shared mutex. + * There is a unique data block which is shared with all weak pointers, the + * system will null it atomically when the reference count hits zero. + * + * Usage + * ===== + * + * class MyObject : public FObject, public Weakable { + * [...] + * WEAKABLE_REFCOUNT_METHODS(MyObject) + * }; + * + * WeakPtr ptr = myObject.getWeakPtr(); + */ + +template +class Weakable; + +#define WEAKABLE_REFCOUNT_METHODS(T) \ +public: \ + Steinberg::uint32 PLUGIN_API addRef() SMTG_OVERRIDE { return weakAddRef(); } \ + Steinberg::uint32 PLUGIN_API release() SMTG_OVERRIDE { return weakRelease(); } \ +private: \ + friend class Weakable; \ + friend class WeakPtr; + +/// +template +struct WeakPtrSharedData : public std::enable_shared_from_this> { + explicit WeakPtrSharedData(T* self) : self_(self) {} + std::mutex mutex_; + T* self_ = nullptr; +}; + +/// +template +class WeakPtr { + friend class Weakable; + using SharedData = WeakPtrSharedData; + +public: + WeakPtr() = default; + + Steinberg::IPtr lock() + { + std::shared_ptr data = data_.lock(); + if (!data) + return nullptr; + std::lock_guard lock { data->mutex_ }; + T* self = data->self_; + if (self) + ++self->refCount; // manually because we are holding the lock + return Steinberg::IPtr(self, false); + } + +private: + explicit WeakPtr(std::weak_ptr data) : data_(data) {} + std::weak_ptr data_; +}; + +/// +template +class Weakable { + using SharedData = WeakPtrSharedData; + +public: + Weakable() + : weakData_(new SharedData(static_cast(this))) + { + } + + WeakPtr getWeakPtr() + { + return WeakPtr(weakData_); + } + +protected: + Steinberg::uint32 weakAddRef() //override + { + T* self = static_cast(this); + std::lock_guard lock { weakData_->mutex_ }; + return ++self->refCount; + } + + Steinberg::uint32 weakRelease() //override + { + T* self = static_cast(this); + std::shared_ptr data = weakData_; + std::unique_lock lock { data->mutex_ }; + Steinberg::uint32 count = --self->refCount; + if (count == 0) { + data->self_ = nullptr; + weakData_.reset(); + self->refCount = -1000; + lock.unlock(); + delete self; + return 0; + } + return count; + } + +private: + std::shared_ptr weakData_; +};