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
+274
View File
@@ -0,0 +1,274 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : SDK Base
// Version : 1.0
//
// Category : Helpers
// Filename : base/source/fobject.cpp
// Created by : Steinberg, 2008
// Description : Basic Object implementing FUnknown
//
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#include "base/source/fobject.h"
#include "base/thread/include/flock.h"
#include <functional>
#include <vector>
#define SMTG_VALIDATE_DEPENDENCY_COUNT DEVELOPMENT // validating dependencyCount
#if SMTG_DEPENDENCY_COUNT
#include "base/source/updatehandler.h"
#define SMTG_DEPENDENCY_CHECK_LEVEL 1 // 1 => minimal assert, 2 => full assert
#endif // SMTG_DEPENDENCY_COUNT
namespace Steinberg {
// Entry point for addRef/release tracking. See fobjecttracker.h
//------------------------------------------------------------------------
#if DEVELOPMENT
using FObjectTrackerFn = std::function<void (FObject*, bool)>;
FObjectTrackerFn gFObjectTracker = nullptr;
#endif
IUpdateHandler* FObject::gUpdateHandler = nullptr;
//------------------------------------------------------------------------
const FUID FObject::iid;
//------------------------------------------------------------------------
struct FObjectIIDInitializer
{
// the object iid is always generated so that different components
// only can cast to their own objects
// this initializer must be after the definition of FObject::iid, otherwise
// the default constructor of FUID will clear the generated iid
FObjectIIDInitializer () { const_cast<FUID&> (FObject::iid).generate (); }
} gFObjectIidInitializer;
//------------------------------------------------------------------------
FObject::~FObject ()
{
#if SMTG_DEPENDENCY_COUNT && DEVELOPMENT
static bool localNeverDebugger = false;
#endif
#if DEVELOPMENT
if (refCount > 1)
FDebugPrint ("Refcount is %d when trying to delete %s\n", refCount, isA ());
#endif
#if SMTG_DEPENDENCY_COUNT
#if SMTG_DEPENDENCY_CHECK_LEVEL >= 1
if (gUpdateHandler)
{
#if DEVELOPMENT
SMTG_ASSERT (dependencyCount == 0 || localNeverDebugger);
#endif // DEVELOPMENT
}
#endif
#endif // SMTG_DEPENDENCY_COUNT
#if SMTG_VALIDATE_DEPENDENCY_COUNT
if (!gUpdateHandler || gUpdateHandler != UpdateHandler::instance (false))
return;
auto updateHandler = UpdateHandler::instance ();
if (!updateHandler || updateHandler == this)
return;
SMTG_ASSERT ((updateHandler->checkDeferred (this) == false || localNeverDebugger) &&
"'this' has scheduled a deferUpdate that was not yet delivered");
if (updateHandler->hasDependencies (this))
{
SMTG_ASSERT (
(false || localNeverDebugger) &&
"Another object is still dependent on 'this'. This leads to zombie entries in the dependency map that can later crash.");
FDebugPrint ("Object still has dependencies %x %s\n", this, this->isA ());
updateHandler->printForObject (this);
}
#endif // SMTG_VALIDATE_DEPENDENCY_COUNT
}
//------------------------------------------------------------------------
uint32 PLUGIN_API FObject::addRef ()
{
#if DEVELOPMENT
if (gFObjectTracker)
{
gFObjectTracker (this, true);
}
#endif
return FUnknownPrivate::atomicAdd (refCount, 1);
}
//------------------------------------------------------------------------
uint32 PLUGIN_API FObject::release ()
{
#if DEVELOPMENT
if (gFObjectTracker)
{
gFObjectTracker (this, false);
}
#endif
auto rc = FUnknownPrivate::atomicAdd (refCount, -1);
if (rc == 0)
{
refCount = -1000;
delete this;
return 0;
}
return rc;
}
//------------------------------------------------------------------------
tresult PLUGIN_API FObject::queryInterface (const TUID _iid, void** obj)
{
QUERY_INTERFACE (_iid, obj, FUnknown::iid, FUnknown)
QUERY_INTERFACE (_iid, obj, IDependent::iid, IDependent)
QUERY_INTERFACE (_iid, obj, FObject::iid, FObject)
*obj = nullptr;
return kNoInterface;
}
//------------------------------------------------------------------------
void FObject::addDependent (IDependent* dep)
{
if (!gUpdateHandler)
return;
gUpdateHandler->addDependent (unknownCast (), dep);
#if SMTG_DEPENDENCY_COUNT
dependencyCount++;
#endif
}
//------------------------------------------------------------------------
void FObject::removeDependent (IDependent* dep)
{
#if SMTG_DEPENDENCY_COUNT && DEVELOPMENT
static bool localNeverDebugger = false;
#endif
if (!gUpdateHandler)
return;
#if SMTG_DEPENDENCY_COUNT
if (gUpdateHandler != UpdateHandler::instance (false))
{
gUpdateHandler->removeDependent (unknownCast (), dep);
dependencyCount--;
return;
}
#if SMTG_DEPENDENCY_CHECK_LEVEL > 1
SMTG_ASSERT ((dependencyCount > 0 || localNeverDebugger) &&
"All dependencies have already been removed - mmichaelis 7/2021");
#endif
size_t removeCount;
UpdateHandler::instance ()->removeDependent (unknownCast (), dep, removeCount);
if (removeCount == 0)
{
#if SMTG_DEPENDENCY_CHECK_LEVEL > 1
SMTG_ASSERT (localNeverDebugger && "No dependency to remove - ygrabit 8/2021");
#endif
}
else
{
SMTG_ASSERT ((removeCount == 1 || localNeverDebugger) &&
"Duplicated dependencies established - mmichaelis 7/2021");
}
dependencyCount -= (int16)removeCount;
#else
gUpdateHandler->removeDependent (unknownCast (), dep);
#endif // SMTG_DEPENDENCY_COUNT
}
//------------------------------------------------------------------------
void FObject::changed (int32 msg)
{
if (gUpdateHandler)
gUpdateHandler->triggerUpdates (unknownCast (), msg);
else
updateDone (msg);
}
//------------------------------------------------------------------------
void FObject::deferUpdate (int32 msg)
{
if (gUpdateHandler)
gUpdateHandler->deferUpdates (unknownCast (), msg);
else
updateDone (msg);
}
//------------------------------------------------------------------------
/** Automatic creation and destruction of singleton instances. */
//------------------------------------------------------------------------
namespace Singleton {
using ObjectVector = std::vector<FObject**>;
ObjectVector* singletonInstances = nullptr;
bool singletonsTerminated = false;
Steinberg::Base::Thread::FLock* singletonsLock;
bool isTerminated ()
{
return singletonsTerminated;
}
void lockRegister ()
{
if (!singletonsLock) // assume first call not from multiple threads
singletonsLock = NEW Steinberg::Base::Thread::FLock;
singletonsLock->lock ();
}
void unlockRegister ()
{
singletonsLock->unlock ();
}
void registerInstance (FObject** o)
{
SMTG_ASSERT (singletonsTerminated == false)
if (singletonsTerminated == false)
{
if (singletonInstances == nullptr)
singletonInstances = NEW std::vector<FObject**>;
singletonInstances->push_back (o);
}
}
struct Deleter
{
~Deleter ()
{
singletonsTerminated = true;
if (singletonInstances)
{
for (Steinberg::FObject** obj : *singletonInstances)
{
(*obj)->release ();
*obj = nullptr;
obj = nullptr;
}
delete singletonInstances;
singletonInstances = nullptr;
}
delete singletonsLock;
singletonsLock = nullptr;
}
} deleter;
}
//------------------------------------------------------------------------
} // namespace Steinberg