#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)
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;
}
}
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);
}
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);
}
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);
sequence_local_storage_map.Set(kSlotId, std::move(value_destructor_pair2));
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>()) {
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>()) {
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);
sequence_local_storage_map.Set(kSlotId,
std::move(value_destructor_pair2));
EXPECT_TRUE(set_on_destruction);
EXPECT_FALSE(set_on_destruction2);
}
EXPECT_TRUE(set_on_destruction2);
}
}
TEST(SequenceLocalStorageMapTest, DestructorCalledOnSetOverwriteInline) {
DestructorCalledOnSetOverwriteInline<SetOnDestroy>();
}
}