1//
2// Mutex.h
3//
4// Library: Foundation
5// Package: Threading
6// Module: Mutex
7//
8// Definition of the Mutex and FastMutex classes.
9//
10// Copyright (c) 2004-2008, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15
16
17#ifndef Foundation_Mutex_INCLUDED
18#define Foundation_Mutex_INCLUDED
19
20
21#include "Poco/Foundation.h"
22#include "Poco/Timestamp.h"
23#include "Poco/Exception.h"
24#include "Poco/ScopedLock.h"
25#include <atomic>
26
27
28#if (POCO_OS == POCO_OS_CYGWIN || POCO_OS == POCO_OS_ANDROID)
29#include "Poco/Mutex_POSIX.h"
30#else
31#include "Poco/Mutex_STD.h"
32#endif
33
34namespace Poco {
35
36
37class Foundation_API Mutex: private MutexImpl
38 /// A Mutex (mutual exclusion) is a synchronization
39 /// mechanism used to control access to a shared resource
40 /// in a concurrent (multithreaded) scenario.
41 /// Using the ScopedLock class is the preferred way to automatically
42 /// lock and unlock a mutex.
43{
44public:
45 enum MutexType
46 /// The type of a mutex.
47 {
48 MUTEX_RECURSIVE = MUTEX_RECURSIVE_IMPL, /// A recursive mutex
49 MUTEX_NONRECURSIVE = MUTEX_NONRECURSIVE_IMPL /// A non-recursive mutex
50 };
51
52 typedef Poco::ScopedLock<Mutex> ScopedLock;
53
54 explicit Mutex(MutexType type = MUTEX_RECURSIVE);
55 /// Creates the Mutex.
56
57 ~Mutex();
58 /// Destroys the Mutex.
59
60 void lock();
61 /// Locks the mutex. Blocks if the mutex
62 /// is held by another thread.
63
64 void lock(long milliseconds);
65 /// Locks the mutex. Blocks up to the given number of milliseconds
66 /// if the mutex is held by another thread. Throws a TimeoutException
67 /// if the mutex can not be locked within the given timeout.
68 ///
69 /// Performance Note: On most platforms (including Windows), this member function is
70 /// implemented using a loop calling (the equivalent of) tryLock() and Thread::sleep().
71 /// On POSIX platforms that support pthread_mutex_timedlock(), this is used.
72
73 bool tryLock();
74 /// Tries to lock the mutex. Returns false immediately
75 /// if the mutex is already held by another thread.
76 /// Returns true if the mutex was successfully locked.
77
78 bool tryLock(long milliseconds);
79 /// Locks the mutex. Blocks up to the given number of milliseconds
80 /// if the mutex is held by another thread.
81 /// Returns true if the mutex was successfully locked.
82 ///
83 /// Performance Note: On most platforms (including Windows), this member function is
84 /// implemented using a loop calling (the equivalent of) tryLock() and Thread::sleep().
85 /// On POSIX platforms that support pthread_mutex_timedlock(), this is used.
86
87 void unlock();
88 /// Unlocks the mutex so that it can be acquired by
89 /// other threads.
90
91private:
92 Mutex(const Mutex&);
93 Mutex& operator = (const Mutex&);
94};
95
96
97class Foundation_API FastMutex: private FastMutexImpl
98 /// A FastMutex (mutual exclusion) is similar to a Mutex.
99 /// Locking a FastMutex is guaranteed to be at least as
100 /// fast as locking a Mutex. However, a FastMutex is not
101 /// guaranteed to be either recursive or non-recursive.
102 /// It is best suited to thread safe components like pools,
103 /// caches and queues where locking is internal to the component.
104 /// Using the ScopedLock class is the preferred way to automatically
105 /// lock and unlock a mutex.
106{
107public:
108 typedef Poco::ScopedLock<FastMutex> ScopedLock;
109
110 FastMutex();
111 /// creates the Mutex.
112
113 ~FastMutex();
114 /// destroys the Mutex.
115
116 void lock();
117 /// Locks the mutex. Blocks if the mutex
118 /// is held by another thread.
119
120 void lock(long milliseconds);
121 /// Locks the mutex. Blocks up to the given number of milliseconds
122 /// if the mutex is held by another thread. Throws a TimeoutException
123 /// if the mutex can not be locked within the given timeout.
124 ///
125 /// Performance Note: On most platforms (including Windows), this member function is
126 /// implemented using a loop calling (the equivalent of) tryLock() and Thread::sleep().
127 /// On POSIX platforms that support pthread_mutex_timedlock(), this is used.
128
129 bool tryLock();
130 /// Tries to lock the mutex. Returns false immediately
131 /// if the mutex is already held by another thread.
132 /// Returns true if the mutex was successfully locked.
133
134 bool tryLock(long milliseconds);
135 /// Locks the mutex. Blocks up to the given number of milliseconds
136 /// if the mutex is held by another thread.
137 /// Returns true if the mutex was successfully locked.
138 ///
139 /// Performance Note: On most platforms (including Windows), this member function is
140 /// implemented using a loop calling (the equivalent of) tryLock() and Thread::sleep().
141 /// On POSIX platforms that support pthread_mutex_timedlock(), this is used.
142
143 void unlock();
144 /// Unlocks the mutex so that it can be acquired by
145 /// other threads.
146
147private:
148 FastMutex(const FastMutex&);
149 FastMutex& operator = (const FastMutex&);
150};
151
152
153class Foundation_API SpinlockMutex
154 /// A SpinlockMutex, implemented in terms of std::atomic_flag, as
155 /// busy-wait mutual exclusion.
156 ///
157 /// While in some cases (eg. locking small blocks of code)
158 /// busy-waiting may be an optimal solution, in many scenarios
159 /// spinlock may not be the right choice - it is up to the user to
160 /// choose the proper mutex type for their particular case.
161 ///
162 /// Works with the ScopedLock class.
163{
164public:
165 typedef Poco::ScopedLock<SpinlockMutex> ScopedLock;
166
167 SpinlockMutex();
168 /// Creates the SpinlockMutex.
169
170 ~SpinlockMutex();
171 /// Destroys the SpinlockMutex.
172
173 void lock();
174 /// Locks the mutex. Blocks if the mutex
175 /// is held by another thread.
176
177 void lock(long milliseconds);
178 /// Locks the mutex. Blocks up to the given number of milliseconds
179 /// if the mutex is held by another thread. Throws a TimeoutException
180 /// if the mutex can not be locked within the given timeout.
181
182 bool tryLock();
183 /// Tries to lock the mutex. Returns immediately, false
184 /// if the mutex is already held by another thread, true
185 /// if the mutex was successfully locked.
186
187 bool tryLock(long milliseconds);
188 /// Locks the mutex. Blocks up to the given number of milliseconds
189 /// if the mutex is held by another thread.
190 /// Returns true if the mutex was successfully locked.
191
192 void unlock();
193 /// Unlocks the mutex so that it can be acquired by
194 /// other threads.
195
196private:
197 std::atomic_flag _flag = ATOMIC_FLAG_INIT;
198};
199
200
201class Foundation_API NullMutex
202 /// A NullMutex is an empty mutex implementation
203 /// which performs no locking at all. Useful in policy driven design
204 /// where the type of mutex used can be now a template parameter allowing the user to switch
205 /// between thread-safe and not thread-safe depending on his need
206 /// Works with the ScopedLock class
207{
208public:
209 typedef Poco::ScopedLock<NullMutex> ScopedLock;
210
211 NullMutex()
212 /// Creates the NullMutex.
213 {
214 }
215
216 ~NullMutex()
217 /// Destroys the NullMutex.
218 {
219 }
220
221 void lock()
222 /// Does nothing.
223 {
224 }
225
226 void lock(long)
227 /// Does nothing.
228 {
229 }
230
231 bool tryLock()
232 /// Does nothing and always returns true.
233 {
234 return true;
235 }
236
237 bool tryLock(long)
238 /// Does nothing and always returns true.
239 {
240 return true;
241 }
242
243 void unlock()
244 /// Does nothing.
245 {
246 }
247};
248
249
250//
251// inlines
252//
253
254//
255// Mutex
256//
257
258inline void Mutex::lock()
259{
260 lockImpl();
261}
262
263
264inline void Mutex::lock(long milliseconds)
265{
266 if (!tryLockImpl(milliseconds))
267 throw TimeoutException();
268}
269
270
271inline bool Mutex::tryLock()
272{
273 return tryLockImpl();
274}
275
276
277inline bool Mutex::tryLock(long milliseconds)
278{
279 return tryLockImpl(milliseconds);
280}
281
282
283inline void Mutex::unlock()
284{
285 unlockImpl();
286}
287
288
289//
290// FastMutex
291//
292
293inline void FastMutex::lock()
294{
295 lockImpl();
296}
297
298
299inline void FastMutex::lock(long milliseconds)
300{
301 if (!tryLockImpl(milliseconds))
302 throw TimeoutException();
303}
304
305
306inline bool FastMutex::tryLock()
307{
308 return tryLockImpl();
309}
310
311
312inline bool FastMutex::tryLock(long milliseconds)
313{
314 return tryLockImpl(milliseconds);
315}
316
317
318inline void FastMutex::unlock()
319{
320 unlockImpl();
321}
322
323
324//
325// SpinlockMutex
326//
327
328inline void SpinlockMutex::lock()
329{
330 while (_flag.test_and_set(std::memory_order_acquire));
331}
332
333
334inline void SpinlockMutex::lock(long milliseconds)
335{
336 Timestamp now;
337 Timestamp::TimeDiff diff(Timestamp::TimeDiff(milliseconds)*1000);
338 while (_flag.test_and_set(std::memory_order_acquire))
339 {
340 if (now.isElapsed(diff)) throw TimeoutException();
341 }
342}
343
344
345inline bool SpinlockMutex::tryLock()
346{
347 return !_flag.test_and_set(std::memory_order_acquire);
348}
349
350
351inline bool SpinlockMutex::tryLock(long milliseconds)
352{
353 Timestamp now;
354 Timestamp::TimeDiff diff(Timestamp::TimeDiff(milliseconds)*1000);
355 while (_flag.test_and_set(std::memory_order_acquire))
356 {
357 if (now.isElapsed(diff)) return false;
358 }
359 return true;
360}
361
362
363inline void SpinlockMutex::unlock()
364{
365 _flag.clear(std::memory_order_release);
366}
367
368
369} // namespace Poco
370
371
372#endif // Foundation_Mutex_INCLUDED
373