// Copyright 2024 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/memory/structured_shared_memory.h" #include #include #include #include "base/containers/span.h" #include "base/memory/platform_shared_memory_handle.h" #include "base/memory/read_only_shared_memory_region.h" #include "base/memory/shared_memory_mapper.h" #include "base/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { // A SharedMemoryMapper that always fails to map memory. class FailingSharedMemoryMapper final : public SharedMemoryMapper { public: std::optional> Map(subtle::PlatformSharedMemoryHandle handle, bool write_allowed, uint64_t offset, size_t size) final { return std::nullopt; } void Unmap(span mapping) final {} }; TEST(StructuredSharedMemoryTest, ReadWrite) { auto writable_memory = StructuredSharedMemory::Create(); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->WritablePtr(), &writable_memory->WritableRef()); EXPECT_EQ(writable_memory->WritablePtr(), writable_memory->ReadOnlyPtr()); EXPECT_EQ(writable_memory->ReadOnlyPtr(), &writable_memory->ReadOnlyRef()); EXPECT_EQ(writable_memory->ReadOnlyRef(), 0.0); auto read_only_memory = StructuredSharedMemory::MapReadOnlyRegion( writable_memory->TakeReadOnlyRegion()); ASSERT_TRUE(read_only_memory.has_value()); EXPECT_EQ(read_only_memory->ReadOnlyPtr(), &read_only_memory->ReadOnlyRef()); EXPECT_EQ(read_only_memory->ReadOnlyRef(), 0.0); writable_memory->WritableRef() += 0.5; EXPECT_EQ(read_only_memory->ReadOnlyRef(), 0.5); } TEST(StructuredSharedMemoryTest, Initialize) { // Default initialize. auto writable_memory = StructuredSharedMemory::Create(); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->ReadOnlyRef(), 0.0); // Initialize from same type. writable_memory = StructuredSharedMemory::Create(1.2); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->ReadOnlyRef(), 1.2); // Initialize from compatible type. writable_memory = StructuredSharedMemory::Create(3); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->ReadOnlyRef(), 3); } TEST(StructuredSharedMemoryTest, MapFailure) { // Fail to map writable memory. FailingSharedMemoryMapper failing_mapper; auto writable_memory = StructuredSharedMemory::CreateWithCustomMapper(&failing_mapper); EXPECT_FALSE(writable_memory.has_value()); // Initialize from same type, but fail. writable_memory = StructuredSharedMemory::CreateWithCustomMapper( 1.2, &failing_mapper); EXPECT_FALSE(writable_memory.has_value()); // Initialize from compatible type, but fail. writable_memory = StructuredSharedMemory::CreateWithCustomMapper( 3, &failing_mapper); EXPECT_FALSE(writable_memory.has_value()); // Fail to create read-only region (bad handle). ReadOnlySharedMemoryRegion region; EXPECT_FALSE(region.IsValid()); auto read_only_memory = StructuredSharedMemory::MapReadOnlyRegion(std::move(region)); EXPECT_FALSE(read_only_memory.has_value()); // Valid handle for read-only region, but fail to map memory. writable_memory = StructuredSharedMemory::Create(); ASSERT_TRUE(writable_memory.has_value()); region = writable_memory->TakeReadOnlyRegion(); EXPECT_TRUE(region.IsValid()); read_only_memory = StructuredSharedMemory::MapReadOnlyRegion( std::move(region), &failing_mapper); EXPECT_FALSE(read_only_memory.has_value()); } TEST(StructuredSharedMemoryDeathTest, DuplicateRegion) { auto writable_memory = StructuredSharedMemory::Create(); ASSERT_TRUE(writable_memory.has_value()); ReadOnlySharedMemoryRegion region = writable_memory->DuplicateReadOnlyRegion(); ReadOnlySharedMemoryRegion region2 = writable_memory->DuplicateReadOnlyRegion(); EXPECT_EQ(region.GetGUID(), region2.GetGUID()); ReadOnlySharedMemoryRegion region3 = writable_memory->TakeReadOnlyRegion(); EXPECT_EQ(region.GetGUID(), region3.GetGUID()); // Region is no longer valid. EXPECT_CHECK_DEATH(writable_memory->DuplicateReadOnlyRegion()); EXPECT_CHECK_DEATH(writable_memory->TakeReadOnlyRegion()); } TEST(StructuredSharedMemoryTest, AtomicReadWrite) { auto writable_memory = AtomicSharedMemory::Create(); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->WritablePtr(), &writable_memory->WritableRef()); EXPECT_EQ(writable_memory->WritablePtr(), writable_memory->ReadOnlyPtr()); EXPECT_EQ(writable_memory->ReadOnlyPtr(), &writable_memory->ReadOnlyRef()); EXPECT_EQ(writable_memory->ReadOnlyRef().load(std::memory_order_relaxed), 0); auto read_only_memory = AtomicSharedMemory::MapReadOnlyRegion( writable_memory->TakeReadOnlyRegion()); ASSERT_TRUE(read_only_memory.has_value()); EXPECT_EQ(read_only_memory->ReadOnlyPtr(), &read_only_memory->ReadOnlyRef()); EXPECT_EQ(read_only_memory->ReadOnlyRef().load(std::memory_order_relaxed), 0); writable_memory->WritableRef().store(1, std::memory_order_relaxed); EXPECT_EQ(read_only_memory->ReadOnlyRef().load(std::memory_order_relaxed), 1); } TEST(StructuredSharedMemoryTest, AtomicInitialize) { auto writable_memory = AtomicSharedMemory::Create(); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->ReadOnlyRef().load(std::memory_order_relaxed), 0); writable_memory = AtomicSharedMemory::Create(1); ASSERT_TRUE(writable_memory.has_value()); EXPECT_EQ(writable_memory->ReadOnlyRef().load(std::memory_order_relaxed), 1); } } // namespace } // namespace base