1 //===------------------------- mutex.cpp ----------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "mutex" 10 #include "limits" 11 #include "system_error" 12 #include "include/atomic_support.h" 13 #include "__undef_macros" 14 15 _LIBCPP_BEGIN_NAMESPACE_STD 16 #ifndef _LIBCPP_HAS_NO_THREADS 17 18 const defer_lock_t defer_lock = {}; 19 const try_to_lock_t try_to_lock = {}; 20 const adopt_lock_t adopt_lock = {}; 21 22 mutex::~mutex() 23 { 24 __libcpp_mutex_destroy(&__m_); 25 } 26 27 void 28 mutex::lock() 29 { 30 int ec = __libcpp_mutex_lock(&__m_); 31 if (ec) 32 __throw_system_error(ec, "mutex lock failed"); 33 } 34 35 bool 36 mutex::try_lock() _NOEXCEPT 37 { 38 return __libcpp_mutex_trylock(&__m_); 39 } 40 41 void 42 mutex::unlock() _NOEXCEPT 43 { 44 int ec = __libcpp_mutex_unlock(&__m_); 45 (void)ec; 46 _LIBCPP_ASSERT(ec == 0, "call to mutex::unlock failed"); 47 } 48 49 // recursive_mutex 50 51 recursive_mutex::recursive_mutex() 52 { 53 int ec = __libcpp_recursive_mutex_init(&__m_); 54 if (ec) 55 __throw_system_error(ec, "recursive_mutex constructor failed"); 56 } 57 58 recursive_mutex::~recursive_mutex() 59 { 60 int e = __libcpp_recursive_mutex_destroy(&__m_); 61 (void)e; 62 _LIBCPP_ASSERT(e == 0, "call to ~recursive_mutex() failed"); 63 } 64 65 void 66 recursive_mutex::lock() 67 { 68 int ec = __libcpp_recursive_mutex_lock(&__m_); 69 if (ec) 70 __throw_system_error(ec, "recursive_mutex lock failed"); 71 } 72 73 void 74 recursive_mutex::unlock() _NOEXCEPT 75 { 76 int e = __libcpp_recursive_mutex_unlock(&__m_); 77 (void)e; 78 _LIBCPP_ASSERT(e == 0, "call to recursive_mutex::unlock() failed"); 79 } 80 81 bool 82 recursive_mutex::try_lock() _NOEXCEPT 83 { 84 return __libcpp_recursive_mutex_trylock(&__m_); 85 } 86 87 // timed_mutex 88 89 timed_mutex::timed_mutex() 90 : __locked_(false) 91 { 92 } 93 94 timed_mutex::~timed_mutex() 95 { 96 lock_guard<mutex> _(__m_); 97 } 98 99 void 100 timed_mutex::lock() 101 { 102 unique_lock<mutex> lk(__m_); 103 while (__locked_) 104 __cv_.wait(lk); 105 __locked_ = true; 106 } 107 108 bool 109 timed_mutex::try_lock() _NOEXCEPT 110 { 111 unique_lock<mutex> lk(__m_, try_to_lock); 112 if (lk.owns_lock() && !__locked_) 113 { 114 __locked_ = true; 115 return true; 116 } 117 return false; 118 } 119 120 void 121 timed_mutex::unlock() _NOEXCEPT 122 { 123 lock_guard<mutex> _(__m_); 124 __locked_ = false; 125 __cv_.notify_one(); 126 } 127 128 // recursive_timed_mutex 129 130 recursive_timed_mutex::recursive_timed_mutex() 131 : __count_(0), 132 __id_(0) 133 { 134 } 135 136 recursive_timed_mutex::~recursive_timed_mutex() 137 { 138 lock_guard<mutex> _(__m_); 139 } 140 141 void 142 recursive_timed_mutex::lock() 143 { 144 __libcpp_thread_id id = __libcpp_thread_get_current_id(); 145 unique_lock<mutex> lk(__m_); 146 if (__libcpp_thread_id_equal(id, __id_)) 147 { 148 if (__count_ == numeric_limits<size_t>::max()) 149 __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached"); 150 ++__count_; 151 return; 152 } 153 while (__count_ != 0) 154 __cv_.wait(lk); 155 __count_ = 1; 156 __id_ = id; 157 } 158 159 bool 160 recursive_timed_mutex::try_lock() _NOEXCEPT 161 { 162 __libcpp_thread_id id = __libcpp_thread_get_current_id(); 163 unique_lock<mutex> lk(__m_, try_to_lock); 164 if (lk.owns_lock() && (__count_ == 0 || __libcpp_thread_id_equal(id, __id_))) 165 { 166 if (__count_ == numeric_limits<size_t>::max()) 167 return false; 168 ++__count_; 169 __id_ = id; 170 return true; 171 } 172 return false; 173 } 174 175 void 176 recursive_timed_mutex::unlock() _NOEXCEPT 177 { 178 unique_lock<mutex> lk(__m_); 179 if (--__count_ == 0) 180 { 181 __id_ = 0; 182 lk.unlock(); 183 __cv_.notify_one(); 184 } 185 } 186 187 #endif // !_LIBCPP_HAS_NO_THREADS 188 189 // If dispatch_once_f ever handles C++ exceptions, and if one can get to it 190 // without illegal macros (unexpected macros not beginning with _UpperCase or 191 // __lowercase), and if it stops spinning waiting threads, then call_once should 192 // call into dispatch_once_f instead of here. Relevant radar this code needs to 193 // keep in sync with: 7741191. 194 195 #ifndef _LIBCPP_HAS_NO_THREADS 196 _LIBCPP_SAFE_STATIC static __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER; 197 _LIBCPP_SAFE_STATIC static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER; 198 #endif 199 200 void __call_once(volatile once_flag::_State_type& flag, void* arg, 201 void (*func)(void*)) 202 { 203 #if defined(_LIBCPP_HAS_NO_THREADS) 204 if (flag == 0) 205 { 206 #ifndef _LIBCPP_NO_EXCEPTIONS 207 try 208 { 209 #endif // _LIBCPP_NO_EXCEPTIONS 210 flag = 1; 211 func(arg); 212 flag = ~once_flag::_State_type(0); 213 #ifndef _LIBCPP_NO_EXCEPTIONS 214 } 215 catch (...) 216 { 217 flag = 0; 218 throw; 219 } 220 #endif // _LIBCPP_NO_EXCEPTIONS 221 } 222 #else // !_LIBCPP_HAS_NO_THREADS 223 __libcpp_mutex_lock(&mut); 224 while (flag == 1) 225 __libcpp_condvar_wait(&cv, &mut); 226 if (flag == 0) 227 { 228 #ifndef _LIBCPP_NO_EXCEPTIONS 229 try 230 { 231 #endif // _LIBCPP_NO_EXCEPTIONS 232 __libcpp_relaxed_store(&flag, once_flag::_State_type(1)); 233 __libcpp_mutex_unlock(&mut); 234 func(arg); 235 __libcpp_mutex_lock(&mut); 236 __libcpp_atomic_store(&flag, ~once_flag::_State_type(0), 237 _AO_Release); 238 __libcpp_mutex_unlock(&mut); 239 __libcpp_condvar_broadcast(&cv); 240 #ifndef _LIBCPP_NO_EXCEPTIONS 241 } 242 catch (...) 243 { 244 __libcpp_mutex_lock(&mut); 245 __libcpp_relaxed_store(&flag, once_flag::_State_type(0)); 246 __libcpp_mutex_unlock(&mut); 247 __libcpp_condvar_broadcast(&cv); 248 throw; 249 } 250 #endif // _LIBCPP_NO_EXCEPTIONS 251 } 252 else 253 __libcpp_mutex_unlock(&mut); 254 #endif // !_LIBCPP_HAS_NO_THREADS 255 } 256 257 _LIBCPP_END_NAMESPACE_STD 258