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