@@ -34,6 +34,7 @@ | |||||
#include "PluginContext.hpp" | #include "PluginContext.hpp" | ||||
#include "WindowParameters.hpp" | #include "WindowParameters.hpp" | ||||
#include "extra/Base64.hpp" | #include "extra/Base64.hpp" | ||||
#include "extra/SharedResourcePointer.hpp" | |||||
namespace rack { | namespace rack { | ||||
namespace plugin { | namespace plugin { | ||||
@@ -47,7 +48,7 @@ START_NAMESPACE_DISTRHO | |||||
// ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
struct Initializer { | struct Initializer { | ||||
Initializer() | |||||
Initializer(const CardinalBasePlugin* const plugin) | |||||
{ | { | ||||
using namespace rack; | using namespace rack; | ||||
@@ -60,10 +61,12 @@ struct Initializer { | |||||
settings::skipLoadOnLaunch = true; | settings::skipLoadOnLaunch = true; | ||||
settings::showTipsOnLaunch = false; | settings::showTipsOnLaunch = false; | ||||
settings::threadCount = 1; | settings::threadCount = 1; | ||||
system::init(); | system::init(); | ||||
asset::init(); | asset::init(); | ||||
logger::init(); | logger::init(); | ||||
random::init(); | random::init(); | ||||
ui::init(); | |||||
// Make system dir point to source code location. It is good enough for now | // 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"; | asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack"; | ||||
@@ -73,7 +76,6 @@ struct Initializer { | |||||
INFO("%s", system::getOperatingSystemInfo().c_str()); | INFO("%s", system::getOperatingSystemInfo().c_str()); | ||||
INFO("System directory: %s", asset::systemDir.c_str()); | INFO("System directory: %s", asset::systemDir.c_str()); | ||||
INFO("User directory: %s", asset::userDir.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 | // Check existence of the system res/ directory | ||||
const std::string resDir = asset::system("res"); | const std::string resDir = asset::system("res"); | ||||
@@ -83,42 +85,54 @@ struct Initializer { | |||||
"Make sure Cardinal was downloaded and installed correctly.", resDir.c_str()); | "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); | rack::audio::addDriver(0, new CardinalAudioDriver); | ||||
INFO("Initializing plugins"); | |||||
plugin::initStaticPlugins(); | plugin::initStaticPlugins(); | ||||
ui::init(); | |||||
} | } | ||||
~Initializer() | ~Initializer() | ||||
{ | { | ||||
using namespace rack; | using namespace rack; | ||||
ui::destroy(); // does nothing | |||||
INFO("Destroying plugins"); | INFO("Destroying plugins"); | ||||
plugin::destroyStaticPlugins(); | plugin::destroyStaticPlugins(); | ||||
INFO("Destroying MIDI devices"); | |||||
midi::destroy(); | midi::destroy(); | ||||
INFO("Destroying audio devices"); | |||||
audio::destroy(); | audio::destroy(); | ||||
INFO("Destroying logger"); | INFO("Destroying logger"); | ||||
logger::destroy(); | 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 | class CardinalPlugin : public CardinalBasePlugin | ||||
{ | { | ||||
SharedResourcePointer<Initializer> fInitializer; | |||||
float* fAudioBufferIn; | float* fAudioBufferIn; | ||||
float* fAudioBufferOut; | float* fAudioBufferOut; | ||||
std::string fAutosavePath; | std::string fAutosavePath; | ||||
@@ -130,24 +144,10 @@ class CardinalPlugin : public CardinalBasePlugin | |||||
float fWindowParameters[kWindowParameterCount]; | 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: | public: | ||||
CardinalPlugin() | CardinalPlugin() | ||||
: CardinalBasePlugin(kModuleParameters + kWindowParameterCount, 0, 1), | : CardinalBasePlugin(kModuleParameters + kWindowParameterCount, 0, 1), | ||||
fInitializer(this), | |||||
fAudioBufferIn(nullptr), | fAudioBufferIn(nullptr), | ||||
fAudioBufferOut(nullptr), | fAudioBufferOut(nullptr), | ||||
fIsActive(false), | fIsActive(false), | ||||
@@ -517,7 +517,6 @@ CardinalPluginContext* getRackContextFromPlugin(void* const ptr) | |||||
Plugin* createPlugin() | Plugin* createPlugin() | ||||
{ | { | ||||
getInitializerInstance(); | |||||
return new CardinalPlugin(); | 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 |