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