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