ROSE  0.9.11.56
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 
216  MultiInstanceTls& operator=(const T &value) {
217  if (!repo_)
218  repo_ = new Repo;
219  repo_->insert(reinterpret_cast<IntPtr>(this), value);
220  return *this;
221  }
222 
223  ~MultiInstanceTls() {
224  if (repo_)
225  repo_->erase(reinterpret_cast<IntPtr>(this));
226  }
227 
231  T& get() {
232  if (!repo_)
233  repo_ = new Repo;
234  return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
235  }
236  const T& get() const {
237  if (!repo_)
238  repo_ = new Repo;
239  return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
240  }
243  T& operator*() {
244  return get();
245  }
246 
247  const T& operator*() const {
248  return get();
249  }
250 
251  T* operator->() {
252  return &get();
253  }
254 
255  const T* operator->() const {
256  return &get();
257  }
258 
262  operator T() const {
263  if (!repo_)
264  repo_ = new Repo;
265  return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
266  }
267 };
268 
269 template<typename T>
270 SAWYER_THREAD_LOCAL Container::Map<Type::UnsignedInteger<8*sizeof(void*)>::type, T>* MultiInstanceTls<T>::repo_;
271 
272 } // namespace
273 #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.
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.
An unsigned integer of particular size.
Definition: Type.h:26
Container associating values with keys.
Definition: Sawyer/Map.h:66