blob: 7f01fbab15da60a4d76eff1666c22db50accfbee [file] [log] [blame]
// 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 <atomic>
#include <optional>
#include <utility>
#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<span<uint8_t>> Map(subtle::PlatformSharedMemoryHandle handle,
bool write_allowed,
uint64_t offset,
size_t size) final {
return std::nullopt;
}
void Unmap(span<uint8_t> mapping) final {}
};
TEST(StructuredSharedMemoryTest, ReadWrite) {
auto writable_memory = StructuredSharedMemory<double>::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<double>::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<double>::Create();
ASSERT_TRUE(writable_memory.has_value());
EXPECT_EQ(writable_memory->ReadOnlyRef(), 0.0);
// Initialize from same type.
writable_memory = StructuredSharedMemory<double>::Create(1.2);
ASSERT_TRUE(writable_memory.has_value());
EXPECT_EQ(writable_memory->ReadOnlyRef(), 1.2);
// Initialize from compatible type.
writable_memory = StructuredSharedMemory<double>::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<double>::CreateWithCustomMapper(&failing_mapper);
EXPECT_FALSE(writable_memory.has_value());
// Initialize from same type, but fail.
writable_memory = StructuredSharedMemory<double>::CreateWithCustomMapper(
1.2, &failing_mapper);
EXPECT_FALSE(writable_memory.has_value());
// Initialize from compatible type, but fail.
writable_memory = StructuredSharedMemory<double>::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<double>::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<double>::Create();
ASSERT_TRUE(writable_memory.has_value());
region = writable_memory->TakeReadOnlyRegion();
EXPECT_TRUE(region.IsValid());
read_only_memory = StructuredSharedMemory<double>::MapReadOnlyRegion(
std::move(region), &failing_mapper);
EXPECT_FALSE(read_only_memory.has_value());
}
TEST(StructuredSharedMemoryDeathTest, DuplicateRegion) {
auto writable_memory = StructuredSharedMemory<double>::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<int>::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<int>::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<int>::Create();
ASSERT_TRUE(writable_memory.has_value());
EXPECT_EQ(writable_memory->ReadOnlyRef().load(std::memory_order_relaxed), 0);
writable_memory = AtomicSharedMemory<int>::Create(1);
ASSERT_TRUE(writable_memory.has_value());
EXPECT_EQ(writable_memory->ReadOnlyRef().load(std::memory_order_relaxed), 1);
}
} // namespace
} // namespace base