<memory>: Fix atomic smart pointers array type interaction (#1339)

Co-authored-by: Nicole Mazzuca <mazzucan@outlook.com>
This commit is contained in:
Axel Boldt-Christmas 2022-05-05 10:57:32 +02:00 коммит произвёл GitHub
Родитель d89a32b2a4
Коммит 24b899e4ad
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 447 добавлений и 15 удалений

Просмотреть файл

@ -3850,9 +3850,10 @@ class alignas(2 * sizeof(void*)) _Atomic_ptr_base {
protected:
constexpr _Atomic_ptr_base() noexcept = default;
_Atomic_ptr_base(_Ty* const _Px, _Ref_count_base* const _Ref) noexcept : _Ptr(_Px), _Repptr(_Ref) {}
_Atomic_ptr_base(remove_extent_t<_Ty>* const _Px, _Ref_count_base* const _Ref) noexcept
: _Ptr(_Px), _Repptr(_Ref) {}
void _Wait(_Ty* _Old, memory_order) const noexcept {
void _Wait(remove_extent_t<_Ty>* _Old, memory_order) const noexcept {
for (;;) {
auto _Rep = _Repptr._Lock_and_load();
bool _Equal = _Ptr.load(memory_order_relaxed) == _Old;
@ -3872,7 +3873,7 @@ protected:
_Ptr.notify_all();
}
atomic<_Ty*> _Ptr{nullptr};
atomic<remove_extent_t<_Ty>*> _Ptr{nullptr};
mutable _Locked_pointer<_Ref_count_base> _Repptr;
};
@ -3892,9 +3893,9 @@ public:
void store(shared_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept {
_Check_store_memory_order(_Order);
const auto _Rep = this->_Repptr._Lock_and_load();
_Ty* const _Tmp = _Value._Ptr;
_Value._Ptr = this->_Ptr.load(memory_order_relaxed);
const auto _Rep = this->_Repptr._Lock_and_load();
remove_extent_t<_Ty>* const _Tmp = _Value._Ptr;
_Value._Ptr = this->_Ptr.load(memory_order_relaxed);
this->_Ptr.store(_Tmp, memory_order_relaxed);
this->_Repptr._Store_and_unlock(_Value._Rep);
_Value._Rep = _Rep;
@ -3947,8 +3948,8 @@ public:
_Check_memory_order(_Order);
auto _Rep = this->_Repptr._Lock_and_load();
if (this->_Ptr.load(memory_order_relaxed) == _Expected._Ptr && _Rep == _Expected._Rep) {
_Ty* const _Tmp = _Desired._Ptr;
_Desired._Ptr = this->_Ptr.load(memory_order_relaxed);
remove_extent_t<_Ty>* const _Tmp = _Desired._Ptr;
_Desired._Ptr = this->_Ptr.load(memory_order_relaxed);
this->_Ptr.store(_Tmp, memory_order_relaxed);
_STD swap(_Rep, _Desired._Rep);
this->_Repptr._Store_and_unlock(_Rep);
@ -4011,9 +4012,9 @@ public:
void store(weak_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept {
_Check_store_memory_order(_Order);
const auto _Rep = this->_Repptr._Lock_and_load();
_Ty* const _Tmp = _Value._Ptr;
_Value._Ptr = this->_Ptr.load(memory_order_relaxed);
const auto _Rep = this->_Repptr._Lock_and_load();
remove_extent_t<_Ty>* const _Tmp = _Value._Ptr;
_Value._Ptr = this->_Ptr.load(memory_order_relaxed);
this->_Ptr.store(_Tmp, memory_order_relaxed);
this->_Repptr._Store_and_unlock(_Value._Rep);
_Value._Rep = _Rep;
@ -4066,8 +4067,8 @@ public:
_Check_memory_order(_Order);
auto _Rep = this->_Repptr._Lock_and_load();
if (this->_Ptr.load(memory_order_relaxed) == _Expected._Ptr && _Rep == _Expected._Rep) {
_Ty* const _Tmp = _Desired._Ptr;
_Desired._Ptr = this->_Ptr.load(memory_order_relaxed);
remove_extent_t<_Ty>* const _Tmp = _Desired._Ptr;
_Desired._Ptr = this->_Ptr.load(memory_order_relaxed);
this->_Ptr.store(_Tmp, memory_order_relaxed);
_STD swap(_Rep, _Desired._Rep);
this->_Repptr._Store_and_unlock(_Rep);

Просмотреть файл

@ -108,7 +108,8 @@ void test_notify_all_notifies_all(UnderlyingType old_value, const UnderlyingType
template <class UnderlyingType>
void test_notify_all_notifies_all_ptr(UnderlyingType old_value, const UnderlyingType new_value,
const std::chrono::steady_clock::duration waiting_duration) {
test_notify_all_notifies_all_impl<std::atomic, UnderlyingType>(old_value, new_value, waiting_duration);
// increased waiting_duration because timing assumption might not hold for atomic smart pointers
test_notify_all_notifies_all_impl<std::atomic, UnderlyingType>(old_value, new_value, 3 * waiting_duration);
}
@ -228,6 +229,21 @@ inline void test_atomic_wait() {
test_atomic_wait_func_ptr(std::make_shared<int>('a'), std::make_shared<int>('a'), waiting_duration);
test_atomic_wait_func_ptr(
std::weak_ptr{std::make_shared<int>('a')}, std::weak_ptr{std::make_shared<int>('a')}, waiting_duration);
test_atomic_wait_func_ptr(std::make_shared<int[]>(0), std::make_shared<int[]>(0), waiting_duration);
test_atomic_wait_func_ptr(
std::weak_ptr{std::make_shared<int[]>(0)}, std::weak_ptr{std::make_shared<int[]>(0)}, waiting_duration);
test_atomic_wait_func_ptr(std::make_shared<int[]>(1), std::make_shared<int[]>(1), waiting_duration);
test_atomic_wait_func_ptr(
std::weak_ptr{std::make_shared<int[]>(1)}, std::weak_ptr{std::make_shared<int[]>(1)}, waiting_duration);
test_atomic_wait_func_ptr(std::make_shared<int[2]>(), std::make_shared<int[2]>(), waiting_duration);
test_atomic_wait_func_ptr(
std::weak_ptr{std::make_shared<int[2]>()}, std::weak_ptr{std::make_shared<int[2]>()}, waiting_duration);
test_atomic_wait_func_ptr(std::make_shared<int[][2]>(2), std::make_shared<int[][2]>(2), waiting_duration);
test_atomic_wait_func_ptr(
std::weak_ptr{std::make_shared<int[][2]>(2)}, std::weak_ptr{std::make_shared<int[][2]>(2)}, waiting_duration);
test_atomic_wait_func_ptr(std::make_shared<int[2][2]>(), std::make_shared<int[2][2]>(), waiting_duration);
test_atomic_wait_func_ptr(
std::weak_ptr{std::make_shared<int[2][2]>()}, std::weak_ptr{std::make_shared<int[2][2]>()}, waiting_duration);
test_notify_all_notifies_all<char>(1, 2, waiting_duration);
test_notify_all_notifies_all<signed char>(1, 2, waiting_duration);
@ -248,6 +264,25 @@ inline void test_atomic_wait() {
test_notify_all_notifies_all(three_chars{1, 1, 3}, three_chars{1, 2, 3}, waiting_duration);
test_notify_all_notifies_all(big_char_like{'a'}, big_char_like{'b'}, waiting_duration);
test_notify_all_notifies_all_ptr(std::make_shared<int>('a'), std::make_shared<int>('a'), waiting_duration);
test_notify_all_notifies_all_ptr(
std::weak_ptr{std::make_shared<int>('a')}, std::weak_ptr{std::make_shared<int>('a')}, waiting_duration);
test_notify_all_notifies_all_ptr(std::make_shared<int[]>(0), std::make_shared<int[]>(0), waiting_duration);
test_notify_all_notifies_all_ptr(
std::weak_ptr{std::make_shared<int[]>(0)}, std::weak_ptr{std::make_shared<int[]>(0)}, waiting_duration);
test_notify_all_notifies_all_ptr(std::make_shared<int[]>(1), std::make_shared<int[]>(1), waiting_duration);
test_notify_all_notifies_all_ptr(
std::weak_ptr{std::make_shared<int[]>(1)}, std::weak_ptr{std::make_shared<int[]>(1)}, waiting_duration);
test_notify_all_notifies_all_ptr(std::make_shared<int[2]>(), std::make_shared<int[2]>(), waiting_duration);
test_notify_all_notifies_all_ptr(
std::weak_ptr{std::make_shared<int[2]>()}, std::weak_ptr{std::make_shared<int[2]>()}, waiting_duration);
test_notify_all_notifies_all_ptr(std::make_shared<int[][2]>(2), std::make_shared<int[][2]>(2), waiting_duration);
test_notify_all_notifies_all_ptr(
std::weak_ptr{std::make_shared<int[][2]>(2)}, std::weak_ptr{std::make_shared<int[][2]>(2)}, waiting_duration);
test_notify_all_notifies_all_ptr(std::make_shared<int[2][2]>(), std::make_shared<int[2][2]>(), waiting_duration);
test_notify_all_notifies_all_ptr(
std::weak_ptr{std::make_shared<int[2][2]>()}, std::weak_ptr{std::make_shared<int[2][2]>()}, waiting_duration);
#ifndef __clang__ // TRANSITION, LLVM-46685
test_pad_bits<with_padding_bits<2>>(waiting_duration);
test_pad_bits<with_padding_bits<4>>(waiting_duration);

Просмотреть файл

@ -21,7 +21,24 @@ weak_ptr<int> wptr1 = sptr1;
atomic<shared_ptr<int>> atomic_sptr;
atomic<weak_ptr<int>> atomic_wptr;
[[nodiscard]] bool weak_ptr_equal(const weak_ptr<int>& left, const weak_ptr<int>& right) {
shared_ptr<int[]> sarrptr0 = make_shared<int[]>(0);
shared_ptr<int[]> sarrptr1 = make_shared<int[]>(1);
weak_ptr<int[]> warrptr0 = sarrptr0;
weak_ptr<int[]> warrptr1 = sarrptr1;
atomic<shared_ptr<int[]>> atomic_sarrptr;
atomic<weak_ptr<int[]>> atomic_warrptr;
shared_ptr<int[2]> sarrnptr0 = make_shared<int[2]>();
shared_ptr<int[2]> sarrnptr1 = make_shared<int[2]>();
weak_ptr<int[2]> warrnptr0 = sarrnptr0;
weak_ptr<int[2]> warrnptr1 = sarrnptr1;
atomic<shared_ptr<int[2]>> atomic_sarrnptr;
atomic<weak_ptr<int[2]>> atomic_warrnptr;
template <typename T>
[[nodiscard]] bool weak_ptr_equal(const weak_ptr<T>& left, const weak_ptr<T>& right) {
return !(left.owner_before(right) || right.owner_before(left));
}
@ -183,6 +200,324 @@ void test_weak_ptr_compare_exchange_strong() {
}
}
// Repeat test for unbounded array type.
void test_shared_ptr_arr_load_store() {
shared_ptr<int[]> sp0 = sarrptr0;
shared_ptr<int[]> sp1 = sarrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
atomic_sarrptr = sp0;
shared_ptr<int[]> temp = atomic_sarrptr;
assert(temp == sp0 || temp == sp1);
this_thread::yield();
atomic_sarrptr.store(sp1);
temp = atomic_sarrptr.load();
assert(temp == sp0 || temp == sp1);
this_thread::yield();
atomic_sarrptr.store(sp0, memory_order::seq_cst);
temp = atomic_sarrptr.load(memory_order::seq_cst);
assert(temp == sp0 || temp == sp1);
this_thread::yield();
}
}
void test_shared_ptr_arr_exchange() {
shared_ptr<int[]> sp0 = sarrptr0;
shared_ptr<int[]> sp1 = sarrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
shared_ptr<int[]> temp = atomic_sarrptr.exchange(sp0);
assert(temp == sp0 || temp == sp1);
this_thread::yield();
temp = atomic_sarrptr.exchange(sp1, memory_order::seq_cst);
assert(temp == sp0 || temp == sp1);
this_thread::yield();
}
}
void test_shared_ptr_arr_compare_exchange_weak() {
shared_ptr<int[]> sp0 = sarrptr0;
shared_ptr<int[]> sp1 = sarrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
shared_ptr<int[]> local = sp0;
if (atomic_sarrptr.compare_exchange_weak(local, sp1)) {
assert(local == sp0);
} else {
assert(local == sp1);
}
this_thread::yield();
local = sp1;
if (atomic_sarrptr.compare_exchange_weak(local, sp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(local == sp1);
} else {
assert(local == sp0);
}
this_thread::yield();
}
}
void test_shared_ptr_arr_compare_exchange_strong() {
shared_ptr<int[]> sp0 = sarrptr0;
shared_ptr<int[]> sp1 = sarrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
shared_ptr<int[]> local = sp0;
if (atomic_sarrptr.compare_exchange_strong(local, sp1)) {
assert(local == sp0);
} else {
assert(local == sp1);
}
this_thread::yield();
local = sp1;
if (atomic_sarrptr.compare_exchange_strong(local, sp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(local == sp1);
} else {
assert(local == sp0);
}
this_thread::yield();
}
}
void test_weak_ptr_arr_load_store() {
weak_ptr<int[]> wp0 = warrptr0;
weak_ptr<int[]> wp1 = warrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
atomic_warrptr = wp0;
weak_ptr<int[]> temp = atomic_warrptr;
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
atomic_warrptr.store(wp1);
temp = atomic_warrptr.load();
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
atomic_warrptr.store(wp0, memory_order::seq_cst);
temp = atomic_warrptr.load(memory_order::seq_cst);
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
}
}
void test_weak_ptr_arr_exchange() {
weak_ptr<int[]> wp0 = warrptr0;
weak_ptr<int[]> wp1 = warrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
weak_ptr<int[]> temp = atomic_warrptr.exchange(wp0);
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
temp = atomic_warrptr.exchange(wp1, memory_order::seq_cst);
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
}
}
void test_weak_ptr_arr_compare_exchange_weak() {
weak_ptr<int[]> wp0 = warrptr0;
weak_ptr<int[]> wp1 = warrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
weak_ptr<int[]> local = wp0;
if (atomic_warrptr.compare_exchange_weak(local, wp1)) {
assert(weak_ptr_equal(local, wp0));
} else {
assert(weak_ptr_equal(local, wp1));
}
this_thread::yield();
local = wp1;
if (atomic_warrptr.compare_exchange_weak(local, wp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(weak_ptr_equal(local, wp1));
} else {
assert(weak_ptr_equal(local, wp0));
}
this_thread::yield();
}
}
void test_weak_ptr_arr_compare_exchange_strong() {
weak_ptr<int[]> wp0 = warrptr0;
weak_ptr<int[]> wp1 = warrptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
weak_ptr<int[]> local = wp0;
if (atomic_warrptr.compare_exchange_strong(local, wp1)) {
assert(weak_ptr_equal(local, wp0));
} else {
assert(weak_ptr_equal(local, wp1));
}
this_thread::yield();
local = wp1;
if (atomic_warrptr.compare_exchange_strong(local, wp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(weak_ptr_equal(local, wp1));
} else {
assert(weak_ptr_equal(local, wp0));
}
this_thread::yield();
}
}
// Repeat test for bounded array type.
void test_shared_ptr_arrn_load_store() {
shared_ptr<int[2]> sp0 = sarrnptr0;
shared_ptr<int[2]> sp1 = sarrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
atomic_sarrnptr = sp0;
shared_ptr<int[2]> temp = atomic_sarrnptr;
assert(temp == sp0 || temp == sp1);
this_thread::yield();
atomic_sarrnptr.store(sp1);
temp = atomic_sarrnptr.load();
assert(temp == sp0 || temp == sp1);
this_thread::yield();
atomic_sarrnptr.store(sp0, memory_order::seq_cst);
temp = atomic_sarrnptr.load(memory_order::seq_cst);
assert(temp == sp0 || temp == sp1);
this_thread::yield();
}
}
void test_shared_ptr_arrn_exchange() {
shared_ptr<int[2]> sp0 = sarrnptr0;
shared_ptr<int[2]> sp1 = sarrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
shared_ptr<int[2]> temp = atomic_sarrnptr.exchange(sp0);
assert(temp == sp0 || temp == sp1);
this_thread::yield();
temp = atomic_sarrnptr.exchange(sp1, memory_order::seq_cst);
assert(temp == sp0 || temp == sp1);
this_thread::yield();
}
}
void test_shared_ptr_arrn_compare_exchange_weak() {
shared_ptr<int[2]> sp0 = sarrnptr0;
shared_ptr<int[2]> sp1 = sarrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
shared_ptr<int[2]> local = sp0;
if (atomic_sarrnptr.compare_exchange_weak(local, sp1)) {
assert(local == sp0);
} else {
assert(local == sp1);
}
this_thread::yield();
local = sp1;
if (atomic_sarrnptr.compare_exchange_weak(local, sp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(local == sp1);
} else {
assert(local == sp0);
}
this_thread::yield();
}
}
void test_shared_ptr_arrn_compare_exchange_strong() {
shared_ptr<int[2]> sp0 = sarrnptr0;
shared_ptr<int[2]> sp1 = sarrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
shared_ptr<int[2]> local = sp0;
if (atomic_sarrnptr.compare_exchange_strong(local, sp1)) {
assert(local == sp0);
} else {
assert(local == sp1);
}
this_thread::yield();
local = sp1;
if (atomic_sarrnptr.compare_exchange_strong(local, sp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(local == sp1);
} else {
assert(local == sp0);
}
this_thread::yield();
}
}
void test_weak_ptr_arrn_load_store() {
weak_ptr<int[2]> wp0 = warrnptr0;
weak_ptr<int[2]> wp1 = warrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
atomic_warrnptr = wp0;
weak_ptr<int[2]> temp = atomic_warrnptr;
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
atomic_warrnptr.store(wp1);
temp = atomic_warrnptr.load();
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
atomic_warrnptr.store(wp0, memory_order::seq_cst);
temp = atomic_warrnptr.load(memory_order::seq_cst);
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
}
}
void test_weak_ptr_arrn_exchange() {
weak_ptr<int[2]> wp0 = warrnptr0;
weak_ptr<int[2]> wp1 = warrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
weak_ptr<int[2]> temp = atomic_warrnptr.exchange(wp0);
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
temp = atomic_warrnptr.exchange(wp1, memory_order::seq_cst);
assert(weak_ptr_equal(temp, wp0) || weak_ptr_equal(temp, wp1));
this_thread::yield();
}
}
void test_weak_ptr_arrn_compare_exchange_weak() {
weak_ptr<int[2]> wp0 = warrnptr0;
weak_ptr<int[2]> wp1 = warrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
weak_ptr<int[2]> local = wp0;
if (atomic_warrnptr.compare_exchange_weak(local, wp1)) {
assert(weak_ptr_equal(local, wp0));
} else {
assert(weak_ptr_equal(local, wp1));
}
this_thread::yield();
local = wp1;
if (atomic_warrnptr.compare_exchange_weak(local, wp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(weak_ptr_equal(local, wp1));
} else {
assert(weak_ptr_equal(local, wp0));
}
this_thread::yield();
}
}
void test_weak_ptr_arrn_compare_exchange_strong() {
weak_ptr<int[2]> wp0 = warrnptr0;
weak_ptr<int[2]> wp1 = warrnptr1;
for (uintmax_t i = 0; i < iterations; ++i) {
weak_ptr<int[2]> local = wp0;
if (atomic_warrnptr.compare_exchange_strong(local, wp1)) {
assert(weak_ptr_equal(local, wp0));
} else {
assert(weak_ptr_equal(local, wp1));
}
this_thread::yield();
local = wp1;
if (atomic_warrnptr.compare_exchange_strong(local, wp0, memory_order::seq_cst, memory_order::seq_cst)) {
assert(weak_ptr_equal(local, wp1));
} else {
assert(weak_ptr_equal(local, wp0));
}
this_thread::yield();
}
}
void run_test(void (*fp)()) {
thread thr0(fp);
thread thr1(fp);
@ -222,6 +557,25 @@ void ensure_nonmember_calls_compile() {
}
}
template <class AtomicType>
void ensure_member_calls_compile() {
AtomicType instance;
const AtomicType constInstance;
auto loaded = instance.load();
loaded = constInstance.load();
loaded = instance;
loaded = constInstance;
instance.store(loaded);
loaded = instance.exchange(instance);
loaded = instance.exchange(constInstance);
if (instance.compare_exchange_weak(loaded, constInstance)) {
// intentionally empty
}
if (instance.compare_exchange_strong(loaded, constInstance)) {
// intentionally empty
}
}
#ifndef __EDG__ // TRANSITION, DevCom-1656924
// LWG-3661: constinit atomic<shared_ptr<T>> a(nullptr); should work
constinit atomic<shared_ptr<bool>> a{};
@ -245,6 +599,34 @@ int main() {
run_test(test_weak_ptr_compare_exchange_strong);
ensure_nonmember_calls_compile<atomic<shared_ptr<int>>>();
ensure_nonmember_calls_compile<atomic<weak_ptr<int>>>();
run_test(test_shared_ptr_arr_load_store);
run_test(test_shared_ptr_arr_exchange);
run_test(test_shared_ptr_arr_compare_exchange_weak);
run_test(test_shared_ptr_arr_compare_exchange_strong);
run_test(test_weak_ptr_arr_load_store);
run_test(test_weak_ptr_arr_exchange);
run_test(test_weak_ptr_arr_compare_exchange_weak);
run_test(test_weak_ptr_arr_compare_exchange_strong);
ensure_nonmember_calls_compile<atomic<shared_ptr<int[]>>>();
ensure_nonmember_calls_compile<atomic<weak_ptr<int[]>>>();
run_test(test_shared_ptr_arrn_load_store);
run_test(test_shared_ptr_arrn_exchange);
run_test(test_shared_ptr_arrn_compare_exchange_weak);
run_test(test_shared_ptr_arrn_compare_exchange_strong);
run_test(test_weak_ptr_arrn_load_store);
run_test(test_weak_ptr_arrn_exchange);
run_test(test_weak_ptr_arrn_compare_exchange_weak);
run_test(test_weak_ptr_arrn_compare_exchange_strong);
ensure_nonmember_calls_compile<atomic<shared_ptr<int[2]>>>();
ensure_nonmember_calls_compile<atomic<weak_ptr<int[2]>>>();
ensure_nonmember_calls_compile<atomic<shared_ptr<int[][2]>>>();
ensure_nonmember_calls_compile<atomic<weak_ptr<int[][2]>>>();
ensure_nonmember_calls_compile<atomic<shared_ptr<int[2][2]>>>();
ensure_nonmember_calls_compile<atomic<weak_ptr<int[2][2]>>>();
ensure_member_calls_compile<atomic<shared_ptr<int[][2]>>>();
ensure_member_calls_compile<atomic<weak_ptr<int[][2]>>>();
ensure_member_calls_compile<atomic<shared_ptr<int[2][2]>>>();
ensure_member_calls_compile<atomic<weak_ptr<int[2][2]>>>();
#ifdef _DEBUG
sptr0 = {};
@ -253,6 +635,20 @@ int main() {
wptr1 = {};
atomic_sptr.store({});
atomic_wptr.store({});
sarrptr0 = {};
sarrptr1 = {};
warrptr0 = {};
warrptr1 = {};
atomic_sarrptr.store({});
atomic_warrptr.store({});
sarrnptr0 = {};
sarrnptr1 = {};
warrnptr0 = {};
warrnptr1 = {};
atomic_sarrnptr.store({});
atomic_warrnptr.store({});
assert(!_CrtDumpMemoryLeaks());
#endif // _DEBUG
}