//------------------------------------------------------------------------------ // File: AtomicUniquePtrTests.hh // Author: Abhishek Lekshmanan - CERN //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2023 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "common/concurrency/AtomicUniquePtr.h" #include #include #include TEST(AtomicUniquePtr, Basic) { eos::common::atomic_unique_ptr p(new int(1)); EXPECT_EQ(*p.get(), 1); auto old_ptr = p.release(); EXPECT_EQ(*old_ptr, 1); EXPECT_EQ(p.get(), nullptr); std::unique_ptr old_ptr_guard(old_ptr); } TEST(AtomicUniquePtr, Reset) { eos::common::atomic_unique_ptr p(new int(1)); EXPECT_EQ(*p.get(), 1); auto old_ptr = p.reset(new int(2)); std::shared_ptr old_ptr_guard(old_ptr); EXPECT_EQ(*p.get(), 2); EXPECT_EQ(*old_ptr_guard.get(), 1); } TEST(AtomicUniquePtr, MoveCtor) { eos::common::atomic_unique_ptr p1(new int(1)); eos::common::atomic_unique_ptr p2(std::move(p1)); EXPECT_EQ(*p2.get(), 1); EXPECT_EQ(p1.get(), nullptr); } TEST(AtomicUniquePtr, reset_from_null) { eos::common::atomic_unique_ptr p; EXPECT_EQ(p.get(), nullptr); p.reset_from_null(new int(1)); EXPECT_EQ(*p.get(), 1); } TEST(AtomicUniquePtr, MemberAccessOperator) { struct A { std::string data; explicit A(std::string d) : data(d) {} }; eos::common::atomic_unique_ptr p(new A("hello")); EXPECT_EQ(p->data, "hello"); } TEST(AtomicUniquePtr, VectorOfAtomics) { std::vector> v; v.emplace_back(new int(1)); v.emplace_back(new int(2)); v.emplace_back(new int(3)); EXPECT_EQ(*v[0].get(), 1); EXPECT_EQ(*v[1].get(), 2); EXPECT_EQ(*v[2].get(), 3); } TEST(AtomicUniquePtr, SimpleGC) { std::vector> v; eos::common::atomic_unique_ptr p(new int(1)); int * p_ = p.reset(new int(2)); // p_ points to 1 v.emplace_back(p_); EXPECT_EQ(*p.get(), 2); EXPECT_EQ(*v[0].get(), 1); // v[0] points to 1 } TEST(AtomicUniquePtr, multireadwrite) { std::mutex gc_mtx; std::vector> old_ptrs; old_ptrs.reserve(20'000'000); eos::common::atomic_unique_ptr p(new std::string("start")); auto writer_fn = [&p, &old_ptrs, &gc_mtx]() { auto tid_hash = std::hash{}(std::this_thread::get_id()); for (int i = 0; i<100'000; ++i) { std::string new_str = "greetings from thread" + std::to_string(tid_hash); auto old_ptr = p.reset(new std::string(new_str)); std::scoped_lock lock(gc_mtx); old_ptrs.emplace_back(old_ptr); } //std::cout << "Done with writer="< reader_threads; std::vector writer_threads; for (int i=0;i<200;i++) { reader_threads.emplace_back(reader_fn); if (i%10==0) { writer_threads.emplace_back(writer_fn); } } for (auto& t: reader_threads) { t.join(); } for (auto& t: writer_threads) { t.join(); } } TEST(SharedPtrNonTSSEGV, multireadwrite) { GTEST_FLAG_SET(death_test_style, "threadsafe"); ASSERT_DEATH( { std::shared_ptr p(new std::string("start")); auto writer_fn = [&p]() { auto tid_hash = std::hash{}(std::this_thread::get_id()); for (int i = 0; i < 100'000; ++i) { std::string new_str = "greetings from thread" + std::to_string(tid_hash); p.reset(new std::string(new_str)); } // std::cout << "Done with writer="< reader_threads; std::vector writer_threads; for (int i = 0; i < 200; i++) { reader_threads.emplace_back(reader_fn); if (i % 10 == 0) { writer_threads.emplace_back(writer_fn); } } for (auto& t : reader_threads) { t.join(); } for (auto& t : writer_threads) { t.join(); } } , ".*"); } struct MyDataSP { std::shared_ptr get_data() { std::shared_ptr data_copy(data); return data_copy; } void reset(std::string* new_val) { std::scoped_lock wlock(mtx); data.reset(new_val); } MyDataSP(std::string&& val) : data(std::make_shared(val)) {} private: std::shared_ptr data; std::mutex mtx; }; TEST(SharedPtrNonTS2SEGV, multireadwrite) { GTEST_FLAG_SET(death_test_style, "threadsafe"); ASSERT_DEATH({ MyDataSP p("start"); auto writer_fn = [&p]() { auto tid_hash = std::hash{}(std::this_thread::get_id()); for (int i = 0; i < 100'000; ++i) { std::string new_str = "greetings from thread" + std::to_string(tid_hash); p.reset(new std::string(new_str)); } // std::cout << "Done with writer="< reader_threads; std::vector writer_threads; for (int i = 0; i < 200; i++) { reader_threads.emplace_back(reader_fn); if (i % 10 == 0) { writer_threads.emplace_back(writer_fn); } } for (auto& t : reader_threads) { t.join(); } for (auto& t : writer_threads) { t.join(); } }, ".*"); } struct MyDataAtomicSP { std::shared_ptr get_data() { return std::atomic_load_explicit(&data, std::memory_order_acquire); } void reset(std::string* new_val) { std::shared_ptr new_data(new_val); std::atomic_store_explicit(&data, new_data, std::memory_order_release); } MyDataAtomicSP(std::string&& val) : data(std::make_shared(val)) {} private: std::shared_ptr data; }; TEST(SharedPtrTS, multireadwrite) { MyDataAtomicSP p("start"); auto writer_fn = [&p]() { auto tid_hash = std::hash{}(std::this_thread::get_id()); for (int i = 0; i<100'000; ++i) { std::string new_str = "greetings from thread" + std::to_string(tid_hash); p.reset(new std::string(new_str)); } //std::cout << "Done with writer="< reader_threads; std::vector writer_threads; for (int i=0;i<200;i++) { reader_threads.emplace_back(reader_fn); if (i%10==0) { writer_threads.emplace_back(writer_fn); } } for (auto& t: reader_threads) { t.join(); } for (auto& t: writer_threads) { t.join(); } }