| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/threading/sequence_local_storage_map.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| namespace internal { |
| |
| namespace { |
| |
| constexpr int kSlotId = 1; |
| |
| class SetOnDestroy { |
| public: |
| SetOnDestroy(bool* was_destroyed_ptr) |
| : was_destroyed_ptr_(was_destroyed_ptr) { |
| DCHECK(was_destroyed_ptr_); |
| DCHECK(!(*was_destroyed_ptr_)); |
| } |
| |
| SetOnDestroy(const SetOnDestroy&) = delete; |
| SetOnDestroy& operator=(const SetOnDestroy&) = delete; |
| |
| ~SetOnDestroy() { |
| DCHECK(!(*was_destroyed_ptr_)); |
| *was_destroyed_ptr_ = true; |
| } |
| |
| private: |
| const raw_ptr<bool> was_destroyed_ptr_; |
| }; |
| |
| template <typename T, typename... Args> |
| SequenceLocalStorageMap::ValueDestructorPair CreateValueDestructorPair( |
| Args... args) { |
| T* value = new T(args...); |
| SequenceLocalStorageMap::ValueDestructorPair::DestructorFunc* destructor = |
| [](void* ptr) { std::default_delete<T>()(static_cast<T*>(ptr)); }; |
| |
| SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{ |
| value, destructor}; |
| |
| return value_destructor_pair; |
| } |
| |
| } // namespace |
| |
| // Verify that setting a value in the SequenceLocalStorageMap, then getting |
| // it will yield the same value. |
| TEST(SequenceLocalStorageMapTest, SetGet) { |
| SequenceLocalStorageMap sequence_local_storage_map; |
| ScopedSetSequenceLocalStorageMapForCurrentThread |
| scoped_sequence_local_storage_map(&sequence_local_storage_map); |
| |
| SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair = |
| CreateValueDestructorPair<int>(5); |
| |
| sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair)); |
| |
| EXPECT_EQ(*static_cast<int*>(sequence_local_storage_map.Get(kSlotId)), 5); |
| } |
| |
| // Verify that the destructor is called on a value stored in the |
| // SequenceLocalStorageMap when SequenceLocalStorageMap is destroyed. |
| TEST(SequenceLocalStorageMapTest, Destructor) { |
| bool set_on_destruction = false; |
| |
| { |
| SequenceLocalStorageMap sequence_local_storage_map; |
| ScopedSetSequenceLocalStorageMapForCurrentThread |
| scoped_sequence_local_storage_map(&sequence_local_storage_map); |
| |
| SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair = |
| CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction); |
| |
| sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair)); |
| } |
| |
| EXPECT_TRUE(set_on_destruction); |
| } |
| |
| // Verify that overwriting a value already in the SequenceLocalStorageMap |
| // calls value's destructor. |
| TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwrite) { |
| bool set_on_destruction = false; |
| bool set_on_destruction2 = false; |
| { |
| SequenceLocalStorageMap sequence_local_storage_map; |
| ScopedSetSequenceLocalStorageMapForCurrentThread |
| scoped_sequence_local_storage_map(&sequence_local_storage_map); |
| |
| SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair = |
| CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction); |
| SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 = |
| CreateValueDestructorPair<SetOnDestroy>(&set_on_destruction2); |
| |
| sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair)); |
| |
| ASSERT_FALSE(set_on_destruction); |
| |
| // Overwrites the old value in the slot. |
| sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair2)); |
| |
| // Destructor should've been called for the old value in the slot, and not |
| // yet called for the new value. |
| EXPECT_TRUE(set_on_destruction); |
| EXPECT_FALSE(set_on_destruction2); |
| } |
| EXPECT_TRUE(set_on_destruction2); |
| } |
| |
| } // namespace internal |
| } // namespace base |