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