• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Do not include.  This is an implementation detail of base/mutex.h.
2//
3// Declares three classes:
4//
5// base::internal::MutexImpl - implementation helper for Mutex
6// base::internal::CondVarImpl - implementation helper for CondVar
7// base::internal::SynchronizationStorage<T> - implementation helper for
8//                                             Mutex, CondVar
9
10#include <type_traits>
11
12#if defined(_WIN32)
13#include <condition_variable>
14#include <mutex>
15#else
16#include <pthread.h>
17#endif
18
19#include "absl/base/call_once.h"
20#include "absl/time/time.h"
21
22// Declare that Mutex::ReaderLock is actually Lock().  Intended primarily
23// for tests, and even then as a last resort.
24#ifdef ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE
25#error ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE cannot be directly set
26#else
27#define ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE 1
28#endif
29
30// Declare that Mutex::EnableInvariantDebugging is not implemented.
31// Intended primarily for tests, and even then as a last resort.
32#ifdef ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED
33#error ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED cannot be directly set
34#else
35#define ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED 1
36#endif
37
38namespace absl {
39ABSL_NAMESPACE_BEGIN
40class Condition;
41
42namespace synchronization_internal {
43
44class MutexImpl;
45
46// Do not use this implementation detail of CondVar. Provides most of the
47// implementation, but should not be placed directly in static storage
48// because it will not linker initialize properly. See
49// SynchronizationStorage<T> below for what we mean by linker
50// initialization.
51class CondVarImpl {
52 public:
53  CondVarImpl();
54  CondVarImpl(const CondVarImpl&) = delete;
55  CondVarImpl& operator=(const CondVarImpl&) = delete;
56  ~CondVarImpl();
57
58  void Signal();
59  void SignalAll();
60  void Wait(MutexImpl* mutex);
61  bool WaitWithDeadline(MutexImpl* mutex, absl::Time deadline);
62
63 private:
64#if defined(_WIN32)
65  std::condition_variable_any std_cv_;
66#else
67  pthread_cond_t pthread_cv_;
68#endif
69};
70
71// Do not use this implementation detail of Mutex. Provides most of the
72// implementation, but should not be placed directly in static storage
73// because it will not linker initialize properly. See
74// SynchronizationStorage<T> below for what we mean by linker
75// initialization.
76class MutexImpl {
77 public:
78  MutexImpl();
79  MutexImpl(const MutexImpl&) = delete;
80  MutexImpl& operator=(const MutexImpl&) = delete;
81  ~MutexImpl();
82
83  void Lock();
84  bool TryLock();
85  void Unlock();
86  void Await(const Condition& cond);
87  bool AwaitWithDeadline(const Condition& cond, absl::Time deadline);
88
89 private:
90  friend class CondVarImpl;
91
92#if defined(_WIN32)
93  std::mutex std_mutex_;
94#else
95  pthread_mutex_t pthread_mutex_;
96#endif
97
98  // True if the underlying mutex is locked.  If the destructor is entered
99  // while locked_, the underlying mutex is unlocked.  Mutex supports
100  // destruction while locked, but the same is undefined behavior for both
101  // pthread_mutex_t and std::mutex.
102  bool locked_ = false;
103
104  // Signaled before releasing the lock, in support of Await.
105  CondVarImpl released_;
106};
107
108// Do not use this implementation detail of CondVar and Mutex.  A storage
109// space for T that supports a LinkerInitialized constructor. T must
110// have a default constructor, which is called by the first call to
111// get(). T's destructor is never called if the LinkerInitialized
112// constructor is called.
113//
114// Objects constructed with the default constructor are constructed and
115// destructed like any other object, and should never be allocated in
116// static storage.
117//
118// Objects constructed with the LinkerInitialized constructor should
119// always be in static storage. For such objects, calls to get() are always
120// valid, except from signal handlers.
121//
122// Note that this implementation relies on undefined language behavior that
123// are known to hold for the set of supported compilers. An analysis
124// follows.
125//
126// From the C++11 standard:
127//
128// [basic.life] says an object has non-trivial initialization if it is of
129// class type and it is initialized by a constructor other than a trivial
130// default constructor.  (the LinkerInitialized constructor is
131// non-trivial)
132//
133// [basic.life] says the lifetime of an object with a non-trivial
134// constructor begins when the call to the constructor is complete.
135//
136// [basic.life] says the lifetime of an object with non-trivial destructor
137// ends when the call to the destructor begins.
138//
139// [basic.life] p5 specifies undefined behavior when accessing non-static
140// members of an instance outside its
141// lifetime. (SynchronizationStorage::get() access non-static members)
142//
143// So, LinkerInitialized object of SynchronizationStorage uses a
144// non-trivial constructor, which is called at some point during dynamic
145// initialization, and is therefore subject to order of dynamic
146// initialization bugs, where get() is called before the object's
147// constructor is, resulting in undefined behavior.
148//
149// Similarly, a LinkerInitialized SynchronizationStorage object has a
150// non-trivial destructor, and so its lifetime ends at some point during
151// destruction of objects with static storage duration [basic.start.term]
152// p4. There is a window where other exit code could call get() after this
153// occurs, resulting in undefined behavior.
154//
155// Combined, these statements imply that LinkerInitialized instances
156// of SynchronizationStorage<T> rely on undefined behavior.
157//
158// However, in practice, the implementation works on all supported
159// compilers. Specifically, we rely on:
160//
161// a) zero-initialization being sufficient to initialize
162// LinkerInitialized instances for the purposes of calling
163// get(), regardless of when the constructor is called. This is
164// because the is_dynamic_ boolean is correctly zero-initialized to
165// false.
166//
167// b) the LinkerInitialized constructor is a NOP, and immaterial to
168// even to concurrent calls to get().
169//
170// c) the destructor being a NOP for LinkerInitialized objects
171// (guaranteed by a check for !is_dynamic_), and so any concurrent and
172// subsequent calls to get() functioning as if the destructor were not
173// called, by virtue of the instances' storage remaining valid after the
174// destructor runs.
175//
176// d) That a-c apply transitively when SynchronizationStorage<T> is the
177// only member of a class allocated in static storage.
178//
179// Nothing in the language standard guarantees that a-d hold.  In practice,
180// these hold in all supported compilers.
181//
182// Future direction:
183//
184// Ideally, we would simply use std::mutex or a similar class, which when
185// allocated statically would support use immediately after static
186// initialization up until static storage is reclaimed (i.e. the properties
187// we require of all "linker initialized" instances).
188//
189// Regarding construction in static storage, std::mutex is required to
190// provide a constexpr default constructor [thread.mutex.class], which
191// ensures the instance's lifetime begins with static initialization
192// [basic.start.init], and so is immune to any problems caused by the order
193// of dynamic initialization. However, as of this writing Microsoft's
194// Visual Studio does not provide a constexpr constructor for std::mutex.
195// See
196// https://blogs.msdn.microsoft.com/vcblog/2015/06/02/constexpr-complete-for-vs-2015-rtm-c11-compiler-c17-stl/
197//
198// Regarding destruction of instances in static storage, [basic.life] does
199// say an object ends when storage in which the occupies is released, in
200// the case of non-trivial destructor. However, std::mutex is not specified
201// to have a trivial destructor.
202//
203// So, we would need a class with a constexpr default constructor and a
204// trivial destructor. Today, we can achieve neither desired property using
205// std::mutex directly.
206template <typename T>
207class SynchronizationStorage {
208 public:
209  // Instances allocated on the heap or on the stack should use the default
210  // constructor.
211  SynchronizationStorage()
212      : is_dynamic_(true), once_() {}
213
214  // Instances allocated in static storage (not on the heap, not on the
215  // stack) should use this constructor.
216  explicit SynchronizationStorage(base_internal::LinkerInitialized) {}
217
218  constexpr explicit SynchronizationStorage(absl::ConstInitType)
219      : is_dynamic_(false), once_(), space_{{0}} {}
220
221  SynchronizationStorage(SynchronizationStorage&) = delete;
222  SynchronizationStorage& operator=(SynchronizationStorage&) = delete;
223
224  ~SynchronizationStorage() {
225    if (is_dynamic_) {
226      get()->~T();
227    }
228  }
229
230  // Retrieve the object in storage. This is fast and thread safe, but does
231  // incur the cost of absl::call_once().
232  //
233  // For instances in static storage constructed with the
234  // LinkerInitialized constructor, may be called at any time without
235  // regard for order of dynamic initialization or destruction of objects
236  // in static storage. See the class comment for caveats.
237  T* get() {
238    absl::call_once(once_, SynchronizationStorage::Construct, this);
239    return reinterpret_cast<T*>(&space_);
240  }
241
242 private:
243  static void Construct(SynchronizationStorage<T>* self) {
244    new (&self->space_) T();
245  }
246
247  // When true, T's destructor is run when this is destructed.
248  //
249  // The LinkerInitialized constructor assumes this value will be set
250  // false by static initialization.
251  bool is_dynamic_;
252
253  absl::once_flag once_;
254
255  // An aligned space for the T.
256  alignas(T) unsigned char space_[sizeof(T)];
257};
258
259}  // namespace synchronization_internal
260ABSL_NAMESPACE_END
261}  // namespace absl
262