//----------------------------------------------------------------------------- // Flags : clang-format SMTGSequencer // Project : VST SDK // // Category : Helpers // Filename : public.sdk/source/vst/utility/rttransfer.h // Created by : Steinberg, 04/2021 // Description : Realtime Object Transfer // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include #include #include #include //------------------------------------------------------------------------ namespace Steinberg { namespace Vst { //------------------------------------------------------------------------ /** Transfer objects from a non realtime thread to a realtime one * * You have to use it from two threads, the realtime context thread where you are not allowed to * block and a non realtime thread from where the object is coming. * * It's guaranteed that the function you should only call in the realtime context is wait free and * does not do any allocations or deallocations * */ template > struct RTTransferT { using ObjectType = ObjectT; using ObjectTypePtr = std::unique_ptr; RTTransferT () { assert (storage[0].is_lock_free ()); } ~RTTransferT () noexcept { clear_ui (); } /** Access the transfer object. * * If there's a new object, the proc is called with the new object. The object is only valid * inside the proc. * * To be called from the realtime context. */ template void accessTransferObject_rt (Proc proc) noexcept { ObjectType* newObject {nullptr}; ObjectType* currentObject = storage[0].load (); if (currentObject && storage[0].compare_exchange_strong (currentObject, newObject)) { proc (*currentObject); ObjectType* transitObj = storage[1].load (); if (storage[1].compare_exchange_strong (transitObj, currentObject) == false) { assert (false); } ObjectType* oldObject = storage[2].load (); if (storage[2].compare_exchange_strong (oldObject, transitObj) == false) { assert (false); } } } /** Transfer an object to the realtime context. * * The ownership of newObject is transfered to this object and the Deleter is used to free * the memory of it afterwards. * * If there's already an object in transfer the previous object will be deallocated and * replaced with the new one without passing to the realtime context. * * To be called from the non realtime context. */ void transferObject_ui (ObjectTypePtr&& newObjectPtr) { ObjectType* newObject = newObjectPtr.release (); clear_ui (); while (true) { ObjectType* currentObject = storage[0].load (); if (storage[0].compare_exchange_strong (currentObject, newObject)) { deallocate (currentObject); break; } } } /** Clear the transfer. * * To be called from the non realtime context. */ void clear_ui () { clearStorage (storage[0]); clearStorage (storage[1]); clearStorage (storage[2]); } private: using AtomicObjectPtr = std::atomic; void clearStorage (AtomicObjectPtr& atomObj) { ObjectType* newObject = nullptr; while (ObjectType* current = atomObj.load ()) { if (atomObj.compare_exchange_strong (current, newObject)) { deallocate (current); break; } } } void deallocate (ObjectType* object) { if (object) { Deleter d; d (object); } } std::array storage {}; }; //------------------------------------------------------------------------ } // Vst } // Steinberg