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