xref: /llvm-project-15.0.7/libcxx/src/mutex.cpp (revision 0fd00a58)
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