blob: 2109bcb75a07ebf8d2d1401dda3fa0aabde509e0 [file] [log] [blame]
// 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/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "partition_alloc/buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::internal {
namespace {
constexpr int kSlotId = 1;
class TRIVIAL_ABI SetOnDestroy {
public:
explicit 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(SetOnDestroy&& other) {
using std::swap;
swap(was_destroyed_ptr_, other.was_destroyed_ptr_);
}
SetOnDestroy& operator=(SetOnDestroy&& other) {
using std::swap;
swap(was_destroyed_ptr_, other.was_destroyed_ptr_);
return *this;
}
~SetOnDestroy() {
if (!was_destroyed_ptr_) {
return;
}
DCHECK(!(*was_destroyed_ptr_));
*was_destroyed_ptr_ = true;
}
private:
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER)
// In instance tracer mode, raw_ptr is larger than a void*, but values stored
// inline in a SequenceLocalStorageMap must be at most sizeof(void*).
RAW_PTR_EXCLUSION bool* was_destroyed_ptr_;
#else
raw_ptr<bool> was_destroyed_ptr_;
#endif
};
template <typename T, typename... Args>
SequenceLocalStorageMap::ValueDestructorPair CreateExternalValueDestructorPair(
Args... args) {
internal::SequenceLocalStorageMap::ExternalValue value;
value.emplace(new T(args...));
auto* destructor =
SequenceLocalStorageMap::MakeExternalDestructor<T,
std::default_delete<T>>();
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
std::move(value), destructor};
return value_destructor_pair;
}
template <typename T, typename... Args>
SequenceLocalStorageMap::ValueDestructorPair CreateInlineValueDestructorPair(
Args... args) {
internal::SequenceLocalStorageMap::InlineValue value;
value.emplace<T>(args...);
auto* destructor = SequenceLocalStorageMap::MakeInlineDestructor<T>();
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair{
std::move(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, SetGetExternal) {
SequenceLocalStorageMap sequence_local_storage_map;
ScopedSetSequenceLocalStorageMapForCurrentThread
scoped_sequence_local_storage_map(&sequence_local_storage_map);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
CreateExternalValueDestructorPair<int>(5);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
EXPECT_EQ(
sequence_local_storage_map.Get(kSlotId)->external_value.value_as<int>(),
5);
}
TEST(SequenceLocalStorageMapTest, SetGetInline) {
SequenceLocalStorageMap sequence_local_storage_map;
ScopedSetSequenceLocalStorageMapForCurrentThread
scoped_sequence_local_storage_map(&sequence_local_storage_map);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair =
CreateInlineValueDestructorPair<int>(5);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
EXPECT_EQ(
sequence_local_storage_map.Get(kSlotId)->inline_value.value_as<int>(), 5);
}
// Verify that the destructor is called on a value stored in the
// SequenceLocalStorageMap when SequenceLocalStorageMap is destroyed.
TEST(SequenceLocalStorageMapTest, DestructorExternal) {
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 =
CreateExternalValueDestructorPair<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, DestructorCalledOnSetOverwriteExternal) {
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 =
CreateExternalValueDestructorPair<SetOnDestroy>(&set_on_destruction);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
CreateExternalValueDestructorPair<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);
}
template <typename T>
void DestructorInline() {
if constexpr (!absl::is_trivially_relocatable<T>()) {
// Test disabled because there is no reliable way to detect SetOnDestroy
// is trivially relocatble.
// See https://github.com/llvm/llvm-project/issues/69394
GTEST_SKIP();
} else {
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 =
CreateInlineValueDestructorPair<T>(&set_on_destruction);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair));
}
EXPECT_TRUE(set_on_destruction);
}
}
TEST(SequenceLocalStorageMapTest, DestructorInline) {
DestructorInline<SetOnDestroy>();
}
template <typename T>
void DestructorCalledOnSetOverwriteInline() {
if constexpr (!absl::is_trivially_relocatable<T>()) {
// Test disabled because there is no reliable way to detect SetOnDestroy
// is trivially relocatble.
// See https://github.com/llvm/llvm-project/issues/69394
GTEST_SKIP();
} else {
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 =
CreateInlineValueDestructorPair<T>(&set_on_destruction);
SequenceLocalStorageMap::ValueDestructorPair value_destructor_pair2 =
CreateInlineValueDestructorPair<T>(&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);
}
}
TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwriteInline) {
DestructorCalledOnSetOverwriteInline<SetOnDestroy>();
}
} // namespace base::internal