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