ROSE 0.11.145.147
Synchronization.h
1// WARNING: Changes to this file must be contributed back to Sawyer or else they will
2// be clobbered by the next update from Sawyer. The Sawyer repository is at
3// https://gitlab.com/charger7534/sawyer.git.
4
5
6
7
8#ifndef Sawyer_Synchronization_H
9#define Sawyer_Synchronization_H
10
11#include <Sawyer/Sawyer.h>
12#include <Sawyer/Map.h>
13#include <Sawyer/Type.h>
14
15#if SAWYER_MULTI_THREADED
16 // It appears as though a certain version of GNU libc interacts badly with C++03 GCC and LLVM compilers. Some system header
17 // file defines _XOPEN_UNIX as "1" and __UINTPTR_TYPE__ as "unsigned long int" but doesn't provide a definition for
18 // "uintptr_t". This triggers a compilation error in <boost/atomic/atomic.hpp> for boost-1.54 because it assumes that
19 // "uintptr_t" is available based on the preprocessor macros and the included files. These errors occur (at a minimum) on
20 // Debian 8.2 and 8.3 using C++03 mode of gcc-4.8.4, gcc-4.9.2, or llvm-3.5.
21 #include <boost/version.hpp>
22 #if __cplusplus < 201103L && BOOST_VERSION == 105400
23 #include <stdint.h> // must be included before <boost/thread.hpp>
24 #endif
25
26 #include <boost/thread.hpp>
27 #include <boost/thread/barrier.hpp>
28 #include <boost/thread/condition_variable.hpp>
29 #include <boost/thread/mutex.hpp>
30 #include <boost/thread/locks.hpp>
31 #include <boost/thread/once.hpp>
32 #include <boost/thread/recursive_mutex.hpp>
33#endif
34
35namespace Sawyer {
36
42
48
49// Used internally as a mutex in a single-threaded environment. Although it doesn't make sense to be able lock or unlock a
50// mutex in a single-threaded environment, incrementing a data member for each unlock might be useful and it works in
51// conjunction with NullLockGuard to prevent compilers from warning about unused variables which, at least in the
52// multi-threaded environment, are used only for their RAII side effects.
53class NullMutex {
54 size_t n;
55public:
56 NullMutex(): n(0) {}
57 void lock() {}
58 void unlock() { ++n; }
59 bool try_lock() { return true; }
60};
61
62// Used internally as a lock guard in a single-threaded environment.
64 NullMutex &mutex_;
65public:
67 : mutex_(m) {
68 lock();
69 }
71 unlock();
72 }
73 void lock() {
74 mutex_.lock();
75 }
76 void unlock() {
77 mutex_.unlock();
78 }
79};
80
81// Used internally as a barrier in a single-threaded environment.
83public:
84 explicit NullBarrier(unsigned count) {
85 if (count > 1)
86 throw std::runtime_error("barrier would deadlock");
87 }
88 bool wait() {
89 return true;
90 }
91};
92
94template<typename Mutex>
96 Mutex &m1_, &m2_;
97public:
98 LockGuard2(Mutex &m1, Mutex &m2): m1_(m1), m2_(m2) {
99#if SAWYER_MULTI_THREADED
100 boost::lock(m1, m2);
101#endif
102 }
103 ~LockGuard2() {
104 m1_.unlock();
105 m2_.unlock();
106 }
107};
108
109template<>
111public:
113};
114
116template<typename SyncTag>
118
119template<>
121#if SAWYER_MULTI_THREADED
122 enum { SUPPORTED = 1 };
123 typedef boost::mutex Mutex;
124 typedef boost::recursive_mutex RecursiveMutex;
125 typedef boost::lock_guard<boost::mutex> LockGuard;
126 typedef boost::unique_lock<boost::mutex> UniqueLock;
127 typedef boost::lock_guard<boost::recursive_mutex> RecursiveLockGuard;
128 typedef boost::condition_variable_any ConditionVariable;
129 typedef boost::barrier Barrier;
130#else
131 enum { SUPPORTED = 0 };
132 typedef NullMutex Mutex;
134 typedef NullLockGuard LockGuard;
137 //typedef ... ConditionVariable; -- does not make sense to use this in a single-threaded program
138 typedef NullBarrier Barrier;
139#endif
141};
142
143
144template<>
146 enum { SUPPORTED = 0 };
147 typedef NullMutex Mutex;
149 typedef NullLockGuard LockGuard;
152 //typedef ... ConditionVariable; -- does not make sense to use this in a single-threaded program
153 typedef NullBarrier Barrier;
155};
156
157// Used internally.
158SAWYER_EXPORT SAWYER_THREAD_TRAITS::RecursiveMutex& bigMutex();
159
166SAWYER_EXPORT size_t fastRandomIndex(size_t n, size_t seed = 0);
167
189template<typename T>
191 // The implementation needs to handle the case when this object is created on one thread and used in another thread. The
192 // constructor, running in thread A, creates a thread-local repo which doesn't exist in thread B using this object.
193 //
194 // This is a pointer to avoid lack of thread-local dynamic initialization prior to C++11, and to avoid lack of well defined
195 // order when initializing and destroying global variables in C++.
196 typedef Type::UnsignedInteger<8*sizeof(void*)>::type IntPtr;
198 static SAWYER_THREAD_LOCAL Repo *repo_; // no mutex necessary since this is thread-local
199
200public:
203 if (!repo_)
204 repo_ = new Repo;
205 repo_->insert(reinterpret_cast<IntPtr>(this), T());
206 }
207
209 /*implicit*/ MultiInstanceTls(const T& value) {
210 if (!repo_)
211 repo_ = new Repo;
212 repo_->insert(reinterpret_cast<IntPtr>(this), value);
213 }
214
215 /*implicit*/ MultiInstanceTls(const MultiInstanceTls &other) {
216 if (!repo_)
217 repo_ = new Repo;
218 repo_->insert(reinterpret_cast<IntPtr>(this), other.get());
219 }
220
222 MultiInstanceTls& operator=(const T &value) {
223 if (!repo_)
224 repo_ = new Repo;
225 repo_->insert(reinterpret_cast<IntPtr>(this), value);
226 return *this;
227 }
228
230 if (!repo_)
231 repo_ = new Repo;
232 repo_->insert(reinterpret_cast<IntPtr>(this), other.get());
233 return *this;
234 }
235
236 ~MultiInstanceTls() {
237 if (repo_)
238 repo_->erase(reinterpret_cast<IntPtr>(this));
239 }
240
244 T& get() {
245 if (!repo_)
246 repo_ = new Repo;
247 return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
248 }
249 const T& get() const {
250 if (!repo_)
251 repo_ = new Repo;
252 return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
253 }
256 T& operator*() {
257 return get();
258 }
259
260 const T& operator*() const {
261 return get();
262 }
263
264 T* operator->() {
265 return &get();
266 }
267
268 const T* operator->() const {
269 return &get();
270 }
271
275 operator T() const {
276 if (!repo_)
277 repo_ = new Repo;
278 return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
279 }
280};
281
282template<typename T>
283SAWYER_THREAD_LOCAL Container::Map<Type::UnsignedInteger<8*sizeof(void*)>::type, T>* MultiInstanceTls<T>::repo_;
284
285} // namespace
286#endif
Container associating values with keys.
Definition Sawyer/Map.h:72
Value & insertMaybeDefault(const Key &key)
Conditionally insert a new key with default value.
Definition Sawyer/Map.h:711
Map & insert(const Key &key, const Value &value)
Insert or update a key/value pair.
Definition Sawyer/Map.h:646
Locks multiple mutexes.
Thread local data per object instance.
MultiInstanceTls(const T &value)
Initialize value.
MultiInstanceTls & operator=(const T &value)
Assignment operator.
T & get()
Get interior object.
MultiInstanceTls()
Default-constructed value.
const T & get() const
Get interior object.
Sawyer support library.
size_t fastRandomIndex(size_t n, size_t seed=0)
Thread-safe random number generator.
Tag indicating that an algorithm or API should assume multiple threads.
Tag indicating that an algorithm or API can assume only a single thread.
Traits for thread synchronization.
An unsigned integer of particular size.
Definition Type.h:26