Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,142 @@
//-----------------------------------------------------------------------------
// 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 <array>
#include <atomic>
#include <cassert>
#include <memory>
//------------------------------------------------------------------------
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 <typename ObjectT, typename Deleter = std::default_delete<ObjectT>>
struct RTTransferT
{
using ObjectType = ObjectT;
using ObjectTypePtr = std::unique_ptr<ObjectType, Deleter>;
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 <typename Proc>
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<ObjectType*>;
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<AtomicObjectPtr, 3> storage {};
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg