Apache Mesos
synchronized.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_SYNCHRONIZED_HPP__
14 #define __STOUT_SYNCHRONIZED_HPP__
15 
16 #include <atomic>
17 #include <condition_variable>
18 #include <mutex>
19 #include <type_traits>
20 
21 #include <glog/logging.h>
22 
23 #include <stout/preprocessor.hpp>
24 
25 // An RAII class for the 'synchronized(m)' macro.
26 template <typename T>
28 {
29 public:
30  explicit Synchronized(T* t, void (*acquire)(T*), void (*release)(T*))
31  : t_(CHECK_NOTNULL(t)), release_(release)
32  {
33  acquire(t_);
34  }
35 
36  ~Synchronized() { release_(t_); }
37 
38  // NOTE: 'false' being returned here has no significance.
39  // Refer to the NOTE for 'synchronized' at the bottom for why.
40  explicit operator bool() const { return false; }
41 
42 private:
43  T* t_;
44 
45  void (*release_)(T*);
46 };
47 
48 
49 // The generic version handles mutexes which have 'lock' and 'unlock'
50 // member functions such as 'std::mutex' and 'std::recursive_mutex'.
51 template <typename T>
53 {
54  return Synchronized<T>(
55  t,
56  [](T* t) { t->lock(); },
57  [](T* t) { t->unlock(); }
58  );
59 }
60 
61 
62 // An overload of the 'synchronize' function for 'std::atomic_flag'.
63 inline Synchronized<std::atomic_flag> synchronize(std::atomic_flag* lock)
64 {
66  lock,
67  [](std::atomic_flag* lock) {
68  while (lock->test_and_set(std::memory_order_acquire)) {}
69  },
70  [](std::atomic_flag* lock) {
71  lock->clear(std::memory_order_release);
72  }
73  );
74 }
75 
76 
77 template <typename T>
79 {
80  return *CHECK_NOTNULL(t);
81 }
82 
83 
84 template <typename T>
86 {
87  return t;
88 }
89 
90 
91 // Macros to help generate "unique" identifiers for the
92 // synchronization variable name and label. The line number gets
93 // embedded which makes it unique enough, but not absolutely unique.
94 // It shouldn't be a problem however, since it's very unlikely that
95 // anyone would place multiple 'synchronized' blocks on one line.
96 #define SYNCHRONIZED_PREFIX CAT(__synchronizer_, __LINE__)
97 #define SYNCHRONIZED_VAR CAT(SYNCHRONIZED_PREFIX, _var__)
98 #define SYNCHRONIZED_LABEL CAT(SYNCHRONIZED_PREFIX, _label__)
99 
100 
101 // A macro for acquiring a scoped 'guard' on any type that can satisfy
102 // the 'Synchronized' interface. We support 'std::mutex',
103 // 'std::recursive_mutex' and 'std::atomic_flag' by default.
104 //
105 // Example usage:
106 // std::mutex m;
107 // synchronized (m) {
108 // // Do something under the lock.
109 // }
110 //
111 // You can easily extend support for your synchronization primitive
112 // here, by overloading the 'synchronize' function.
113 //
114 // Example overload:
115 // inline Synchronized<bufferevent> synchronize(bufferevent* bev)
116 // {
117 // return Synchronized<bufferevent>(
118 // bev,
119 // [](bufferevent* bev) { bufferevent_lock(bev); },
120 // [](bufferevent* bev) { bufferevent_unlock(bev); },
121 // );
122 // }
123 //
124 // How it works: An instance of the synchronization primitive is
125 // constructed inside the 'condition' of if statement. This variable
126 // stays alive for the lifetime of the block. The trick with 'goto',
127 // 'else' and the label allows the compiler to figure out that the
128 // synchronized block will always execute. This allows us to return
129 // within the synchronized block and avoid the
130 // 'control reaches the end of a non-void function' warning.
131 // Note that the variable declared inside the 'condition' of an if
132 // statement is guaranteed to live through the 'else' clause as well.
133 //
134 // From Section 6.4.3:
135 // A name introduced by a declaration in a condition (either
136 // introduced by the decl-specifier-seq or the declarator of the
137 // condition) is in scope from its point of declaration until the
138 // end of the substatements controlled by the condition.
139 #define synchronized(m) \
140  if (Synchronized<typename std::remove_pointer<decltype(m)>::type> \
141  SYNCHRONIZED_VAR = ::synchronize(::synchronized_get_pointer(&m))) { \
142  goto SYNCHRONIZED_LABEL; \
143  } else SYNCHRONIZED_LABEL:
144 
145 
160 template <typename CV, typename Lock>
161 void synchronized_wait(CV* cv, Lock* lock);
162 
163 
188 template <>
189 inline void synchronized_wait(std::condition_variable* cv, std::mutex* mutex)
190 {
191  CHECK_NOTNULL(cv);
192  CHECK_NOTNULL(mutex);
193 
194  // Since the pre-condition is that 'mutex' is already owned by the
195  // calling thread, we use 'std::adopt_lock' to adopt the mutex
196  // rather than trying to lock it ourselves.
197  std::unique_lock<std::mutex> lock(*mutex, std::adopt_lock);
198 
199  // On entrance, 'std::condition_variable::wait' releases 'mutex'.
200  // On exit, 'std::condition_variable::wait' re-acquires 'mutex'.
201  cv->wait(lock);
202 
203  // At this point, the 'mutex' has been acquired. Since the
204  // post-condition is that 'mutex' is still owned by the calling
205  // thread, we use 'release' in order to prevent 'std::unique_lock'
206  // from unlocking the 'mutex'.
207  lock.release();
208 }
209 
210 
221 template <typename Lock>
222 void synchronized_wait(std::condition_variable_any* cv, Lock* lock) = delete;
223 
224 #endif // __STOUT_SYNCHRONIZED_HPP__
Synchronized< T > synchronize(T *t)
Definition: synchronized.hpp:52
Synchronized(T *t, void(*acquire)(T *), void(*release)(T *))
Definition: synchronized.hpp:30
Definition: synchronized.hpp:27
void synchronized_wait(CV *cv, Lock *lock)
Waits on the condition variable associated with &#39;lock&#39; which has already been synchronized.
~Synchronized()
Definition: synchronized.hpp:36
Try< Version > release()
Definition: os.hpp:377
T * synchronized_get_pointer(T **t)
Definition: synchronized.hpp:78