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