1 | // std::unique_lock implementation -*- C++ -*- |
2 | |
3 | // Copyright (C) 2008-2024 Free Software Foundation, Inc. |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free |
6 | // software; you can redistribute it and/or modify it under the |
7 | // terms of the GNU General Public License as published by the |
8 | // Free Software Foundation; either version 3, or (at your option) |
9 | // any later version. |
10 | |
11 | // This library is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | |
16 | // Under Section 7 of GPL version 3, you are granted additional |
17 | // permissions described in the GCC Runtime Library Exception, version |
18 | // 3.1, as published by the Free Software Foundation. |
19 | |
20 | // You should have received a copy of the GNU General Public License and |
21 | // a copy of the GCC Runtime Library Exception along with this program; |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
23 | // <http://www.gnu.org/licenses/>. |
24 | |
25 | /** @file bits/unique_lock.h |
26 | * This is an internal header file, included by other library headers. |
27 | * Do not attempt to use it directly. @headername{mutex} |
28 | */ |
29 | |
30 | #ifndef _GLIBCXX_UNIQUE_LOCK_H |
31 | #define _GLIBCXX_UNIQUE_LOCK_H 1 |
32 | |
33 | #pragma GCC system_header |
34 | |
35 | #if __cplusplus < 201103L |
36 | # include <bits/c++0x_warning.h> |
37 | #else |
38 | |
39 | #include <bits/chrono.h> |
40 | #include <bits/error_constants.h> // for std::errc |
41 | #include <bits/move.h> // for std::swap |
42 | #include <bits/std_mutex.h> // for std::defer_lock_t |
43 | |
44 | namespace std _GLIBCXX_VISIBILITY(default) |
45 | { |
46 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
47 | |
48 | /** @brief A movable scoped lock type. |
49 | * |
50 | * A unique_lock controls mutex ownership within a scope. Ownership of the |
51 | * mutex can be delayed until after construction and can be transferred |
52 | * to another unique_lock by move construction or move assignment. If a |
53 | * mutex lock is owned when the destructor runs ownership will be released. |
54 | * |
55 | * @headerfile mutex |
56 | * @ingroup mutexes |
57 | * @since C++11 |
58 | */ |
59 | template<typename _Mutex> |
60 | class unique_lock |
61 | { |
62 | public: |
63 | typedef _Mutex mutex_type; |
64 | |
65 | unique_lock() noexcept |
66 | : _M_device(0), _M_owns(false) |
67 | { } |
68 | |
69 | [[__nodiscard__]] |
70 | explicit unique_lock(mutex_type& __m) |
71 | : _M_device(std::__addressof(__m)), _M_owns(false) |
72 | { |
73 | lock(); |
74 | _M_owns = true; |
75 | } |
76 | |
77 | unique_lock(mutex_type& __m, defer_lock_t) noexcept |
78 | : _M_device(std::__addressof(__m)), _M_owns(false) |
79 | { } |
80 | |
81 | [[__nodiscard__]] |
82 | unique_lock(mutex_type& __m, try_to_lock_t) |
83 | : _M_device(std::__addressof(__m)), _M_owns(_M_device->try_lock()) |
84 | { } |
85 | |
86 | [[__nodiscard__]] |
87 | unique_lock(mutex_type& __m, adopt_lock_t) noexcept |
88 | : _M_device(std::__addressof(__m)), _M_owns(true) |
89 | { |
90 | // XXX calling thread owns mutex |
91 | } |
92 | |
93 | template<typename _Clock, typename _Duration> |
94 | [[__nodiscard__]] |
95 | unique_lock(mutex_type& __m, |
96 | const chrono::time_point<_Clock, _Duration>& __atime) |
97 | : _M_device(std::__addressof(__m)), |
98 | _M_owns(_M_device->try_lock_until(__atime)) |
99 | { } |
100 | |
101 | template<typename _Rep, typename _Period> |
102 | [[__nodiscard__]] |
103 | unique_lock(mutex_type& __m, |
104 | const chrono::duration<_Rep, _Period>& __rtime) |
105 | : _M_device(std::__addressof(__m)), |
106 | _M_owns(_M_device->try_lock_for(__rtime)) |
107 | { } |
108 | |
109 | ~unique_lock() |
110 | { |
111 | if (_M_owns) |
112 | unlock(); |
113 | } |
114 | |
115 | unique_lock(const unique_lock&) = delete; |
116 | unique_lock& operator=(const unique_lock&) = delete; |
117 | |
118 | unique_lock(unique_lock&& __u) noexcept |
119 | : _M_device(__u._M_device), _M_owns(__u._M_owns) |
120 | { |
121 | __u._M_device = 0; |
122 | __u._M_owns = false; |
123 | } |
124 | |
125 | unique_lock& operator=(unique_lock&& __u) noexcept |
126 | { |
127 | if(_M_owns) |
128 | unlock(); |
129 | |
130 | unique_lock(std::move(__u)).swap(*this); |
131 | |
132 | __u._M_device = 0; |
133 | __u._M_owns = false; |
134 | |
135 | return *this; |
136 | } |
137 | |
138 | void |
139 | lock() |
140 | { |
141 | if (!_M_device) |
142 | __throw_system_error(int(errc::operation_not_permitted)); |
143 | else if (_M_owns) |
144 | __throw_system_error(int(errc::resource_deadlock_would_occur)); |
145 | else |
146 | { |
147 | _M_device->lock(); |
148 | _M_owns = true; |
149 | } |
150 | } |
151 | |
152 | _GLIBCXX_NODISCARD |
153 | bool |
154 | try_lock() |
155 | { |
156 | if (!_M_device) |
157 | __throw_system_error(int(errc::operation_not_permitted)); |
158 | else if (_M_owns) |
159 | __throw_system_error(int(errc::resource_deadlock_would_occur)); |
160 | else |
161 | { |
162 | _M_owns = _M_device->try_lock(); |
163 | return _M_owns; |
164 | } |
165 | } |
166 | |
167 | template<typename _Clock, typename _Duration> |
168 | _GLIBCXX_NODISCARD |
169 | bool |
170 | try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime) |
171 | { |
172 | if (!_M_device) |
173 | __throw_system_error(int(errc::operation_not_permitted)); |
174 | else if (_M_owns) |
175 | __throw_system_error(int(errc::resource_deadlock_would_occur)); |
176 | else |
177 | { |
178 | _M_owns = _M_device->try_lock_until(__atime); |
179 | return _M_owns; |
180 | } |
181 | } |
182 | |
183 | template<typename _Rep, typename _Period> |
184 | _GLIBCXX_NODISCARD |
185 | bool |
186 | try_lock_for(const chrono::duration<_Rep, _Period>& __rtime) |
187 | { |
188 | if (!_M_device) |
189 | __throw_system_error(int(errc::operation_not_permitted)); |
190 | else if (_M_owns) |
191 | __throw_system_error(int(errc::resource_deadlock_would_occur)); |
192 | else |
193 | { |
194 | _M_owns = _M_device->try_lock_for(__rtime); |
195 | return _M_owns; |
196 | } |
197 | } |
198 | |
199 | void |
200 | unlock() |
201 | { |
202 | if (!_M_owns) |
203 | __throw_system_error(int(errc::operation_not_permitted)); |
204 | else if (_M_device) |
205 | { |
206 | _M_device->unlock(); |
207 | _M_owns = false; |
208 | } |
209 | } |
210 | |
211 | void |
212 | swap(unique_lock& __u) noexcept |
213 | { |
214 | std::swap(_M_device, __u._M_device); |
215 | std::swap(_M_owns, __u._M_owns); |
216 | } |
217 | |
218 | mutex_type* |
219 | release() noexcept |
220 | { |
221 | mutex_type* __ret = _M_device; |
222 | _M_device = 0; |
223 | _M_owns = false; |
224 | return __ret; |
225 | } |
226 | |
227 | _GLIBCXX_NODISCARD |
228 | bool |
229 | owns_lock() const noexcept |
230 | { return _M_owns; } |
231 | |
232 | explicit operator bool() const noexcept |
233 | { return owns_lock(); } |
234 | |
235 | _GLIBCXX_NODISCARD |
236 | mutex_type* |
237 | mutex() const noexcept |
238 | { return _M_device; } |
239 | |
240 | private: |
241 | mutex_type* _M_device; |
242 | bool _M_owns; |
243 | }; |
244 | |
245 | /// Swap overload for unique_lock objects. |
246 | /// @relates unique_lock |
247 | template<typename _Mutex> |
248 | inline void |
249 | swap(unique_lock<_Mutex>& __x, unique_lock<_Mutex>& __y) noexcept |
250 | { __x.swap(__y); } |
251 | |
252 | _GLIBCXX_END_NAMESPACE_VERSION |
253 | } // namespace |
254 | |
255 | #endif // C++11 |
256 | #endif // _GLIBCXX_UNIQUE_LOCK_H |
257 | |