Skip to content

[libc++] Implement P0718R2: atomic<shared_ptr<T>> #78317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_atomic_ref`` *unimplemented*
--------------------------------------------------- -----------------
``__cpp_lib_atomic_shared_ptr`` *unimplemented*
``__cpp_lib_atomic_shared_ptr`` ``201711L``
--------------------------------------------------- -----------------
``__cpp_lib_atomic_value_initialization`` ``201911L``
--------------------------------------------------- -----------------
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Implemented Papers
- P0521R0 - Proposed Resolution for CA 14 (``shared_ptr`` ``use_count/unique``)
- P1759R6 - Native handles and file streams
- P2517R1 - Add a conditional ``noexcept`` specification to ``std::apply``
- P0718R2 - Revising ``atomic_shared_ptr`` for C++20


Improvements and New Features
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"`P0600R1 <https://wg21.link/P0600R1>`__","LWG","nodiscard in the Library","Albuquerque","|Complete|","16.0"
"`P0616R0 <https://wg21.link/P0616R0>`__","LWG","de-pessimize legacy <numeric> algorithms with std::move","Albuquerque","|Complete|","12.0"
"`P0653R2 <https://wg21.link/P0653R2>`__","LWG","Utility to convert a pointer to a raw pointer","Albuquerque","|Complete|","6.0"
"`P0718R2 <https://wg21.link/P0718R2>`__","LWG","Atomic shared_ptr","Albuquerque","",""
"`P0718R2 <https://wg21.link/P0718R2>`__","LWG","Atomic shared_ptr","Albuquerque","|Complete|","18.0"
"`P0767R1 <https://wg21.link/P0767R1>`__","CWG","Deprecate POD","Albuquerque","|Complete|","7.0"
"`P0768R1 <https://wg21.link/P0768R1>`__","CWG","Library Support for the Spaceship (Comparison) Operator","Albuquerque","|Complete|",""
"`P0777R1 <https://wg21.link/P0777R1>`__","LWG","Treating Unnecessary ``decay``\ ","Albuquerque","|Complete|","7.0"
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
"`3654 <https://wg21.link/LWG3654>`__","``basic_format_context::arg(size_t)`` should be ``noexcept`` ","February 2022","|Complete|","15.0","|format|"
"`3657 <https://wg21.link/LWG3657>`__","``std::hash<std::filesystem::path>`` is not enabled","February 2022","|Complete|","17.0"
"`3660 <https://wg21.link/LWG3660>`__","``iterator_traits<common_iterator>::pointer`` should conform to §[iterator.traits]","February 2022","|Complete|","14.0","|ranges|"
"`3661 <https://wg21.link/LWG3661>`__","``constinit atomic<shared_ptr<T>> a(nullptr);`` should work","February 2022","",""
"`3661 <https://wg21.link/LWG3661>`__","``constinit atomic<shared_ptr<T>> a(nullptr);`` should work","February 2022","|Complete|","18.0"
"","","","","",""
"`3564 <https://wg21.link/LWG3564>`__","``transform_view::iterator<true>::value_type`` and ``iterator_category`` should use ``const F&``","July 2022","","","|ranges|"
"`3617 <https://wg21.link/LWG3617>`__","``function``/``packaged_task`` deduction guides and deducing ``this``","July 2022","",""
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cIssues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"`3884 <https://wg21.link/LWG3884>`__","``flat_foo`` is missing allocator-extended copy/move constructors","Varna June 2023","","","|flat_containers|"
"`3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","Varna June 2023","|Nothing To Do|","",""
"`3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","Varna June 2023","","",""
"`3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","Varna June 2023","","",""
"`3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","Varna June 2023","|Complete|","18.0",""
"`3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","Varna June 2023","","",""
"`3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","Varna June 2023","|Complete|","7.0",""
"`3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","Varna June 2023","","","|ranges|"
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ set(files
__memory/allocator_destructor.h
__memory/allocator_traits.h
__memory/assume_aligned.h
__memory/atomic_shared_ptr.h
__memory/auto_ptr.h
__memory/builtin_new_allocator.h
__memory/compressed_pair.h
Expand Down
10 changes: 10 additions & 0 deletions libcxx/include/__atomic/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ struct atomic<_Tp> : __atomic_base<_Tp> {
_LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) noexcept { return fetch_sub(__op) - __op; }
};

template <class _Tp>
class shared_ptr;
template <class _Tp>
class weak_ptr;

template <class _Tp>
struct atomic<shared_ptr<_Tp>>;
template <class _Tp>
struct atomic<weak_ptr<_Tp>>;

#endif // _LIBCPP_STD_VER >= 20

// atomic_is_lock_free
Expand Down
233 changes: 233 additions & 0 deletions libcxx/include/__memory/atomic_shared_ptr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H
#define _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H

#include <__memory/addressof.h>
#include <__memory/shared_ptr.h>
#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_ATOMIC_HEADER)
# include <__atomic/memory_order.h>
#endif

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if !defined(_LIBCPP_HAS_NO_THREADS)

class _LIBCPP_EXPORTED_FROM_ABI __sp_mut {
void* __lx_;

public:
void lock() _NOEXCEPT;
void unlock() _NOEXCEPT;

private:
_LIBCPP_CONSTEXPR __sp_mut(void*) _NOEXCEPT;
__sp_mut(const __sp_mut&);
__sp_mut& operator=(const __sp_mut&);

friend _LIBCPP_EXPORTED_FROM_ABI __sp_mut& __get_sp_mut(const void*);
};

_LIBCPP_EXPORTED_FROM_ABI __sp_mut& __get_sp_mut(const void*);

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp __sp_atomic_load(const _Tp* __p) {
__sp_mut& __m = std::__get_sp_mut(__p);
__m.lock();
_Tp __q = *__p;
__m.unlock();
return __q;
}

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI void __sp_atomic_store(_Tp* __p, _Tp& __r) {
__sp_mut& __m = std::__get_sp_mut(__p);
__m.lock();
__p->swap(__r);
__m.unlock();
}

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI bool __sp_atomic_compare_exchange_strong(_Tp* __p, _Tp* __v, _Tp& __w) {
_Tp __temp;
__sp_mut& __m = std::__get_sp_mut(__p);
__m.lock();
if (__p->__owner_equivalent(*__v)) {
std::swap(__temp, *__p);
*__p = __w;
__m.unlock();
return true;
}
std::swap(__temp, *__v);
*__v = *__p;
__m.unlock();
return false;
}

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp __sp_atomic_exchange(_Tp* __p, _Tp& __r) {
__sp_mut& __m = std::__get_sp_mut(__p);
__m.lock();
__p->swap(__r);
__m.unlock();
return __r;
}

# if _LIBCPP_STD_VER >= 20
template <class _Tp>
struct atomic;

template <class _Tp>
struct __sp_atomic_base {
using value_type = _Tp;

static constexpr bool is_always_lock_free = false;
_LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return false; }

_LIBCPP_HIDE_FROM_ABI constexpr __sp_atomic_base() noexcept = default;
_LIBCPP_HIDE_FROM_ABI __sp_atomic_base(_Tp&& __d) noexcept : __p(std::move(__d)) {}
_LIBCPP_HIDE_FROM_ABI __sp_atomic_base(const __sp_atomic_base&) = delete;
_LIBCPP_HIDE_FROM_ABI void operator=(const __sp_atomic_base&) = delete;

_LIBCPP_HIDE_FROM_ABI _Tp load(memory_order = memory_order_seq_cst) noexcept {
return std::__sp_atomic_load(std::addressof(__p));
}
_LIBCPP_HIDE_FROM_ABI operator _Tp() const noexcept { return load(); }
_LIBCPP_HIDE_FROM_ABI void store(_Tp __d, memory_order = memory_order_seq_cst) noexcept {
std::__sp_atomic_store(std::addressof(__p), __d);
}
_LIBCPP_HIDE_FROM_ABI void operator=(_Tp __d) noexcept { std::__sp_atomic_store(std::addressof(__p), __d); }
_LIBCPP_HIDE_FROM_ABI void operator=(nullptr_t) noexcept { store(nullptr); }

_LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __d, memory_order = memory_order_seq_cst) noexcept {
return std::__sp_atomic_exchange(std::addressof(__p), __d);
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(_Tp& __e, _Tp __d, memory_order, memory_order) noexcept {
return std::__sp_atomic_compare_exchange_strong(std::addressof(__p), std::addressof(__e), __d);
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(_Tp& __e, _Tp __d, memory_order, memory_order) noexcept {
return std::__sp_atomic_compare_exchange_strong(std::addressof(__p), std::addressof(__e), __d);
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(_Tp& __e, _Tp __d, memory_order = memory_order_seq_cst) noexcept {
return std::__sp_atomic_compare_exchange_strong(std::addressof(__p), std::addressof(__e), __d);
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(_Tp& __e, _Tp __d, memory_order = memory_order_seq_cst) noexcept {
return std::__sp_atomic_compare_exchange_strong(std::addressof(__p), std::addressof(__e), __d);
}

// P1644R0 not implemented
// void wait(_Tp old, memory_order order = memory_order::seq_cst) const noexcept;
// void notify_one() noexcept;
// void notify_all() noexcept;
Comment on lines +130 to +133
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these implementable given that you are just storing a shared_ptr ?


private:
_Tp __p;
};

template <class _Tp>
struct atomic<shared_ptr<_Tp>> : __sp_atomic_base<shared_ptr<_Tp>> {
_LIBCPP_HIDE_FROM_ABI constexpr atomic() noexcept = default;
_LIBCPP_HIDE_FROM_ABI constexpr atomic(nullptr_t) noexcept : atomic() {}
_LIBCPP_HIDE_FROM_ABI atomic(shared_ptr<_Tp> desired) noexcept
: __sp_atomic_base<shared_ptr<_Tp>>(std::move(desired)) {}
_LIBCPP_HIDE_FROM_ABI atomic(const atomic&) = delete;

_LIBCPP_HIDE_FROM_ABI void operator=(const atomic&) = delete;
using __sp_atomic_base<shared_ptr<_Tp>>::operator=;
};

template <class _Tp>
struct atomic<weak_ptr<_Tp>> : __sp_atomic_base<weak_ptr<_Tp>> {
_LIBCPP_HIDE_FROM_ABI constexpr atomic() noexcept = default;
_LIBCPP_HIDE_FROM_ABI constexpr atomic(nullptr_t) noexcept : atomic() {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason was said in LWG3611:

There is no need to also change atomic<weak_ptr<T>> because weak_ptr doesn't have a constructor taking nullptr.

Did you submit an LWG issue for corresponding change of atomic<weak_ptr<T>>?

_LIBCPP_HIDE_FROM_ABI atomic(weak_ptr<_Tp> desired) noexcept : __sp_atomic_base<weak_ptr<_Tp>>(std::move(desired)) {}
_LIBCPP_HIDE_FROM_ABI atomic(const atomic&) = delete;

_LIBCPP_HIDE_FROM_ABI void operator=(const atomic&) = delete;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we apply the resolution of LWG3611 to atomic<weak_ptr<T>>, perhaps we should also apply that of LWG3893.

using __sp_atomic_base<weak_ptr<_Tp>>::operator=;
};
# endif // _LIBCPP_STD_VER >= 20

// [depr.util.smartptr.shared.atomic]

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_is_lock_free(const shared_ptr<_Tp>*) {
return false;
}

template <class _Tp>
_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
atomic_load(const shared_ptr<_Tp>* __p) {
return std::__sp_atomic_load(__p);
}

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) {
return std::atomic_load(__p);
}

template <class _Tp>
_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI void atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
std::__sp_atomic_store(__p, __r);
}

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI void
atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
std::atomic_store(__p, __r);
}

template <class _Tp>
_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) {
return std::__sp_atomic_exchange(__p, __r);
}

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
atomic_exchange_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, memory_order) {
return std::atomic_exchange(__p, std::move(__r));
}

template <class _Tp>
_LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool
atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w) {
return std::__sp_atomic_compare_exchange_strong(__p, __v, __w);
}

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool
atomic_compare_exchange_weak(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w) {
return std::atomic_compare_exchange_strong(__p, __v, std::move(__w));
}

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_strong_explicit(
shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w, memory_order, memory_order) {
return std::atomic_compare_exchange_strong(__p, __v, std::move(__w));
}

template <class _Tp>
inline _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_weak_explicit(
shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, shared_ptr<_Tp> __w, memory_order, memory_order) {
return std::atomic_compare_exchange_weak(__p, __v, std::move(__w));
}

#endif // !defined(_LIBCPP_HAS_NO_THREADS)

_LIBCPP_END_NAMESPACE_STD

#endif // LLVM_ATOMIC_ATOMIC_SHARED_PTR_H
Loading