ROSE  0.11.50.0
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://github.com/matzke1/sawyer.
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 
35 namespace Sawyer {
36 
41 struct MultiThreadedTag {};
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.
53 class NullMutex {
54  size_t n;
55 public:
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_;
65 public:
67  : mutex_(m) {
68  lock();
69  }
70  ~NullLockGuard() {
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.
82 class NullBarrier {
83 public:
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 
94 template<typename Mutex>
95 class LockGuard2 {
96  Mutex &m1_, &m2_;
97 public:
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 
109 template<>
111 public:
113 };
114 
116 template<typename SyncTag>
118 
119 template<>
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;
133  typedef NullMutex RecursiveMutex;
134  typedef NullLockGuard LockGuard;
135  typedef NullLockGuard UniqueLock;
136  typedef NullLockGuard RecursiveLockGuard;
137  //typedef ... ConditionVariable; -- does not make sense to use this in a single-threaded program
138  typedef NullBarrier Barrier;
139 #endif
141 };
142 
143 
144 template<>
146  enum { SUPPORTED = 0 };
147  typedef NullMutex Mutex;
148  typedef NullMutex RecursiveMutex;
149  typedef NullLockGuard LockGuard;
150  typedef NullLockGuard UniqueLock;
152  //typedef ... ConditionVariable; -- does not make sense to use this in a single-threaded program
153  typedef NullBarrier Barrier;
155 };
156 
157 // Used internally.
158 SAWYER_EXPORT SAWYER_THREAD_TRAITS::RecursiveMutex& bigMutex();
159 
166 SAWYER_EXPORT size_t fastRandomIndex(size_t n, size_t seed = 0);
167 
189 template<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++.
198  static SAWYER_THREAD_LOCAL Repo *repo_; // no mutex necessary since this is thread-local
199 
200 public:
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 
282 template<typename T>
283 SAWYER_THREAD_LOCAL Container::Map<Type::UnsignedInteger<8*sizeof(void*)>::type, T>* MultiInstanceTls<T>::repo_;
284 
285 } // namespace
286 #endif
MultiInstanceTls()
Default-constructed value.
MultiInstanceTls(const T &value)
Initialize value.
Thread local data per object instance.
Locks multiple mutexes.
Name space for the entire library.
Definition: FeasiblePath.h:787
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.
MultiInstanceTls & operator=(const T &value)
Assignment operator.
Traits for thread synchronization.
Tag indicating that an algorithm or API can assume only a single thread.
T & get()
Get interior object.
An unsigned integer of particular size.
Definition: Type.h:26
Container associating values with keys.
Definition: Sawyer/Map.h:66