// 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_; };