ROSE  0.9.10.47
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;
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 
165 SAWYER_EXPORT size_t fastRandomIndex(size_t n);
166 
188 template<typename T>
190  // The implementation needs to handle the case when this object is created on one thread and used in another thread. The
191  // constructor, running in thread A, creates a thread-local repo which doesn't exist in thread B using this object.
192  //
193  // This is a pointer to avoid lack of thread-local dynamic initialization prior to C++11, and to avoid lack of well defined
194  // order when initializing and destroying global variables in C++.
197  static SAWYER_THREAD_LOCAL Repo *repo_;
198 
199 public:
202  if (!repo_)
203  repo_ = new Repo;
204  repo_->insert(reinterpret_cast<IntPtr>(this), T());
205  }
206 
208  /*implicit*/ MultiInstanceTls(const T& value) {
209  if (!repo_)
210  repo_ = new Repo;
211  repo_->insert(reinterpret_cast<IntPtr>(this), value);
212  }
213 
215  MultiInstanceTls& operator=(const T &value) {
216  if (!repo_)
217  repo_ = new Repo;
218  repo_->insert(reinterpret_cast<IntPtr>(this), value);
219  return *this;
220  }
221 
222  ~MultiInstanceTls() {
223  if (repo_)
224  repo_->erase(reinterpret_cast<IntPtr>(this));
225  }
226 
230  T& get() {
231  if (!repo_)
232  repo_ = new Repo;
233  return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
234  }
235  const T& get() const {
236  if (!repo_)
237  repo_ = new Repo;
238  return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
239  }
242  T& operator*() {
243  return get();
244  }
245 
246  const T& operator*() const {
247  return get();
248  }
249 
250  T* operator->() {
251  return &get();
252  }
253 
254  const T* operator->() const {
255  return &get();
256  }
257 
261  operator T() const {
262  if (!repo_)
263  repo_ = new Repo;
264  return repo_->insertMaybeDefault(reinterpret_cast<IntPtr>(this));
265  }
266 };
267 
268 template<typename T>
269 SAWYER_THREAD_LOCAL Container::Map<Type::UnsignedInteger<8*sizeof(void*)>::type, T>* MultiInstanceTls<T>::repo_;
270 
271 } // namespace
272 #endif
MultiInstanceTls()
Default-constructed value.
size_t fastRandomIndex(size_t n)
Thread-safe random number generator.
MultiInstanceTls(const T &value)
Initialize value.
Thread local data per object instance.
Value & insertMaybeDefault(const Key &key)
Conditionally insert a new key with default value.
Definition: Sawyer/Map.h:595
Locks multiple mutexes.
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.
Map & erase(const Key &key)
Remove a node with specified key.
Definition: Sawyer/Map.h:628
Map & insert(const Key &key, const Value &value)
Insert or update a key/value pair.
Definition: Sawyer/Map.h:530
An unsigned integer of particular size.
Definition: Type.h:26
Container associating values with keys.
Definition: Sawyer/Map.h:64