Import water code for SharedResourcePointer, use it for init
This commit is contained in:
parent
05df01ef3a
commit
74206ec01a
|
@ -34,6 +34,7 @@
|
|||
#include "PluginContext.hpp"
|
||||
#include "WindowParameters.hpp"
|
||||
#include "extra/Base64.hpp"
|
||||
#include "extra/SharedResourcePointer.hpp"
|
||||
|
||||
namespace rack {
|
||||
namespace plugin {
|
||||
|
@ -47,7 +48,7 @@ START_NAMESPACE_DISTRHO
|
|||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
struct Initializer {
|
||||
Initializer()
|
||||
Initializer(const CardinalBasePlugin* const plugin)
|
||||
{
|
||||
using namespace rack;
|
||||
|
||||
|
@ -60,10 +61,12 @@ struct Initializer {
|
|||
settings::skipLoadOnLaunch = true;
|
||||
settings::showTipsOnLaunch = false;
|
||||
settings::threadCount = 1;
|
||||
|
||||
system::init();
|
||||
asset::init();
|
||||
logger::init();
|
||||
random::init();
|
||||
ui::init();
|
||||
|
||||
// Make system dir point to source code location. It is good enough for now
|
||||
asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack";
|
||||
|
@ -73,7 +76,6 @@ struct Initializer {
|
|||
INFO("%s", system::getOperatingSystemInfo().c_str());
|
||||
INFO("System directory: %s", asset::systemDir.c_str());
|
||||
INFO("User directory: %s", asset::userDir.c_str());
|
||||
INFO("System time: %s", string::formatTimeISO(system::getUnixTime()).c_str());
|
||||
|
||||
// Check existence of the system res/ directory
|
||||
const std::string resDir = asset::system("res");
|
||||
|
@ -83,42 +85,54 @@ struct Initializer {
|
|||
"Make sure Cardinal was downloaded and installed correctly.", resDir.c_str());
|
||||
}
|
||||
|
||||
INFO("Initializing environment");
|
||||
audio::init(); // does nothing
|
||||
midi::init(); // does nothing
|
||||
|
||||
INFO("Initializing audio driver");
|
||||
rack::audio::addDriver(0, new CardinalAudioDriver);
|
||||
|
||||
INFO("Initializing plugins");
|
||||
plugin::initStaticPlugins();
|
||||
ui::init();
|
||||
}
|
||||
|
||||
~Initializer()
|
||||
{
|
||||
using namespace rack;
|
||||
|
||||
ui::destroy(); // does nothing
|
||||
|
||||
INFO("Destroying plugins");
|
||||
plugin::destroyStaticPlugins();
|
||||
|
||||
INFO("Destroying MIDI devices");
|
||||
midi::destroy();
|
||||
|
||||
INFO("Destroying audio devices");
|
||||
audio::destroy();
|
||||
|
||||
INFO("Destroying logger");
|
||||
logger::destroy();
|
||||
}
|
||||
};
|
||||
|
||||
static const Initializer& getInitializerInstance()
|
||||
{
|
||||
static const Initializer init;
|
||||
return init;
|
||||
}
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
struct ScopedContext {
|
||||
const MutexLocker cml;
|
||||
|
||||
ScopedContext(const CardinalBasePlugin* const plugin)
|
||||
: cml(plugin->context->mutex)
|
||||
{
|
||||
rack::contextSet(plugin->context);
|
||||
}
|
||||
|
||||
~ScopedContext()
|
||||
{
|
||||
rack::contextSet(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
class CardinalPlugin : public CardinalBasePlugin
|
||||
{
|
||||
SharedResourcePointer<Initializer> fInitializer;
|
||||
|
||||
float* fAudioBufferIn;
|
||||
float* fAudioBufferOut;
|
||||
std::string fAutosavePath;
|
||||
|
@ -130,24 +144,10 @@ class CardinalPlugin : public CardinalBasePlugin
|
|||
|
||||
float fWindowParameters[kWindowParameterCount];
|
||||
|
||||
struct ScopedContext {
|
||||
const MutexLocker cml;
|
||||
|
||||
ScopedContext(const CardinalPlugin* const plugin)
|
||||
: cml(plugin->context->mutex)
|
||||
{
|
||||
rack::contextSet(plugin->context);
|
||||
}
|
||||
|
||||
~ScopedContext()
|
||||
{
|
||||
rack::contextSet(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
CardinalPlugin()
|
||||
: CardinalBasePlugin(kModuleParameters + kWindowParameterCount, 0, 1),
|
||||
fInitializer(this),
|
||||
fAudioBufferIn(nullptr),
|
||||
fAudioBufferOut(nullptr),
|
||||
fIsActive(false),
|
||||
|
@ -517,7 +517,6 @@ CardinalPluginContext* getRackContextFromPlugin(void* const ptr)
|
|||
|
||||
Plugin* createPlugin()
|
||||
{
|
||||
getInitializerInstance();
|
||||
return new CardinalPlugin();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the Water library.
|
||||
Copyright (c) 2016 ROLI Ltd.
|
||||
Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
|
||||
|
||||
Permission is granted to use this software under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license/
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef WATER_ATOMIC_HPP_INCLUDED
|
||||
#define WATER_ATOMIC_HPP_INCLUDED
|
||||
|
||||
#include "DistrhoUtils.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Simple class to hold a primitive value and perform atomic operations on it.
|
||||
|
||||
The type used must be a 32 or 64 bit primitive, like an int, pointer, etc.
|
||||
There are methods to perform most of the basic atomic operations.
|
||||
*/
|
||||
template <typename Type>
|
||||
class Atomic
|
||||
{
|
||||
public:
|
||||
/** Creates a new value, initialised to zero. */
|
||||
inline Atomic() noexcept
|
||||
: value (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a new value, with a given initial value. */
|
||||
inline explicit Atomic (const Type initialValue) noexcept
|
||||
: value (initialValue)
|
||||
{
|
||||
}
|
||||
|
||||
/** Copies another value (atomically). */
|
||||
inline Atomic (const Atomic& other) noexcept
|
||||
: value (other.get())
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
inline ~Atomic() noexcept
|
||||
{
|
||||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
|
||||
// This class can only be used for types which are 32 or 64 bits in size.
|
||||
static_assert (sizeof (Type) == 4 || sizeof (Type) == 8, "Only for 32 or 64 bits");
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Atomically reads and returns the current value. */
|
||||
Type get() const noexcept;
|
||||
|
||||
/** Copies another value onto this one (atomically). */
|
||||
inline Atomic& operator= (const Atomic& other) noexcept { exchange (other.get()); return *this; }
|
||||
|
||||
/** Copies another value onto this one (atomically). */
|
||||
inline Atomic& operator= (const Type newValue) noexcept { exchange (newValue); return *this; }
|
||||
|
||||
/** Atomically sets the current value. */
|
||||
void set (Type newValue) noexcept { exchange (newValue); }
|
||||
|
||||
/** Atomically sets the current value, returning the value that was replaced. */
|
||||
Type exchange (Type value) noexcept;
|
||||
|
||||
/** Atomically adds a number to this value, returning the new value. */
|
||||
Type operator+= (Type amountToAdd) noexcept;
|
||||
|
||||
/** Atomically subtracts a number from this value, returning the new value. */
|
||||
Type operator-= (Type amountToSubtract) noexcept;
|
||||
|
||||
/** Atomically increments this value, returning the new value. */
|
||||
Type operator++() noexcept;
|
||||
|
||||
/** Atomically decrements this value, returning the new value. */
|
||||
Type operator--() noexcept;
|
||||
|
||||
/** Atomically compares this value with a target value, and if it is equal, sets
|
||||
this to be equal to a new value.
|
||||
|
||||
This operation is the atomic equivalent of doing this:
|
||||
@code
|
||||
bool compareAndSetBool (Type newValue, Type valueToCompare)
|
||||
{
|
||||
if (get() == valueToCompare)
|
||||
{
|
||||
set (newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@endcode
|
||||
|
||||
@returns true if the comparison was true and the value was replaced; false if
|
||||
the comparison failed and the value was left unchanged.
|
||||
@see compareAndSetValue
|
||||
*/
|
||||
bool compareAndSetBool (Type newValue, Type valueToCompare) noexcept;
|
||||
|
||||
/** Atomically compares this value with a target value, and if it is equal, sets
|
||||
this to be equal to a new value.
|
||||
|
||||
This operation is the atomic equivalent of doing this:
|
||||
@code
|
||||
Type compareAndSetValue (Type newValue, Type valueToCompare)
|
||||
{
|
||||
Type oldValue = get();
|
||||
if (oldValue == valueToCompare)
|
||||
set (newValue);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
@endcode
|
||||
|
||||
@returns the old value before it was changed.
|
||||
@see compareAndSetBool
|
||||
*/
|
||||
Type compareAndSetValue (Type newValue, Type valueToCompare) noexcept;
|
||||
|
||||
/** Implements a memory read/write barrier. */
|
||||
static void memoryBarrier() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The raw value that this class operates on.
|
||||
This is exposed publicly in case you need to manipulate it directly
|
||||
for performance reasons.
|
||||
*/
|
||||
#if defined(__LP64__) || defined(_LP64) || defined(__arm64__) || defined(__aarch64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(_M_ARM64)
|
||||
__attribute__ ((aligned (8)))
|
||||
#else
|
||||
__attribute__ ((aligned (4)))
|
||||
#endif
|
||||
volatile Type value;
|
||||
|
||||
private:
|
||||
template <typename Dest, typename Source>
|
||||
static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; }
|
||||
|
||||
static inline Type castFrom32Bit (int32_t value) noexcept { return castTo <Type, int32_t> (value); }
|
||||
static inline Type castFrom64Bit (int64_t value) noexcept { return castTo <Type, int64_t> (value); }
|
||||
static inline Type castFrom32Bit (uint32_t value) noexcept { return castTo <Type, uint32_t> (value); }
|
||||
static inline Type castFrom64Bit (uint64_t value) noexcept { return castTo <Type, uint64_t> (value); }
|
||||
static inline int32_t castTo32Bit (Type value) noexcept { return castTo <int32_t, Type> (value); }
|
||||
static inline int64_t castTo64Bit (Type value) noexcept { return castTo <int64_t, Type> (value); }
|
||||
|
||||
Type operator++ (int); // better to just use pre-increment with atomics..
|
||||
Type operator-- (int);
|
||||
|
||||
/** This templated negate function will negate pointers as well as integers */
|
||||
template <typename ValueType>
|
||||
inline ValueType negateValue (ValueType n) noexcept
|
||||
{
|
||||
return sizeof (ValueType) == 1 ? (ValueType) -(signed char) n
|
||||
: (sizeof (ValueType) == 2 ? (ValueType) -(short) n
|
||||
: (sizeof (ValueType) == 4 ? (ValueType) -(int) n
|
||||
: ((ValueType) -(int64_t) n)));
|
||||
}
|
||||
|
||||
/** This templated negate function will negate pointers as well as integers */
|
||||
template <typename PointerType>
|
||||
inline PointerType* negateValue (PointerType* n) noexcept
|
||||
{
|
||||
return reinterpret_cast<PointerType*> (-reinterpret_cast<intptr_t> (n));
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template<>
|
||||
inline int32_t Atomic<int32_t>::get() const noexcept
|
||||
{
|
||||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
|
||||
static_assert (sizeof (int32_t) == 4, "int32_t must be size 4");
|
||||
#endif
|
||||
return castFrom32Bit ((int32_t) __sync_add_and_fetch (const_cast<volatile int32_t*>(&value), 0));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64_t Atomic<int64_t>::get() const noexcept
|
||||
{
|
||||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
|
||||
static_assert (sizeof (int64_t) == 8, "int64_t must be size 8");
|
||||
#endif
|
||||
return castFrom64Bit ((int64_t) __sync_add_and_fetch (const_cast<volatile int64_t*>(&value), 0));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t Atomic<uint32_t>::get() const noexcept
|
||||
{
|
||||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
|
||||
static_assert (sizeof (uint32_t) == 4, "uint32_t must be size 4");
|
||||
#endif
|
||||
return castFrom32Bit ((uint32_t) __sync_add_and_fetch (const_cast<volatile uint32_t*>(&value), 0));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64_t Atomic<uint64_t>::get() const noexcept
|
||||
{
|
||||
#ifdef DISTRHO_PROPER_CPP11_SUPPORT
|
||||
static_assert (sizeof (uint64_t) == 8, "uint64_t must be size 8");
|
||||
#endif
|
||||
return castFrom64Bit ((uint64_t) __sync_add_and_fetch (const_cast<volatile uint64_t*>(&value), 0));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::exchange (const Type newValue) noexcept
|
||||
{
|
||||
Type currentVal = value;
|
||||
while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; }
|
||||
return currentVal;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator+= (const Type amountToAdd) noexcept
|
||||
{
|
||||
return (Type) __sync_add_and_fetch (&value, amountToAdd);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator-= (const Type amountToSubtract) noexcept
|
||||
{
|
||||
return operator+= (negateValue (amountToSubtract));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator++() noexcept
|
||||
{
|
||||
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1)
|
||||
: (Type) __sync_add_and_fetch ((volatile int64_t*) &value, 1);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::operator--() noexcept
|
||||
{
|
||||
return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1)
|
||||
: (Type) __sync_add_and_fetch ((volatile int64_t*) &value, -1);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline bool Atomic<Type>::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept
|
||||
{
|
||||
return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32_t*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))
|
||||
: __sync_bool_compare_and_swap ((volatile int64_t*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline Type Atomic<Type>::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept
|
||||
{
|
||||
return sizeof (Type) == 4 ? castFrom32Bit ((int32_t) __sync_val_compare_and_swap ((volatile int32_t*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)))
|
||||
: castFrom64Bit ((int64_t) __sync_val_compare_and_swap ((volatile int64_t*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline void Atomic<Type>::memoryBarrier() noexcept
|
||||
{
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
END_NAMESPACE_DISTRHO
|
||||
|
||||
#endif // WATER_ATOMIC_HPP_INCLUDED
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the Water library.
|
||||
Copyright (c) 2016 ROLI Ltd.
|
||||
Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
|
||||
|
||||
Permission is granted to use this software under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license/
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef WATER_REFERENCECOUNTEDOBJECT_HPP_INCLUDED
|
||||
#define WATER_REFERENCECOUNTEDOBJECT_HPP_INCLUDED
|
||||
|
||||
#include "Atomic.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class which provides methods for reference-counting.
|
||||
|
||||
To add reference-counting to a class, derive it from this class, and
|
||||
use the ReferenceCountedObjectPtr class to point to it.
|
||||
|
||||
e.g. @code
|
||||
class MyClass : public ReferenceCountedObject
|
||||
{
|
||||
void foo();
|
||||
|
||||
// This is a neat way of declaring a typedef for a pointer class,
|
||||
// rather than typing out the full templated name each time..
|
||||
typedef ReferenceCountedObjectPtr<MyClass> Ptr;
|
||||
};
|
||||
|
||||
MyClass::Ptr p = new MyClass();
|
||||
MyClass::Ptr p2 = p;
|
||||
p = nullptr;
|
||||
p2->foo();
|
||||
@endcode
|
||||
|
||||
Once a new ReferenceCountedObject has been assigned to a pointer, be
|
||||
careful not to delete the object manually.
|
||||
|
||||
This class uses an Atomic<int> value to hold the reference count, so that it
|
||||
the pointers can be passed between threads safely. For a faster but non-thread-safe
|
||||
version, use SingleThreadedReferenceCountedObject instead.
|
||||
|
||||
@see ReferenceCountedObjectPtr, ReferenceCountedArray, SingleThreadedReferenceCountedObject
|
||||
*/
|
||||
class ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Increments the object's reference count.
|
||||
|
||||
This is done automatically by the smart pointer, but is public just
|
||||
in case it's needed for nefarious purposes.
|
||||
*/
|
||||
void incReferenceCount() noexcept
|
||||
{
|
||||
++refCount;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
If the count gets to zero, the object will be deleted.
|
||||
*/
|
||||
void decReferenceCount() noexcept
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT_RETURN (getReferenceCount() > 0,);
|
||||
|
||||
if (--refCount == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
If the count gets to zero, the object will not be deleted, but this method
|
||||
will return true, allowing the caller to take care of deletion.
|
||||
*/
|
||||
bool decReferenceCountWithoutDeleting() noexcept
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT_RETURN (getReferenceCount() > 0, false);
|
||||
return --refCount == 0;
|
||||
}
|
||||
|
||||
/** Returns the object's current reference count. */
|
||||
int getReferenceCount() const noexcept { return refCount.get(); }
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the reference-counted object (with an initial ref count of zero). */
|
||||
ReferenceCountedObject()
|
||||
: refCount() {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ReferenceCountedObject()
|
||||
{
|
||||
// it's dangerous to delete an object that's still referenced by something else!
|
||||
DISTRHO_SAFE_ASSERT (getReferenceCount() == 0);
|
||||
}
|
||||
|
||||
/** Resets the reference count to zero without deleting the object.
|
||||
You should probably never need to use this!
|
||||
*/
|
||||
void resetReferenceCount() noexcept
|
||||
{
|
||||
refCount = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Atomic<int> refCount;
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE (ReferenceCountedObject)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Adds reference-counting to an object.
|
||||
|
||||
This is effectively a version of the ReferenceCountedObject class, but which
|
||||
uses a non-atomic counter, and so is not thread-safe (but which will be more
|
||||
efficient).
|
||||
For more details on how to use it, see the ReferenceCountedObject class notes.
|
||||
|
||||
@see ReferenceCountedObject, ReferenceCountedObjectPtr, ReferenceCountedArray
|
||||
*/
|
||||
class SingleThreadedReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Increments the object's reference count.
|
||||
|
||||
This is done automatically by the smart pointer, but is public just
|
||||
in case it's needed for nefarious purposes.
|
||||
*/
|
||||
void incReferenceCount() noexcept
|
||||
{
|
||||
++refCount;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
If the count gets to zero, the object will be deleted.
|
||||
*/
|
||||
void decReferenceCount() noexcept
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT_RETURN (getReferenceCount() > 0,);
|
||||
|
||||
if (--refCount == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
/** Decreases the object's reference count.
|
||||
If the count gets to zero, the object will not be deleted, but this method
|
||||
will return true, allowing the caller to take care of deletion.
|
||||
*/
|
||||
bool decReferenceCountWithoutDeleting() noexcept
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT_RETURN (getReferenceCount() > 0, false);
|
||||
return --refCount == 0;
|
||||
}
|
||||
|
||||
/** Returns the object's current reference count. */
|
||||
int getReferenceCount() const noexcept { return refCount; }
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Creates the reference-counted object (with an initial ref count of zero). */
|
||||
SingleThreadedReferenceCountedObject() : refCount (0) {}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SingleThreadedReferenceCountedObject()
|
||||
{
|
||||
// it's dangerous to delete an object that's still referenced by something else!
|
||||
DISTRHO_SAFE_ASSERT_RETURN (getReferenceCount() == 0,);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int refCount;
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE (SingleThreadedReferenceCountedObject)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A smart-pointer class which points to a reference-counted object.
|
||||
|
||||
The template parameter specifies the class of the object you want to point to - the easiest
|
||||
way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject
|
||||
or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable
|
||||
class by implementing a set of methods called incReferenceCount(), decReferenceCount(), and
|
||||
decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods
|
||||
should behave.
|
||||
|
||||
When using this class, you'll probably want to create a typedef to abbreviate the full
|
||||
templated name - e.g.
|
||||
@code
|
||||
struct MyClass : public ReferenceCountedObject
|
||||
{
|
||||
typedef ReferenceCountedObjectPtr<MyClass> Ptr;
|
||||
...
|
||||
@endcode
|
||||
|
||||
@see ReferenceCountedObject, ReferenceCountedObjectArray
|
||||
*/
|
||||
template <class ReferenceCountedObjectClass>
|
||||
class ReferenceCountedObjectPtr
|
||||
{
|
||||
public:
|
||||
/** The class being referenced by this pointer. */
|
||||
typedef ReferenceCountedObjectClass ReferencedType;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a pointer to a null object. */
|
||||
ReferenceCountedObjectPtr() noexcept
|
||||
: referencedObject (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
/** Creates a pointer to an object.
|
||||
This will increment the object's reference-count.
|
||||
*/
|
||||
ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept
|
||||
: referencedObject (refCountedObject)
|
||||
{
|
||||
incIfNotNull (refCountedObject);
|
||||
}
|
||||
|
||||
/** Copies another pointer.
|
||||
This will increment the object's reference-count.
|
||||
*/
|
||||
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept
|
||||
: referencedObject (other.referencedObject)
|
||||
{
|
||||
incIfNotNull (referencedObject);
|
||||
}
|
||||
|
||||
/** Copies another pointer.
|
||||
This will increment the object's reference-count (if it is non-null).
|
||||
*/
|
||||
template <typename Convertible>
|
||||
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<Convertible>& other) noexcept
|
||||
: referencedObject (static_cast<ReferencedType*> (other.get()))
|
||||
{
|
||||
incIfNotNull (referencedObject);
|
||||
}
|
||||
|
||||
/** Changes this pointer to point at a different object.
|
||||
The reference count of the old object is decremented, and it might be
|
||||
deleted if it hits zero. The new object's count is incremented.
|
||||
*/
|
||||
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other)
|
||||
{
|
||||
return operator= (other.referencedObject);
|
||||
}
|
||||
|
||||
/** Changes this pointer to point at a different object.
|
||||
The reference count of the old object is decremented, and it might be
|
||||
deleted if it hits zero. The new object's count is incremented.
|
||||
*/
|
||||
template <typename Convertible>
|
||||
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr<Convertible>& other)
|
||||
{
|
||||
return operator= (static_cast<ReferencedType*> (other.get()));
|
||||
}
|
||||
|
||||
/** Changes this pointer to point at a different object.
|
||||
|
||||
The reference count of the old object is decremented, and it might be
|
||||
deleted if it hits zero. The new object's count is incremented.
|
||||
*/
|
||||
ReferenceCountedObjectPtr& operator= (ReferencedType* const newObject)
|
||||
{
|
||||
if (referencedObject != newObject)
|
||||
{
|
||||
incIfNotNull (newObject);
|
||||
ReferencedType* const oldObject = referencedObject;
|
||||
referencedObject = newObject;
|
||||
decIfNotNull (oldObject);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if WATER_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
/** Takes-over the object from another pointer. */
|
||||
ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept
|
||||
: referencedObject (other.referencedObject)
|
||||
{
|
||||
other.referencedObject = nullptr;
|
||||
}
|
||||
|
||||
/** Takes-over the object from another pointer. */
|
||||
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other)
|
||||
{
|
||||
std::swap (referencedObject, other.referencedObject);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Destructor.
|
||||
This will decrement the object's reference-count, which will cause the
|
||||
object to be deleted when the ref-count hits zero.
|
||||
*/
|
||||
~ReferenceCountedObjectPtr()
|
||||
{
|
||||
ReferencedType* const oldObject = referencedObject; // need to null the pointer before deleting the object
|
||||
referencedObject = nullptr; // in case this ptr is itself deleted as a side-effect
|
||||
decIfNotNull (oldObject); // of the destructor
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be null, of course.
|
||||
*/
|
||||
operator ReferencedType*() const noexcept { return referencedObject; }
|
||||
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be null, of course.
|
||||
*/
|
||||
ReferencedType* get() const noexcept { return referencedObject; }
|
||||
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be null, of course.
|
||||
*/
|
||||
ReferencedType* getObject() const noexcept { return referencedObject; }
|
||||
|
||||
// the -> operator is called on the referenced object
|
||||
ReferencedType* operator->() const noexcept
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT (referencedObject != nullptr); // null pointer method call!
|
||||
return referencedObject;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ReferencedType* referencedObject;
|
||||
|
||||
static void incIfNotNull (ReferencedType* o) noexcept
|
||||
{
|
||||
if (o != nullptr)
|
||||
o->incReferenceCount();
|
||||
}
|
||||
|
||||
static void decIfNotNull (ReferencedType* o) noexcept
|
||||
{
|
||||
if (o != nullptr && o->decReferenceCountWithoutDeleting())
|
||||
delete o;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Compares two ReferenceCountedObjectPtrs. */
|
||||
template <typename ReferenceCountedObjectClass>
|
||||
bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, ReferenceCountedObjectClass* const object2) noexcept
|
||||
{
|
||||
return object1.get() == object2;
|
||||
}
|
||||
|
||||
/** Compares two ReferenceCountedObjectPtrs. */
|
||||
template <typename ReferenceCountedObjectClass>
|
||||
bool operator== (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1.get() == object2.get();
|
||||
}
|
||||
|
||||
/** Compares two ReferenceCountedObjectPtrs. */
|
||||
template <typename ReferenceCountedObjectClass>
|
||||
bool operator== (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1 == object2.get();
|
||||
}
|
||||
|
||||
/** Compares two ReferenceCountedObjectPtrs. */
|
||||
template <typename ReferenceCountedObjectClass>
|
||||
bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectClass* object2) noexcept
|
||||
{
|
||||
return object1.get() != object2;
|
||||
}
|
||||
|
||||
/** Compares two ReferenceCountedObjectPtrs. */
|
||||
template <typename ReferenceCountedObjectClass>
|
||||
bool operator!= (const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1.get() != object2.get();
|
||||
}
|
||||
|
||||
/** Compares two ReferenceCountedObjectPtrs. */
|
||||
template <typename ReferenceCountedObjectClass>
|
||||
bool operator!= (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr<ReferenceCountedObjectClass>& object2) noexcept
|
||||
{
|
||||
return object1 != object2.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // WATER_REFERENCECOUNTEDOBJECT_HPP_INCLUDED
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the Water library.
|
||||
Copyright (c) 2016 ROLI Ltd.
|
||||
Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
|
||||
|
||||
Permission is granted to use this software under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license/
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef WATER_SCOPEDLOCK_HPP_INCLUDED
|
||||
#define WATER_SCOPEDLOCK_HPP_INCLUDED
|
||||
|
||||
#include "DistrhoUtils.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a mutex object.
|
||||
|
||||
Use one of these as a local variable to provide RAII-based locking of a mutex.
|
||||
|
||||
The templated class could be a CriticalSection, SpinLock, or anything else that
|
||||
provides enter() and exit() methods.
|
||||
|
||||
e.g. @code
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const GenericScopedLock<CriticalSection> myScopedLock (myCriticalSection);
|
||||
// myCriticalSection is now locked
|
||||
|
||||
...do some stuff...
|
||||
|
||||
// myCriticalSection gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see GenericScopedUnlock, CriticalSection, SpinLock, ScopedLock, ScopedUnlock
|
||||
*/
|
||||
template <class LockType>
|
||||
class GenericScopedLock
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GenericScopedLock.
|
||||
|
||||
As soon as it is created, this will acquire the lock, and when the GenericScopedLock
|
||||
object is deleted, the lock will be released.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit GenericScopedLock (const LockType& lock) noexcept : lock_ (lock) { lock.enter(); }
|
||||
|
||||
/** Destructor.
|
||||
The lock will be released when the destructor is called.
|
||||
Make sure this object is created and deleted by the same thread, otherwise there are
|
||||
no guarantees what will happen!
|
||||
*/
|
||||
inline ~GenericScopedLock() noexcept { lock_.exit(); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const LockType& lock_;
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE (GenericScopedLock)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically unlocks and re-locks a mutex object.
|
||||
|
||||
This is the reverse of a GenericScopedLock object - instead of locking the mutex
|
||||
for the lifetime of this object, it unlocks it.
|
||||
|
||||
Make sure you don't try to unlock mutexes that aren't actually locked!
|
||||
|
||||
e.g. @code
|
||||
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const GenericScopedLock<CriticalSection> myScopedLock (myCriticalSection);
|
||||
// myCriticalSection is now locked
|
||||
|
||||
... do some stuff with it locked ..
|
||||
|
||||
while (xyz)
|
||||
{
|
||||
... do some stuff with it locked ..
|
||||
|
||||
const GenericScopedUnlock<CriticalSection> unlocker (myCriticalSection);
|
||||
|
||||
// myCriticalSection is now unlocked for the remainder of this block,
|
||||
// and re-locked at the end.
|
||||
|
||||
...do some stuff with it unlocked ...
|
||||
}
|
||||
|
||||
// myCriticalSection gets unlocked here.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see GenericScopedLock, CriticalSection, ScopedLock, ScopedUnlock
|
||||
*/
|
||||
template <class LockType>
|
||||
class GenericScopedUnlock
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GenericScopedUnlock.
|
||||
|
||||
As soon as it is created, this will unlock the CriticalSection, and
|
||||
when the ScopedLock object is deleted, the CriticalSection will
|
||||
be re-locked.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit GenericScopedUnlock (const LockType& lock) noexcept : lock_ (lock) { lock.exit(); }
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The CriticalSection will be unlocked when the destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~GenericScopedUnlock() noexcept { lock_.enter(); }
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const LockType& lock_;
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE (GenericScopedUnlock)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Automatically locks and unlocks a mutex object.
|
||||
|
||||
Use one of these as a local variable to provide RAII-based locking of a mutex.
|
||||
|
||||
The templated class could be a CriticalSection, SpinLock, or anything else that
|
||||
provides enter() and exit() methods.
|
||||
|
||||
e.g. @code
|
||||
|
||||
CriticalSection myCriticalSection;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const GenericScopedTryLock<CriticalSection> myScopedTryLock (myCriticalSection);
|
||||
|
||||
// Unlike using a ScopedLock, this may fail to actually get the lock, so you
|
||||
// should test this with the isLocked() method before doing your thread-unsafe
|
||||
// action..
|
||||
if (myScopedTryLock.isLocked())
|
||||
{
|
||||
...do some stuff...
|
||||
}
|
||||
else
|
||||
{
|
||||
..our attempt at locking failed because another thread had already locked it..
|
||||
}
|
||||
|
||||
// myCriticalSection gets unlocked here (if it was locked)
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see CriticalSection::tryEnter, GenericScopedLock, GenericScopedUnlock
|
||||
*/
|
||||
template <class LockType>
|
||||
class GenericScopedTryLock
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a GenericScopedTryLock.
|
||||
|
||||
As soon as it is created, this will attempt to acquire the lock, and when the
|
||||
GenericScopedTryLock is deleted, the lock will be released (if the lock was
|
||||
successfully acquired).
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen! Best just to use it
|
||||
as a local stack object, rather than creating one with the new() operator.
|
||||
*/
|
||||
inline explicit GenericScopedTryLock (const LockType& lock) noexcept
|
||||
: lock_ (lock), lockWasSuccessful (lock.tryEnter()) {}
|
||||
|
||||
/** Destructor.
|
||||
|
||||
The mutex will be unlocked (if it had been successfully locked) when the
|
||||
destructor is called.
|
||||
|
||||
Make sure this object is created and deleted by the same thread,
|
||||
otherwise there are no guarantees what will happen!
|
||||
*/
|
||||
inline ~GenericScopedTryLock() noexcept { if (lockWasSuccessful) lock_.exit(); }
|
||||
|
||||
/** Returns true if the mutex was successfully locked. */
|
||||
bool isLocked() const noexcept { return lockWasSuccessful; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
const LockType& lock_;
|
||||
const bool lockWasSuccessful;
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE (GenericScopedTryLock)
|
||||
};
|
||||
|
||||
END_NAMESPACE_DISTRHO
|
||||
|
||||
#endif // WATER_SCOPEDLOCK_HPP_INCLUDED
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the Water library.
|
||||
Copyright (c) 2016 ROLI Ltd.
|
||||
Copyright (C) 2017-2019 Filipe Coelho <falktx@falktx.com>
|
||||
|
||||
Permission is granted to use this software under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license/
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef WATER_SHAREDRESOURCEPOINTER_HPP_INCLUDED
|
||||
#define WATER_SHAREDRESOURCEPOINTER_HPP_INCLUDED
|
||||
|
||||
#include "ReferenceCountedObject.hpp"
|
||||
#include "SpinLock.hpp"
|
||||
#include "extra/ScopedPointer.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A smart-pointer that automatically creates and manages the lifetime of a
|
||||
shared static instance of a class.
|
||||
|
||||
The SharedObjectType template type indicates the class to use for the shared
|
||||
object - the only requirements on this class are that it must have a public
|
||||
default constructor and destructor.
|
||||
|
||||
The SharedResourcePointer offers a pattern that differs from using a singleton or
|
||||
static instance of an object, because it uses reference-counting to make sure that
|
||||
the underlying shared object is automatically created/destroyed according to the
|
||||
number of SharedResourcePointer objects that exist. When the last one is deleted,
|
||||
the underlying object is also immediately destroyed. This allows you to use scoping
|
||||
to manage the lifetime of a shared resource.
|
||||
|
||||
Note: the construction/deletion of the shared object must not involve any
|
||||
code that makes recursive calls to a SharedResourcePointer, or you'll cause
|
||||
a deadlock.
|
||||
|
||||
Example:
|
||||
@code
|
||||
// An example of a class that contains the shared data you want to use.
|
||||
struct MySharedData
|
||||
{
|
||||
// There's no need to ever create an instance of this class directly yourself,
|
||||
// but it does need a public constructor that does the initialisation.
|
||||
MySharedData()
|
||||
{
|
||||
sharedStuff = generateHeavyweightStuff();
|
||||
}
|
||||
|
||||
Array<SomeKindOfData> sharedStuff;
|
||||
};
|
||||
|
||||
struct DataUserClass
|
||||
{
|
||||
DataUserClass()
|
||||
{
|
||||
// Multiple instances of the DataUserClass will all have the same
|
||||
// shared common instance of MySharedData referenced by their sharedData
|
||||
// member variables.
|
||||
useSharedStuff (sharedData->sharedStuff);
|
||||
}
|
||||
|
||||
// By keeping this pointer as a member variable, the shared resource
|
||||
// is guaranteed to be available for as long as the DataUserClass object.
|
||||
SharedResourcePointer<MySharedData> sharedData;
|
||||
};
|
||||
|
||||
@endcode
|
||||
*/
|
||||
template <typename SharedObjectType>
|
||||
class SharedResourcePointer
|
||||
{
|
||||
public:
|
||||
/** Creates an instance of the shared object.
|
||||
If other SharedResourcePointer objects for this type already exist, then
|
||||
this one will simply point to the same shared object that they are already
|
||||
using. Otherwise, if this is the first SharedResourcePointer to be created,
|
||||
then a shared object will be created automatically.
|
||||
*/
|
||||
SharedResourcePointer()
|
||||
: sharedObject(nullptr)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SharedResourcePointer(const T* const variant)
|
||||
: sharedObject(nullptr)
|
||||
{
|
||||
initialise_variant<T>(variant);
|
||||
}
|
||||
|
||||
SharedResourcePointer (const SharedResourcePointer&)
|
||||
: sharedObject(nullptr)
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
If no other SharedResourcePointer objects exist, this will also delete
|
||||
the shared object to which it refers.
|
||||
*/
|
||||
~SharedResourcePointer()
|
||||
{
|
||||
SharedObjectHolder& holder = getSharedObjectHolder();
|
||||
const SpinLock::ScopedLockType sl (holder.lock);
|
||||
|
||||
if (--(holder.refCount) == 0)
|
||||
holder.sharedInstance = nullptr;
|
||||
}
|
||||
|
||||
/** Returns the shared object. */
|
||||
operator SharedObjectType*() const noexcept { return sharedObject; }
|
||||
|
||||
/** Returns the shared object. */
|
||||
SharedObjectType& get() const noexcept { return *sharedObject; }
|
||||
|
||||
/** Returns the object that this pointer references.
|
||||
The pointer returned may be a nullptr, of course.
|
||||
*/
|
||||
SharedObjectType& getObject() const noexcept { return *sharedObject; }
|
||||
SharedObjectType* getPointer() const noexcept { return sharedObject; }
|
||||
|
||||
SharedObjectType* operator->() const noexcept { return sharedObject; }
|
||||
|
||||
private:
|
||||
struct SharedObjectHolder : public ReferenceCountedObject
|
||||
{
|
||||
SpinLock lock;
|
||||
ScopedPointer<SharedObjectType> sharedInstance;
|
||||
int refCount;
|
||||
};
|
||||
|
||||
static SharedObjectHolder& getSharedObjectHolder() noexcept
|
||||
{
|
||||
static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { nullptr };
|
||||
return *reinterpret_cast<SharedObjectHolder*> (holder);
|
||||
}
|
||||
|
||||
SharedObjectType* sharedObject;
|
||||
|
||||
void initialise()
|
||||
{
|
||||
SharedObjectHolder& holder = getSharedObjectHolder();
|
||||
const SpinLock::ScopedLockType sl (holder.lock);
|
||||
|
||||
if (++(holder.refCount) == 1)
|
||||
holder.sharedInstance = new SharedObjectType();
|
||||
|
||||
sharedObject = holder.sharedInstance;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void initialise_variant(const T* const variant)
|
||||
{
|
||||
SharedObjectHolder& holder = getSharedObjectHolder();
|
||||
const SpinLock::ScopedLockType sl (holder.lock);
|
||||
|
||||
if (++(holder.refCount) == 1)
|
||||
holder.sharedInstance = new SharedObjectType(variant);
|
||||
|
||||
sharedObject = holder.sharedInstance;
|
||||
}
|
||||
|
||||
// There's no need to assign to a SharedResourcePointer because every
|
||||
// instance of the class is exactly the same!
|
||||
SharedResourcePointer& operator= (const SharedResourcePointer&) = delete;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // WATER_SHAREDRESOURCEPOINTER_HPP_INCLUDED
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the Water library.
|
||||
Copyright (c) 2016 ROLI Ltd.
|
||||
Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
|
||||
|
||||
Permission is granted to use this software under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license/
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
|
||||
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
OF THIS SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef WATER_SPINLOCK_HPP_INCLUDED
|
||||
#define WATER_SPINLOCK_HPP_INCLUDED
|
||||
|
||||
#include "Atomic.hpp"
|
||||
#include "ScopedLock.hpp"
|
||||
|
||||
START_NAMESPACE_DISTRHO
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple spin-lock class that can be used as a simple, low-overhead mutex for
|
||||
uncontended situations.
|
||||
|
||||
Note that unlike a CriticalSection, this type of lock is not re-entrant, and may
|
||||
be less efficient when used it a highly contended situation, but it's very small and
|
||||
requires almost no initialisation.
|
||||
It's most appropriate for simple situations where you're only going to hold the
|
||||
lock for a very brief time.
|
||||
|
||||
@see CriticalSection
|
||||
*/
|
||||
class SpinLock
|
||||
{
|
||||
public:
|
||||
inline SpinLock() noexcept : lock() {}
|
||||
inline ~SpinLock() noexcept {}
|
||||
|
||||
/** Acquires the lock.
|
||||
This will block until the lock has been successfully acquired by this thread.
|
||||
Note that a SpinLock is NOT re-entrant, and is not smart enough to know whether the
|
||||
caller thread already has the lock - so if a thread tries to acquire a lock that it
|
||||
already holds, this method will never return!
|
||||
|
||||
It's strongly recommended that you never call this method directly - instead use the
|
||||
ScopedLockType class to manage the locking using an RAII pattern instead.
|
||||
*/
|
||||
void enter() const noexcept
|
||||
{
|
||||
if (! tryEnter())
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
if (tryEnter())
|
||||
return;
|
||||
|
||||
while (! tryEnter())
|
||||
{
|
||||
#ifdef DISTRHO_OS_WINDOWS
|
||||
Sleep (0);
|
||||
#else
|
||||
sched_yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Attempts to acquire the lock, returning true if this was successful. */
|
||||
inline bool tryEnter() const noexcept
|
||||
{
|
||||
return lock.compareAndSetBool (1, 0);
|
||||
}
|
||||
|
||||
/** Releases the lock. */
|
||||
inline void exit() const noexcept
|
||||
{
|
||||
DISTRHO_SAFE_ASSERT_RETURN (lock.get() == 1,);
|
||||
lock = 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** Provides the type of scoped lock to use for locking a SpinLock. */
|
||||
typedef GenericScopedLock <SpinLock> ScopedLockType;
|
||||
|
||||
/** Provides the type of scoped unlocker to use with a SpinLock. */
|
||||
typedef GenericScopedUnlock <SpinLock> ScopedUnlockType;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
mutable Atomic<int> lock;
|
||||
|
||||
DISTRHO_DECLARE_NON_COPYABLE (SpinLock)
|
||||
};
|
||||
|
||||
END_NAMESPACE_DISTRHO
|
||||
|
||||
#endif // WATER_SPINLOCK_HPP_INCLUDED
|
Loading…
Reference in New Issue