xref: /freebsd/contrib/llvm-project/libcxx/src/mutex.cpp (revision 1b10e191f341111fad7be32ead11484dfd09b800)
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 
29 const defer_lock_t  defer_lock{};
30 const try_to_lock_t try_to_lock{};
31 const adopt_lock_t  adopt_lock{};
32 
33 // ~mutex is defined elsewhere
34 
35 void
36 mutex::lock()
37 {
38     int ec = __libcpp_mutex_lock(&__m_);
39     if (ec)
40         __throw_system_error(ec, "mutex lock failed");
41 }
42 
43 bool
44 mutex::try_lock() noexcept
45 {
46     return __libcpp_mutex_trylock(&__m_);
47 }
48 
49 void
50 mutex::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 
59 recursive_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 
66 recursive_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 
73 void
74 recursive_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 
81 void
82 recursive_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 
89 bool
90 recursive_mutex::try_lock() noexcept
91 {
92     return __libcpp_recursive_mutex_trylock(&__m_);
93 }
94 
95 // timed_mutex
96 
97 timed_mutex::timed_mutex()
98     : __locked_(false)
99 {
100 }
101 
102 timed_mutex::~timed_mutex()
103 {
104     lock_guard<mutex> _(__m_);
105 }
106 
107 void
108 timed_mutex::lock()
109 {
110     unique_lock<mutex> lk(__m_);
111     while (__locked_)
112         __cv_.wait(lk);
113     __locked_ = true;
114 }
115 
116 bool
117 timed_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 
128 void
129 timed_mutex::unlock() noexcept
130 {
131     lock_guard<mutex> _(__m_);
132     __locked_ = false;
133     __cv_.notify_one();
134 }
135 
136 // recursive_timed_mutex
137 
138 recursive_timed_mutex::recursive_timed_mutex()
139     : __count_(0),
140       __id_{}
141 {
142 }
143 
144 recursive_timed_mutex::~recursive_timed_mutex()
145 {
146     lock_guard<mutex> _(__m_);
147 }
148 
149 void
150 recursive_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 
167 bool
168 recursive_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 
183 void
184 recursive_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
204 static constinit __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER;
205 static constinit __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER;
206 #endif
207 
208 void __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