1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 //==============================================================================
27 /**
28     Automatically locks and unlocks a mutex object.
29 
30     Use one of these as a local variable to provide RAII-based locking of a mutex.
31 
32     The templated class could be a CriticalSection, SpinLock, or anything else that
33     provides enter() and exit() methods.
34 
35     e.g. @code
36     CriticalSection myCriticalSection;
37 
38     for (;;)
39     {
40         const GenericScopedLock<CriticalSection> myScopedLock (myCriticalSection);
41         // myCriticalSection is now locked
42 
43         ...do some stuff...
44 
45         // myCriticalSection gets unlocked here.
46     }
47     @endcode
48 
49     @see GenericScopedUnlock, CriticalSection, SpinLock, ScopedLock, ScopedUnlock
50 
51     @tags{Core}
52 */
53 template <class LockType>
54 class GenericScopedLock
55 {
56 public:
57     //==============================================================================
58     /** Creates a GenericScopedLock.
59 
60         As soon as it is created, this will acquire the lock, and when the GenericScopedLock
61         object is deleted, the lock will be released.
62 
63         Make sure this object is created and deleted by the same thread,
64         otherwise there are no guarantees what will happen! Best just to use it
65         as a local stack object, rather than creating one with the new() operator.
66     */
GenericScopedLock(const LockType & lock)67     inline explicit GenericScopedLock (const LockType& lock) noexcept : lock_ (lock)     { lock.enter(); }
68 
69     /** Destructor.
70         The lock will be released when the destructor is called.
71         Make sure this object is created and deleted by the same thread, otherwise there are
72         no guarantees what will happen!
73     */
~GenericScopedLock()74     inline ~GenericScopedLock() noexcept                                                 { lock_.exit(); }
75 
76 private:
77     //==============================================================================
78     const LockType& lock_;
79 
80     JUCE_DECLARE_NON_COPYABLE (GenericScopedLock)
81 };
82 
83 
84 //==============================================================================
85 /**
86     Automatically unlocks and re-locks a mutex object.
87 
88     This is the reverse of a GenericScopedLock object - instead of locking the mutex
89     for the lifetime of this object, it unlocks it.
90 
91     Make sure you don't try to unlock mutexes that aren't actually locked!
92 
93     e.g. @code
94 
95     CriticalSection myCriticalSection;
96 
97     for (;;)
98     {
99         const GenericScopedLock<CriticalSection> myScopedLock (myCriticalSection);
100         // myCriticalSection is now locked
101 
102         ... do some stuff with it locked ..
103 
104         while (xyz)
105         {
106             ... do some stuff with it locked ..
107 
108             const GenericScopedUnlock<CriticalSection> unlocker (myCriticalSection);
109 
110             // myCriticalSection is now unlocked for the remainder of this block,
111             // and re-locked at the end.
112 
113             ...do some stuff with it unlocked ...
114         }
115 
116         // myCriticalSection gets unlocked here.
117     }
118     @endcode
119 
120     @see GenericScopedLock, CriticalSection, ScopedLock, ScopedUnlock
121 
122     @tags{Core}
123 */
124 template <class LockType>
125 class GenericScopedUnlock
126 {
127 public:
128     //==============================================================================
129     /** Creates a GenericScopedUnlock.
130 
131         As soon as it is created, this will unlock the CriticalSection, and
132         when the ScopedLock object is deleted, the CriticalSection will
133         be re-locked.
134 
135         Make sure this object is created and deleted by the same thread,
136         otherwise there are no guarantees what will happen! Best just to use it
137         as a local stack object, rather than creating one with the new() operator.
138     */
GenericScopedUnlock(const LockType & lock)139     inline explicit GenericScopedUnlock (const LockType& lock) noexcept : lock_ (lock)   { lock.exit(); }
140 
141     /** Destructor.
142 
143         The CriticalSection will be unlocked when the destructor is called.
144 
145         Make sure this object is created and deleted by the same thread,
146         otherwise there are no guarantees what will happen!
147     */
~GenericScopedUnlock()148     inline ~GenericScopedUnlock() noexcept                                               { lock_.enter(); }
149 
150 
151 private:
152     //==============================================================================
153     const LockType& lock_;
154 
155     JUCE_DECLARE_NON_COPYABLE (GenericScopedUnlock)
156 };
157 
158 
159 //==============================================================================
160 /**
161     Automatically locks and unlocks a mutex object.
162 
163     Use one of these as a local variable to provide RAII-based locking of a mutex.
164 
165     The templated class could be a CriticalSection, SpinLock, or anything else that
166     provides enter() and exit() methods.
167 
168     e.g. @code
169 
170     CriticalSection myCriticalSection;
171 
172     for (;;)
173     {
174         const GenericScopedTryLock<CriticalSection> myScopedTryLock (myCriticalSection);
175 
176         // Unlike using a ScopedLock, this may fail to actually get the lock, so you
177         // should test this with the isLocked() method before doing your thread-unsafe
178         // action..
179         if (myScopedTryLock.isLocked())
180         {
181            ...do some stuff...
182         }
183         else
184         {
185             ..our attempt at locking failed because another thread had already locked it..
186         }
187 
188         // myCriticalSection gets unlocked here (if it was locked)
189     }
190     @endcode
191 
192     @see CriticalSection::tryEnter, GenericScopedLock, GenericScopedUnlock
193 
194     @tags{Core}
195 */
196 template <class LockType>
197 class GenericScopedTryLock
198 {
199 public:
200     //==============================================================================
201     /** Creates a GenericScopedTryLock.
202 
203         If acquireLockOnInitialisation is true then as soon as this ScopedTryLock
204         is created, it will attempt to acquire the lock with tryEnter.
205 
206         You can retry acquiring the lock by calling retryLock.
207 
208         When GenericScopedTryLock is deleted, the lock will be released (if the lock
209         was successfully acquired).
210 
211         Make sure this object is created and deleted by the same thread,
212         otherwise there are no guarantees what will happen! Best just to use it
213         as a local stack object, rather than creating one with the new() operator.
214 
215         @see retryLock, isLocked
216     */
217     inline explicit GenericScopedTryLock (const LockType& lock, bool acquireLockOnInitialisation = true) noexcept
lock_(lock)218         : lock_ (lock), lockWasSuccessful (acquireLockOnInitialisation && lock.tryEnter()) {}
219 
220     /** Destructor.
221 
222         The mutex will be unlocked (if it had been successfully locked) when the
223         destructor is called.
224 
225         Make sure this object is created and deleted by the same thread,
226         otherwise there are no guarantees what will happen!
227     */
~GenericScopedTryLock()228     inline ~GenericScopedTryLock() noexcept         { if (lockWasSuccessful) lock_.exit(); }
229 
230     /** Returns true if the mutex was successfully locked. */
isLocked()231     bool isLocked() const noexcept                  { return lockWasSuccessful; }
232 
233     /** Retry gaining the lock by calling tryEnter on the underlying lock. */
retryLock()234     bool retryLock() const noexcept                 { lockWasSuccessful = lock_.tryEnter(); return lockWasSuccessful; }
235 
236 private:
237     //==============================================================================
238     const LockType& lock_;
239     mutable bool lockWasSuccessful;
240 
241     JUCE_DECLARE_NON_COPYABLE (GenericScopedTryLock)
242 };
243 
244 } // namespace juce
245