@@ -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 |