| @@ -74,7 +74,7 @@ public: | |||
| thread has it locked for writing, then this will fail and return false. | |||
| @returns true if the lock is successfully gained. | |||
| @see exitRead, ScopedReadLock | |||
| @see exitRead, ScopedTryReadLock | |||
| */ | |||
| bool tryEnterRead() const noexcept; | |||
| @@ -106,7 +106,7 @@ public: | |||
| to obtain the lock. | |||
| @returns true if the lock is successfully gained. | |||
| @see enterWrite | |||
| @see enterWrite, ScopedTryWriteLock | |||
| */ | |||
| bool tryEnterWrite() const noexcept; | |||
| @@ -81,4 +81,90 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE (ScopedReadLock) | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Automatically locks and unlocks a ReadWriteLock object. | |||
| Use one of these as a local variable to control access to a ReadWriteLock. | |||
| e.g. @code | |||
| ReadWriteLock myLock; | |||
| for (;;) | |||
| { | |||
| const ScopedTryReadLock myScopedTryLock (myLock); | |||
| // Unlike using a ScopedReadLock, 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 a write lock has already been issued.. | |||
| } | |||
| // myLock gets unlocked here (if it was locked). | |||
| } | |||
| @endcode | |||
| @see ReadWriteLock, ScopedTryWriteLock | |||
| @tags{Core} | |||
| */ | |||
| class JUCE_API ScopedTryReadLock | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a ScopedTryReadLock and calls ReadWriteLock::tryEnterRead() as soon as it is | |||
| created. When the ScopedTryReadLock object is destructed, the ReadWriteLock will be unlocked | |||
| (if it was successfully acquired). | |||
| Make sure this object is created and destructed 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. | |||
| */ | |||
| explicit ScopedTryReadLock (ReadWriteLock& lockIn) | |||
| : ScopedTryReadLock (lockIn, true) {} | |||
| /** Creates a ScopedTryReadLock. | |||
| If acquireLockOnInitialisation is true then as soon as it is created, this will call | |||
| ReadWriteLock::tryEnterRead(), and when the ScopedTryReadLock object is destructed, the | |||
| ReadWriteLock will be unlocked (if it was successfully acquired). | |||
| Make sure this object is created and destructed 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. | |||
| */ | |||
| ScopedTryReadLock (ReadWriteLock& lockIn, bool acquireLockOnInitialisation) noexcept | |||
| : lock (lockIn), lockWasSuccessful (acquireLockOnInitialisation && lock.tryEnterRead()) {} | |||
| /** Destructor. | |||
| The ReadWriteLock's exitRead() method will be called when the destructor is called. | |||
| Make sure this object is created and destructed by the same thread, otherwise there are no | |||
| guarantees what will happen! | |||
| */ | |||
| ~ScopedTryReadLock() noexcept { if (lockWasSuccessful) lock.exitRead(); } | |||
| /** Returns true if the mutex was successfully locked. */ | |||
| bool isLocked() const noexcept { return lockWasSuccessful; } | |||
| /** Retry gaining the lock by calling tryEnter on the underlying lock. */ | |||
| bool retryLock() noexcept { return lockWasSuccessful = lock.tryEnterRead(); } | |||
| private: | |||
| //============================================================================== | |||
| ReadWriteLock& lock; | |||
| bool lockWasSuccessful; | |||
| JUCE_DECLARE_NON_COPYABLE (ScopedTryReadLock) | |||
| }; | |||
| } // namespace juce | |||
| @@ -81,4 +81,90 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE (ScopedWriteLock) | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Automatically locks and unlocks a ReadWriteLock object. | |||
| Use one of these as a local variable to control access to a ReadWriteLock. | |||
| e.g. @code | |||
| ReadWriteLock myLock; | |||
| for (;;) | |||
| { | |||
| const ScopedTryWriteLock myScopedTryLock (myLock); | |||
| // Unlike using a ScopedWriteLock, 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 some other thread has already locked the object.. | |||
| } | |||
| // myLock gets unlocked here (if it was locked). | |||
| } | |||
| @endcode | |||
| @see ReadWriteLock, ScopedTryWriteLock | |||
| @tags{Core} | |||
| */ | |||
| class JUCE_API ScopedTryWriteLock | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a ScopedTryWriteLock and calls ReadWriteLock::tryEnterWrite() immediately. | |||
| When the ScopedTryWriteLock object is destructed, the ReadWriteLock will be unlocked | |||
| (if it was successfully acquired). | |||
| Make sure this object is created and destructed 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. | |||
| */ | |||
| ScopedTryWriteLock (ReadWriteLock& lockIn) noexcept | |||
| : ScopedTryWriteLock (lockIn, true) {} | |||
| /** Creates a ScopedTryWriteLock. | |||
| If acquireLockOnInitialisation is true then as soon as it is created, this will call | |||
| ReadWriteLock::tryEnterWrite(), and when the ScopedTryWriteLock object is destructed, the | |||
| ReadWriteLock will be unlocked (if it was successfully acquired). | |||
| Make sure this object is created and destructed 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. | |||
| */ | |||
| ScopedTryWriteLock (ReadWriteLock& lockIn, bool acquireLockOnInitialisation) noexcept | |||
| : lock (lockIn), lockWasSuccessful (acquireLockOnInitialisation && lock.tryEnterWrite()) {} | |||
| /** Destructor. | |||
| The ReadWriteLock's exitWrite() method will be called when the destructor is called. | |||
| Make sure this object is created and destructed by the same thread, | |||
| otherwise there are no guarantees what will happen! | |||
| */ | |||
| ~ScopedTryWriteLock() noexcept { if (lockWasSuccessful) lock.exitWrite(); } | |||
| /** Returns true if the mutex was successfully locked. */ | |||
| bool isLocked() const noexcept { return lockWasSuccessful; } | |||
| /** Retry gaining the lock by calling tryEnter on the underlying lock. */ | |||
| bool retryLock() noexcept { return lockWasSuccessful = lock.tryEnterWrite(); } | |||
| private: | |||
| //============================================================================== | |||
| ReadWriteLock& lock; | |||
| bool lockWasSuccessful; | |||
| JUCE_DECLARE_NON_COPYABLE (ScopedTryWriteLock) | |||
| }; | |||
| } | |||