// Copyright 2017 The Chromium Authors. All rights reserved. // 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 #include #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 was_destroyed_ptr_; }; template SequenceLocalStorageMap::ValueDestructorPair CreateValueDestructorPair( Args... args) { T* value = new T(args...); SequenceLocalStorageMap::ValueDestructorPair::DestructorFunc* destructor = [](void* ptr) { std::default_delete()(static_cast(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(5); sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair)); EXPECT_EQ(*static_cast(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(&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(&set_on_destruction); SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 = CreateValueDestructorPair(&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