1 | // shared_ptr atomic access -*- C++ -*- |
2 | |
3 | // Copyright (C) 2014-2022 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/shared_ptr_atomic.h |
26 | * This is an internal header file, included by other library headers. |
27 | * Do not attempt to use it directly. @headername{memory} |
28 | */ |
29 | |
30 | #ifndef _SHARED_PTR_ATOMIC_H |
31 | #define _SHARED_PTR_ATOMIC_H 1 |
32 | |
33 | #include <bits/atomic_base.h> |
34 | |
35 | // Annotations for the custom locking in atomic<shared_ptr<T>>. |
36 | #if defined _GLIBCXX_TSAN && __has_include(<sanitizer/tsan_interface.h>) |
37 | #include <sanitizer/tsan_interface.h> |
38 | #define _GLIBCXX_TSAN_MUTEX_DESTROY(X) \ |
39 | __tsan_mutex_destroy(X, __tsan_mutex_not_static) |
40 | #define _GLIBCXX_TSAN_MUTEX_TRY_LOCK(X) \ |
41 | __tsan_mutex_pre_lock(X, __tsan_mutex_not_static|__tsan_mutex_try_lock) |
42 | #define _GLIBCXX_TSAN_MUTEX_TRY_LOCK_FAILED(X) __tsan_mutex_post_lock(X, \ |
43 | __tsan_mutex_not_static|__tsan_mutex_try_lock_failed, 0) |
44 | #define _GLIBCXX_TSAN_MUTEX_LOCKED(X) \ |
45 | __tsan_mutex_post_lock(X, __tsan_mutex_not_static, 0) |
46 | #define _GLIBCXX_TSAN_MUTEX_PRE_UNLOCK(X) __tsan_mutex_pre_unlock(X, 0) |
47 | #define _GLIBCXX_TSAN_MUTEX_POST_UNLOCK(X) __tsan_mutex_post_unlock(X, 0) |
48 | #define _GLIBCXX_TSAN_MUTEX_PRE_SIGNAL(X) __tsan_mutex_pre_signal(X, 0) |
49 | #define _GLIBCXX_TSAN_MUTEX_POST_SIGNAL(X) __tsan_mutex_post_signal(X, 0) |
50 | #else |
51 | #define _GLIBCXX_TSAN_MUTEX_DESTROY(X) |
52 | #define _GLIBCXX_TSAN_MUTEX_TRY_LOCK(X) |
53 | #define _GLIBCXX_TSAN_MUTEX_TRY_LOCK_FAILED(X) |
54 | #define _GLIBCXX_TSAN_MUTEX_LOCKED(X) |
55 | #define _GLIBCXX_TSAN_MUTEX_PRE_UNLOCK(X) |
56 | #define _GLIBCXX_TSAN_MUTEX_POST_UNLOCK(X) |
57 | #define _GLIBCXX_TSAN_MUTEX_PRE_SIGNAL(X) |
58 | #define _GLIBCXX_TSAN_MUTEX_POST_SIGNAL(X) |
59 | #endif |
60 | |
61 | namespace std _GLIBCXX_VISIBILITY(default) |
62 | { |
63 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
64 | |
65 | /** |
66 | * @addtogroup pointer_abstractions |
67 | * @{ |
68 | */ |
69 | /// @relates shared_ptr @{ |
70 | |
71 | /// @cond undocumented |
72 | |
73 | struct _Sp_locker |
74 | { |
75 | _Sp_locker(const _Sp_locker&) = delete; |
76 | _Sp_locker& operator=(const _Sp_locker&) = delete; |
77 | |
78 | #ifdef __GTHREADS |
79 | explicit |
80 | _Sp_locker(const void*) noexcept; |
81 | _Sp_locker(const void*, const void*) noexcept; |
82 | ~_Sp_locker(); |
83 | |
84 | private: |
85 | unsigned char _M_key1; |
86 | unsigned char _M_key2; |
87 | #else |
88 | explicit _Sp_locker(const void*, const void* = nullptr) { } |
89 | #endif |
90 | }; |
91 | |
92 | /// @endcond |
93 | |
94 | /** |
95 | * @brief Report whether shared_ptr atomic operations are lock-free. |
96 | * @param __p A non-null pointer to a shared_ptr object. |
97 | * @return True if atomic access to @c *__p is lock-free, false otherwise. |
98 | * @{ |
99 | */ |
100 | template<typename _Tp, _Lock_policy _Lp> |
101 | inline bool |
102 | atomic_is_lock_free(const __shared_ptr<_Tp, _Lp>* __p) |
103 | { |
104 | #ifdef __GTHREADS |
105 | return __gthread_active_p() == 0; |
106 | #else |
107 | return true; |
108 | #endif |
109 | } |
110 | |
111 | template<typename _Tp> |
112 | inline bool |
113 | atomic_is_lock_free(const shared_ptr<_Tp>* __p) |
114 | { return std::atomic_is_lock_free<_Tp, __default_lock_policy>(__p); } |
115 | |
116 | /// @} |
117 | |
118 | /** |
119 | * @brief Atomic load for shared_ptr objects. |
120 | * @param __p A non-null pointer to a shared_ptr object. |
121 | * @return @c *__p |
122 | * |
123 | * The memory order shall not be @c memory_order_release or |
124 | * @c memory_order_acq_rel. |
125 | * @{ |
126 | */ |
127 | template<typename _Tp> |
128 | inline shared_ptr<_Tp> |
129 | atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) |
130 | { |
131 | _Sp_locker __lock{__p}; |
132 | return *__p; |
133 | } |
134 | |
135 | template<typename _Tp> |
136 | inline shared_ptr<_Tp> |
137 | atomic_load(const shared_ptr<_Tp>* __p) |
138 | { return std::atomic_load_explicit(__p, memory_order_seq_cst); } |
139 | |
140 | template<typename _Tp, _Lock_policy _Lp> |
141 | inline __shared_ptr<_Tp, _Lp> |
142 | atomic_load_explicit(const __shared_ptr<_Tp, _Lp>* __p, memory_order) |
143 | { |
144 | _Sp_locker __lock{__p}; |
145 | return *__p; |
146 | } |
147 | |
148 | template<typename _Tp, _Lock_policy _Lp> |
149 | inline __shared_ptr<_Tp, _Lp> |
150 | atomic_load(const __shared_ptr<_Tp, _Lp>* __p) |
151 | { return std::atomic_load_explicit(__p, memory_order_seq_cst); } |
152 | /// @} |
153 | |
154 | /** |
155 | * @brief Atomic store for shared_ptr objects. |
156 | * @param __p A non-null pointer to a shared_ptr object. |
157 | * @param __r The value to store. |
158 | * |
159 | * The memory order shall not be @c memory_order_acquire or |
160 | * @c memory_order_acq_rel. |
161 | * @{ |
162 | */ |
163 | template<typename _Tp> |
164 | inline void |
165 | atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, |
166 | memory_order) |
167 | { |
168 | _Sp_locker __lock{__p}; |
169 | __p->swap(__r); // use swap so that **__p not destroyed while lock held |
170 | } |
171 | |
172 | template<typename _Tp> |
173 | inline void |
174 | atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) |
175 | { std::atomic_store_explicit(__p, std::move(__r), memory_order_seq_cst); } |
176 | |
177 | template<typename _Tp, _Lock_policy _Lp> |
178 | inline void |
179 | atomic_store_explicit(__shared_ptr<_Tp, _Lp>* __p, |
180 | __shared_ptr<_Tp, _Lp> __r, |
181 | memory_order) |
182 | { |
183 | _Sp_locker __lock{__p}; |
184 | __p->swap(__r); // use swap so that **__p not destroyed while lock held |
185 | } |
186 | |
187 | template<typename _Tp, _Lock_policy _Lp> |
188 | inline void |
189 | atomic_store(__shared_ptr<_Tp, _Lp>* __p, __shared_ptr<_Tp, _Lp> __r) |
190 | { std::atomic_store_explicit(__p, std::move(__r), memory_order_seq_cst); } |
191 | /// @} |
192 | |
193 | /** |
194 | * @brief Atomic exchange for shared_ptr objects. |
195 | * @param __p A non-null pointer to a shared_ptr object. |
196 | * @param __r New value to store in @c *__p. |
197 | * @return The original value of @c *__p |
198 | * @{ |
199 | */ |
200 | template<typename _Tp> |
201 | inline shared_ptr<_Tp> |
202 | atomic_exchange_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, |
203 | memory_order) |
204 | { |
205 | _Sp_locker __lock{__p}; |
206 | __p->swap(__r); |
207 | return __r; |
208 | } |
209 | |
210 | template<typename _Tp> |
211 | inline shared_ptr<_Tp> |
212 | atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) |
213 | { |
214 | return std::atomic_exchange_explicit(__p, std::move(__r), |
215 | memory_order_seq_cst); |
216 | } |
217 | |
218 | template<typename _Tp, _Lock_policy _Lp> |
219 | inline __shared_ptr<_Tp, _Lp> |
220 | atomic_exchange_explicit(__shared_ptr<_Tp, _Lp>* __p, |
221 | __shared_ptr<_Tp, _Lp> __r, |
222 | memory_order) |
223 | { |
224 | _Sp_locker __lock{__p}; |
225 | __p->swap(__r); |
226 | return __r; |
227 | } |
228 | |
229 | template<typename _Tp, _Lock_policy _Lp> |
230 | inline __shared_ptr<_Tp, _Lp> |
231 | atomic_exchange(__shared_ptr<_Tp, _Lp>* __p, __shared_ptr<_Tp, _Lp> __r) |
232 | { |
233 | return std::atomic_exchange_explicit(__p, std::move(__r), |
234 | memory_order_seq_cst); |
235 | } |
236 | /// @} |
237 | |
238 | /** |
239 | * @brief Atomic compare-and-swap for shared_ptr objects. |
240 | * @param __p A non-null pointer to a shared_ptr object. |
241 | * @param __v A non-null pointer to a shared_ptr object. |
242 | * @param __w A non-null pointer to a shared_ptr object. |
243 | * @return True if @c *__p was equivalent to @c *__v, false otherwise. |
244 | * |
245 | * The memory order for failure shall not be @c memory_order_release or |
246 | * @c memory_order_acq_rel, or stronger than the memory order for success. |
247 | * @{ |
248 | */ |
249 | template<typename _Tp> |
250 | bool |
251 | atomic_compare_exchange_strong_explicit(shared_ptr<_Tp>* __p, |
252 | shared_ptr<_Tp>* __v, |
253 | shared_ptr<_Tp> __w, |
254 | memory_order, |
255 | memory_order) |
256 | { |
257 | shared_ptr<_Tp> __x; // goes out of scope after __lock |
258 | _Sp_locker __lock{__p, __v}; |
259 | owner_less<shared_ptr<_Tp>> __less; |
260 | if (*__p == *__v && !__less(*__p, *__v) && !__less(*__v, *__p)) |
261 | { |
262 | __x = std::move(*__p); |
263 | *__p = std::move(__w); |
264 | return true; |
265 | } |
266 | __x = std::move(*__v); |
267 | *__v = *__p; |
268 | return false; |
269 | } |
270 | |
271 | template<typename _Tp> |
272 | inline bool |
273 | atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, |
274 | shared_ptr<_Tp> __w) |
275 | { |
276 | return std::atomic_compare_exchange_strong_explicit(__p, __v, |
277 | std::move(__w), memory_order_seq_cst, memory_order_seq_cst); |
278 | } |
279 | |
280 | template<typename _Tp> |
281 | inline bool |
282 | atomic_compare_exchange_weak_explicit(shared_ptr<_Tp>* __p, |
283 | shared_ptr<_Tp>* __v, |
284 | shared_ptr<_Tp> __w, |
285 | memory_order __success, |
286 | memory_order __failure) |
287 | { |
288 | return std::atomic_compare_exchange_strong_explicit(__p, __v, |
289 | std::move(__w), __success, __failure); |
290 | } |
291 | |
292 | template<typename _Tp> |
293 | inline bool |
294 | atomic_compare_exchange_weak(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, |
295 | shared_ptr<_Tp> __w) |
296 | { |
297 | return std::atomic_compare_exchange_weak_explicit(__p, __v, |
298 | std::move(__w), memory_order_seq_cst, memory_order_seq_cst); |
299 | } |
300 | |
301 | template<typename _Tp, _Lock_policy _Lp> |
302 | bool |
303 | atomic_compare_exchange_strong_explicit(__shared_ptr<_Tp, _Lp>* __p, |
304 | __shared_ptr<_Tp, _Lp>* __v, |
305 | __shared_ptr<_Tp, _Lp> __w, |
306 | memory_order, |
307 | memory_order) |
308 | { |
309 | __shared_ptr<_Tp, _Lp> __x; // goes out of scope after __lock |
310 | _Sp_locker __lock{__p, __v}; |
311 | owner_less<__shared_ptr<_Tp, _Lp>> __less; |
312 | if (*__p == *__v && !__less(*__p, *__v) && !__less(*__v, *__p)) |
313 | { |
314 | __x = std::move(*__p); |
315 | *__p = std::move(__w); |
316 | return true; |
317 | } |
318 | __x = std::move(*__v); |
319 | *__v = *__p; |
320 | return false; |
321 | } |
322 | |
323 | template<typename _Tp, _Lock_policy _Lp> |
324 | inline bool |
325 | atomic_compare_exchange_strong(__shared_ptr<_Tp, _Lp>* __p, |
326 | __shared_ptr<_Tp, _Lp>* __v, |
327 | __shared_ptr<_Tp, _Lp> __w) |
328 | { |
329 | return std::atomic_compare_exchange_strong_explicit(__p, __v, |
330 | std::move(__w), memory_order_seq_cst, memory_order_seq_cst); |
331 | } |
332 | |
333 | template<typename _Tp, _Lock_policy _Lp> |
334 | inline bool |
335 | atomic_compare_exchange_weak_explicit(__shared_ptr<_Tp, _Lp>* __p, |
336 | __shared_ptr<_Tp, _Lp>* __v, |
337 | __shared_ptr<_Tp, _Lp> __w, |
338 | memory_order __success, |
339 | memory_order __failure) |
340 | { |
341 | return std::atomic_compare_exchange_strong_explicit(__p, __v, |
342 | std::move(__w), __success, __failure); |
343 | } |
344 | |
345 | template<typename _Tp, _Lock_policy _Lp> |
346 | inline bool |
347 | atomic_compare_exchange_weak(__shared_ptr<_Tp, _Lp>* __p, |
348 | __shared_ptr<_Tp, _Lp>* __v, |
349 | __shared_ptr<_Tp, _Lp> __w) |
350 | { |
351 | return std::atomic_compare_exchange_weak_explicit(__p, __v, |
352 | std::move(__w), memory_order_seq_cst, memory_order_seq_cst); |
353 | } |
354 | /// @} |
355 | |
356 | #if __cplusplus >= 202002L |
357 | # define __cpp_lib_atomic_shared_ptr 201711L |
358 | template<typename _Tp> |
359 | class atomic; |
360 | |
361 | template<typename _Up> |
362 | static constexpr bool __is_shared_ptr = false; |
363 | template<typename _Up> |
364 | static constexpr bool __is_shared_ptr<shared_ptr<_Up>> = true; |
365 | |
366 | template<typename _Tp> |
367 | class _Sp_atomic |
368 | { |
369 | using value_type = _Tp; |
370 | |
371 | friend class atomic<_Tp>; |
372 | |
373 | // An atomic version of __shared_count<> and __weak_count<>. |
374 | // Stores a _Sp_counted_base<>* but uses the LSB as a lock. |
375 | struct _Atomic_count |
376 | { |
377 | // Either __shared_count<> or __weak_count<> |
378 | using __count_type = decltype(_Tp::_M_refcount); |
379 | |
380 | // _Sp_counted_base<>* |
381 | using pointer = decltype(__count_type::_M_pi); |
382 | |
383 | // Ensure we can use the LSB as the lock bit. |
384 | static_assert(alignof(remove_pointer_t<pointer>) > 1); |
385 | |
386 | constexpr _Atomic_count() noexcept = default; |
387 | |
388 | explicit |
389 | _Atomic_count(__count_type&& __c) noexcept |
390 | : _M_val(reinterpret_cast<uintptr_t>(__c._M_pi)) |
391 | { |
392 | __c._M_pi = nullptr; |
393 | } |
394 | |
395 | ~_Atomic_count() |
396 | { |
397 | auto __val = _M_val.load(memory_order_relaxed); |
398 | _GLIBCXX_TSAN_MUTEX_DESTROY(&_M_val); |
399 | __glibcxx_assert(!(__val & _S_lock_bit)); |
400 | if (auto __pi = reinterpret_cast<pointer>(__val)) |
401 | { |
402 | if constexpr (__is_shared_ptr<_Tp>) |
403 | __pi->_M_release(); |
404 | else |
405 | __pi->_M_weak_release(); |
406 | } |
407 | } |
408 | |
409 | _Atomic_count(const _Atomic_count&) = delete; |
410 | _Atomic_count& operator=(const _Atomic_count&) = delete; |
411 | |
412 | // Precondition: Caller does not hold lock! |
413 | // Returns the raw pointer value without the lock bit set. |
414 | pointer |
415 | lock(memory_order __o) const noexcept |
416 | { |
417 | // To acquire the lock we flip the LSB from 0 to 1. |
418 | |
419 | auto __current = _M_val.load(memory_order_relaxed); |
420 | while (__current & _S_lock_bit) |
421 | { |
422 | #if __cpp_lib_atomic_wait |
423 | __detail::__thread_relax(); |
424 | #endif |
425 | __current = _M_val.load(memory_order_relaxed); |
426 | } |
427 | |
428 | _GLIBCXX_TSAN_MUTEX_TRY_LOCK(&_M_val); |
429 | |
430 | while (!_M_val.compare_exchange_strong(__current, |
431 | __current | _S_lock_bit, |
432 | __o, |
433 | memory_order_relaxed)) |
434 | { |
435 | _GLIBCXX_TSAN_MUTEX_TRY_LOCK_FAILED(&_M_val); |
436 | #if __cpp_lib_atomic_wait |
437 | __detail::__thread_relax(); |
438 | #endif |
439 | __current = __current & ~_S_lock_bit; |
440 | _GLIBCXX_TSAN_MUTEX_TRY_LOCK(&_M_val); |
441 | } |
442 | _GLIBCXX_TSAN_MUTEX_LOCKED(&_M_val); |
443 | return reinterpret_cast<pointer>(__current); |
444 | } |
445 | |
446 | // Precondition: caller holds lock! |
447 | void |
448 | unlock(memory_order __o) const noexcept |
449 | { |
450 | _GLIBCXX_TSAN_MUTEX_PRE_UNLOCK(&_M_val); |
451 | _M_val.fetch_sub(1, __o); |
452 | _GLIBCXX_TSAN_MUTEX_POST_UNLOCK(&_M_val); |
453 | } |
454 | |
455 | // Swaps the values of *this and __c, and unlocks *this. |
456 | // Precondition: caller holds lock! |
457 | void |
458 | _M_swap_unlock(__count_type& __c, memory_order __o) noexcept |
459 | { |
460 | if (__o != memory_order_seq_cst) |
461 | __o = memory_order_release; |
462 | auto __x = reinterpret_cast<uintptr_t>(__c._M_pi); |
463 | _GLIBCXX_TSAN_MUTEX_PRE_UNLOCK(&_M_val); |
464 | __x = _M_val.exchange(__x, __o); |
465 | _GLIBCXX_TSAN_MUTEX_POST_UNLOCK(&_M_val); |
466 | __c._M_pi = reinterpret_cast<pointer>(__x & ~_S_lock_bit); |
467 | } |
468 | |
469 | #if __cpp_lib_atomic_wait |
470 | // Precondition: caller holds lock! |
471 | void |
472 | _M_wait_unlock(memory_order __o) const noexcept |
473 | { |
474 | _GLIBCXX_TSAN_MUTEX_PRE_UNLOCK(&_M_val); |
475 | auto __v = _M_val.fetch_sub(1, memory_order_relaxed); |
476 | _GLIBCXX_TSAN_MUTEX_POST_UNLOCK(&_M_val); |
477 | _M_val.wait(__v & ~_S_lock_bit, __o); |
478 | } |
479 | |
480 | void |
481 | notify_one() noexcept |
482 | { |
483 | _GLIBCXX_TSAN_MUTEX_PRE_SIGNAL(&_M_val); |
484 | _M_val.notify_one(); |
485 | _GLIBCXX_TSAN_MUTEX_POST_SIGNAL(&_M_val); |
486 | } |
487 | |
488 | void |
489 | notify_all() noexcept |
490 | { |
491 | _GLIBCXX_TSAN_MUTEX_PRE_SIGNAL(&_M_val); |
492 | _M_val.notify_all(); |
493 | _GLIBCXX_TSAN_MUTEX_POST_SIGNAL(&_M_val); |
494 | } |
495 | #endif |
496 | |
497 | private: |
498 | mutable __atomic_base<uintptr_t> _M_val{0}; |
499 | static constexpr uintptr_t _S_lock_bit{1}; |
500 | }; |
501 | |
502 | typename _Tp::element_type* _M_ptr = nullptr; |
503 | _Atomic_count _M_refcount; |
504 | |
505 | static typename _Atomic_count::pointer |
506 | _S_add_ref(typename _Atomic_count::pointer __p) |
507 | { |
508 | if (__p) |
509 | { |
510 | if constexpr (__is_shared_ptr<_Tp>) |
511 | __p->_M_add_ref_copy(); |
512 | else |
513 | __p->_M_weak_add_ref(); |
514 | } |
515 | return __p; |
516 | } |
517 | |
518 | constexpr _Sp_atomic() noexcept = default; |
519 | |
520 | explicit |
521 | _Sp_atomic(value_type __r) noexcept |
522 | : _M_ptr(__r._M_ptr), _M_refcount(std::move(__r._M_refcount)) |
523 | { } |
524 | |
525 | ~_Sp_atomic() = default; |
526 | |
527 | _Sp_atomic(const _Sp_atomic&) = delete; |
528 | void operator=(const _Sp_atomic&) = delete; |
529 | |
530 | value_type |
531 | load(memory_order __o) const noexcept |
532 | { |
533 | __glibcxx_assert(__o != memory_order_release |
534 | && __o != memory_order_acq_rel); |
535 | // Ensure that the correct value of _M_ptr is visible after locking., |
536 | // by upgrading relaxed or consume to acquire. |
537 | if (__o != memory_order_seq_cst) |
538 | __o = memory_order_acquire; |
539 | |
540 | value_type __ret; |
541 | auto __pi = _M_refcount.lock(__o); |
542 | __ret._M_ptr = _M_ptr; |
543 | __ret._M_refcount._M_pi = _S_add_ref(__pi); |
544 | _M_refcount.unlock(memory_order_relaxed); |
545 | return __ret; |
546 | } |
547 | |
548 | void |
549 | swap(value_type& __r, memory_order __o) noexcept |
550 | { |
551 | _M_refcount.lock(memory_order_acquire); |
552 | std::swap(_M_ptr, __r._M_ptr); |
553 | _M_refcount._M_swap_unlock(__r._M_refcount, __o); |
554 | } |
555 | |
556 | bool |
557 | compare_exchange_strong(value_type& __expected, value_type __desired, |
558 | memory_order __o, memory_order __o2) noexcept |
559 | { |
560 | bool __result = true; |
561 | auto __pi = _M_refcount.lock(memory_order_acquire); |
562 | if (_M_ptr == __expected._M_ptr |
563 | && __pi == __expected._M_refcount._M_pi) |
564 | { |
565 | _M_ptr = __desired._M_ptr; |
566 | _M_refcount._M_swap_unlock(__desired._M_refcount, __o); |
567 | } |
568 | else |
569 | { |
570 | _Tp __sink = std::move(__expected); |
571 | __expected._M_ptr = _M_ptr; |
572 | __expected._M_refcount._M_pi = _S_add_ref(__pi); |
573 | _M_refcount.unlock(__o2); |
574 | __result = false; |
575 | } |
576 | return __result; |
577 | } |
578 | |
579 | #if __cpp_lib_atomic_wait |
580 | void |
581 | wait(value_type __old, memory_order __o) const noexcept |
582 | { |
583 | auto __pi = _M_refcount.lock(memory_order_acquire); |
584 | if (_M_ptr == __old._M_ptr && __pi == __old._M_refcount._M_pi) |
585 | _M_refcount._M_wait_unlock(__o); |
586 | else |
587 | _M_refcount.unlock(memory_order_relaxed); |
588 | } |
589 | |
590 | void |
591 | notify_one() noexcept |
592 | { |
593 | _M_refcount.notify_one(); |
594 | } |
595 | |
596 | void |
597 | notify_all() noexcept |
598 | { |
599 | _M_refcount.notify_all(); |
600 | } |
601 | #endif |
602 | }; |
603 | |
604 | template<typename _Tp> |
605 | class atomic<shared_ptr<_Tp>> |
606 | { |
607 | public: |
608 | using value_type = shared_ptr<_Tp>; |
609 | |
610 | static constexpr bool is_always_lock_free = false; |
611 | |
612 | bool |
613 | is_lock_free() const noexcept |
614 | { return false; } |
615 | |
616 | constexpr atomic() noexcept = default; |
617 | |
618 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
619 | // 3661. constinit atomic<shared_ptr<T>> a(nullptr); should work |
620 | constexpr atomic(nullptr_t) noexcept : atomic() { } |
621 | |
622 | atomic(shared_ptr<_Tp> __r) noexcept |
623 | : _M_impl(std::move(__r)) |
624 | { } |
625 | |
626 | atomic(const atomic&) = delete; |
627 | void operator=(const atomic&) = delete; |
628 | |
629 | shared_ptr<_Tp> |
630 | load(memory_order __o = memory_order_seq_cst) const noexcept |
631 | { return _M_impl.load(__o); } |
632 | |
633 | operator shared_ptr<_Tp>() const noexcept |
634 | { return _M_impl.load(memory_order_seq_cst); } |
635 | |
636 | void |
637 | store(shared_ptr<_Tp> __desired, |
638 | memory_order __o = memory_order_seq_cst) noexcept |
639 | { _M_impl.swap(__desired, __o); } |
640 | |
641 | void |
642 | operator=(shared_ptr<_Tp> __desired) noexcept |
643 | { _M_impl.swap(__desired, memory_order_seq_cst); } |
644 | |
645 | // _GLIBCXX_RESOLVE_LIB_DEFECTS |
646 | // 3893. LWG 3661 broke atomic<shared_ptr<T>> a; a = nullptr; |
647 | void |
648 | operator=(nullptr_t) noexcept |
649 | { store(nullptr); } |
650 | |
651 | shared_ptr<_Tp> |
652 | exchange(shared_ptr<_Tp> __desired, |
653 | memory_order __o = memory_order_seq_cst) noexcept |
654 | { |
655 | _M_impl.swap(__desired, __o); |
656 | return __desired; |
657 | } |
658 | |
659 | bool |
660 | compare_exchange_strong(shared_ptr<_Tp>& __expected, |
661 | shared_ptr<_Tp> __desired, |
662 | memory_order __o, memory_order __o2) noexcept |
663 | { |
664 | return _M_impl.compare_exchange_strong(__expected, __desired, __o, __o2); |
665 | } |
666 | |
667 | bool |
668 | compare_exchange_strong(value_type& __expected, value_type __desired, |
669 | memory_order __o = memory_order_seq_cst) noexcept |
670 | { |
671 | memory_order __o2; |
672 | switch (__o) |
673 | { |
674 | case memory_order_acq_rel: |
675 | __o2 = memory_order_acquire; |
676 | break; |
677 | case memory_order_release: |
678 | __o2 = memory_order_relaxed; |
679 | break; |
680 | default: |
681 | __o2 = __o; |
682 | } |
683 | return compare_exchange_strong(__expected, std::move(__desired), |
684 | __o, __o2); |
685 | } |
686 | |
687 | bool |
688 | compare_exchange_weak(value_type& __expected, value_type __desired, |
689 | memory_order __o, memory_order __o2) noexcept |
690 | { |
691 | return compare_exchange_strong(__expected, std::move(__desired), |
692 | __o, __o2); |
693 | } |
694 | |
695 | bool |
696 | compare_exchange_weak(value_type& __expected, value_type __desired, |
697 | memory_order __o = memory_order_seq_cst) noexcept |
698 | { |
699 | return compare_exchange_strong(__expected, std::move(__desired), __o); |
700 | } |
701 | |
702 | #if __cpp_lib_atomic_wait |
703 | void |
704 | wait(value_type __old, |
705 | memory_order __o = memory_order_seq_cst) const noexcept |
706 | { |
707 | _M_impl.wait(std::move(__old), __o); |
708 | } |
709 | |
710 | void |
711 | notify_one() noexcept |
712 | { |
713 | _M_impl.notify_one(); |
714 | } |
715 | |
716 | void |
717 | notify_all() noexcept |
718 | { |
719 | _M_impl.notify_all(); |
720 | } |
721 | #endif |
722 | |
723 | private: |
724 | _Sp_atomic<shared_ptr<_Tp>> _M_impl; |
725 | }; |
726 | |
727 | template<typename _Tp> |
728 | class atomic<weak_ptr<_Tp>> |
729 | { |
730 | public: |
731 | using value_type = weak_ptr<_Tp>; |
732 | |
733 | static constexpr bool is_always_lock_free = false; |
734 | |
735 | bool |
736 | is_lock_free() const noexcept |
737 | { return false; } |
738 | |
739 | constexpr atomic() noexcept = default; |
740 | |
741 | atomic(weak_ptr<_Tp> __r) noexcept |
742 | : _M_impl(move(__r)) |
743 | { } |
744 | |
745 | atomic(const atomic&) = delete; |
746 | void operator=(const atomic&) = delete; |
747 | |
748 | weak_ptr<_Tp> |
749 | load(memory_order __o = memory_order_seq_cst) const noexcept |
750 | { return _M_impl.load(__o); } |
751 | |
752 | operator weak_ptr<_Tp>() const noexcept |
753 | { return _M_impl.load(memory_order_seq_cst); } |
754 | |
755 | void |
756 | store(weak_ptr<_Tp> __desired, |
757 | memory_order __o = memory_order_seq_cst) noexcept |
758 | { _M_impl.swap(__desired, __o); } |
759 | |
760 | void |
761 | operator=(weak_ptr<_Tp> __desired) noexcept |
762 | { _M_impl.swap(__desired, memory_order_seq_cst); } |
763 | |
764 | weak_ptr<_Tp> |
765 | exchange(weak_ptr<_Tp> __desired, |
766 | memory_order __o = memory_order_seq_cst) noexcept |
767 | { |
768 | _M_impl.swap(__desired, __o); |
769 | return __desired; |
770 | } |
771 | |
772 | bool |
773 | compare_exchange_strong(weak_ptr<_Tp>& __expected, |
774 | weak_ptr<_Tp> __desired, |
775 | memory_order __o, memory_order __o2) noexcept |
776 | { |
777 | return _M_impl.compare_exchange_strong(__expected, __desired, __o, __o2); |
778 | } |
779 | |
780 | bool |
781 | compare_exchange_strong(value_type& __expected, value_type __desired, |
782 | memory_order __o = memory_order_seq_cst) noexcept |
783 | { |
784 | memory_order __o2; |
785 | switch (__o) |
786 | { |
787 | case memory_order_acq_rel: |
788 | __o2 = memory_order_acquire; |
789 | break; |
790 | case memory_order_release: |
791 | __o2 = memory_order_relaxed; |
792 | break; |
793 | default: |
794 | __o2 = __o; |
795 | } |
796 | return compare_exchange_strong(__expected, std::move(__desired), |
797 | __o, __o2); |
798 | } |
799 | |
800 | bool |
801 | compare_exchange_weak(value_type& __expected, value_type __desired, |
802 | memory_order __o, memory_order __o2) noexcept |
803 | { |
804 | return compare_exchange_strong(__expected, std::move(__desired), |
805 | __o, __o2); |
806 | } |
807 | |
808 | bool |
809 | compare_exchange_weak(value_type& __expected, value_type __desired, |
810 | memory_order __o = memory_order_seq_cst) noexcept |
811 | { |
812 | return compare_exchange_strong(__expected, std::move(__desired), __o); |
813 | } |
814 | |
815 | #if __cpp_lib_atomic_wait |
816 | void |
817 | wait(value_type __old, |
818 | memory_order __o = memory_order_seq_cst) const noexcept |
819 | { |
820 | _M_impl.wait(std::move(__old), __o); |
821 | } |
822 | |
823 | void |
824 | notify_one() noexcept |
825 | { |
826 | _M_impl.notify_one(); |
827 | } |
828 | |
829 | void |
830 | notify_all() noexcept |
831 | { |
832 | _M_impl.notify_all(); |
833 | } |
834 | #endif |
835 | |
836 | private: |
837 | _Sp_atomic<weak_ptr<_Tp>> _M_impl; |
838 | }; |
839 | #endif // C++20 |
840 | |
841 | /// @} relates shared_ptr |
842 | /// @} group pointer_abstractions |
843 | |
844 | _GLIBCXX_END_NAMESPACE_VERSION |
845 | } // namespace |
846 | |
847 | #endif // _SHARED_PTR_ATOMIC_H |
848 | |