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