Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions score/memory/shared/shared_memory_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ auto SharedMemoryFactory::SetTypedMemoryProvider(std::shared_ptr<TypedMemory> ty
instance().SetTypedMemoryProvider(typed_memory_ptr);
}

auto SharedMemoryFactory::SetInterVMMemoryProvider(std::shared_ptr<TypedMemory> intervm_memory_ptr) noexcept -> void
{
static_cast<SharedMemoryFactoryImpl&>(instance()).SetInterVMMemoryProvider(intervm_memory_ptr);
}

auto SharedMemoryFactory::instance() noexcept -> ISharedMemoryFactory&
{
if (mock_ != nullptr)
Expand Down
2 changes: 2 additions & 0 deletions score/memory/shared/shared_memory_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ class SharedMemoryFactory

static void SetTypedMemoryProvider(std::shared_ptr<score::memory::shared::TypedMemory> typed_memory_ptr) noexcept;

static void SetInterVMMemoryProvider(std::shared_ptr<score::memory::shared::TypedMemory> intervm_memory_ptr) noexcept;

static std::size_t GetControlBlockSize() noexcept
{
return instance().GetControlBlockSize();
Expand Down
91 changes: 63 additions & 28 deletions score/memory/shared/shared_memory_factory_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,23 @@ auto SharedMemoryFactoryImpl::Open(const std::string& path,
{
if (IsInterVmShmPath(path))
{
score::mw::log::LogError("shm") << "Opening inter-VM shared memory (path prefix '" << kInterVmSharedShmPrefix
<< "') is not supported. No portable implementation exists yet. Rejecting path: "
<< path;
return nullptr;
if (intervm_memory_ptr_ == nullptr)
{
score::mw::log::LogError("shm")
<< "Opening inter-VM shared memory (path prefix '" << kInterVmSharedShmPrefix
<< "') requires an InterVM memory provider set via SetInterVMMemoryProvider(). Rejecting path: "
<< path;
return nullptr;
}
}

std::lock_guard<std::mutex> lock{mutex_};
auto resource = GetResourceIfAlreadyOpened(path, resources_);
if (resource == nullptr)
{
const auto effective_ptr = IsInterVmShmPath(path) ? intervm_memory_ptr_ : typed_memory_ptr_;
const auto result =
SharedMemoryResource::Open(path, is_read_write, &CreateAccessControlList, typed_memory_ptr_);
SharedMemoryResource::Open(path, is_read_write, &CreateAccessControlList, effective_ptr);
if (!result.has_value())
{
score::mw::log::LogWarn("shm") << "Could not open Shared Memory " << path << ":" << result.error();
Expand Down Expand Up @@ -184,10 +189,14 @@ auto score::memory::shared::SharedMemoryFactoryImpl::Create(std::string path,
{
if (IsInterVmShmPath(path))
{
score::mw::log::LogError("shm") << "Creating inter-VM shared memory (path prefix '" << kInterVmSharedShmPrefix
<< "') is not supported. No portable implementation exists yet. Rejecting path: "
<< path;
return nullptr;
if (intervm_memory_ptr_ == nullptr)
{
score::mw::log::LogError("shm")
<< "Creating inter-VM shared memory (path prefix '" << kInterVmSharedShmPrefix
<< "') requires an InterVM memory provider set via SetInterVMMemoryProvider(). Rejecting path: "
<< path;
return nullptr;
}
}

std::lock_guard<std::mutex> lock{mutex_};
Expand All @@ -197,17 +206,27 @@ auto score::memory::shared::SharedMemoryFactoryImpl::Create(std::string path,
return nullptr;
}

if (prefer_typed_memory && (typed_memory_ptr_ == nullptr))
// Determine which provider to use: inter-VM paths use the InterVM provider,
// otherwise fall back to the typed memory provider if prefer_typed_memory is set.
std::shared_ptr<TypedMemory> effective_provider = nullptr;
if (IsInterVmShmPath(path))
{
score::mw::log::LogError("shm")
<< "Shared memory has to be created in typed memory but no typed memory instance has "
"been provided using the public interface SetTypedMemoryProvider ";
return nullptr;
effective_provider = intervm_memory_ptr_;
}
else if (prefer_typed_memory)
{
if (typed_memory_ptr_ == nullptr)
{
score::mw::log::LogError("shm")
<< "Shared memory has to be created in typed memory but no typed memory instance has "
"been provided using the public interface SetTypedMemoryProvider ";
return nullptr;
}
effective_provider = typed_memory_ptr_;
}

const auto typed_memory_ptr = prefer_typed_memory ? typed_memory_ptr_ : nullptr;
const auto result = SharedMemoryResource::Create(
path, user_space_to_reserve, std::move(cb), permissions, &CreateAccessControlList, typed_memory_ptr);
path, user_space_to_reserve, std::move(cb), permissions, &CreateAccessControlList, effective_provider);
if (!result.has_value())
{
score::mw::log::LogWarn("shm") << "Could not create Shared Memory " << path << ":" << result.error();
Expand Down Expand Up @@ -264,32 +283,43 @@ auto score::memory::shared::SharedMemoryFactoryImpl::CreateOrOpen(
{
if (IsInterVmShmPath(path))
{
score::mw::log::LogError("shm") << "Creating or opening inter-VM shared memory (path prefix '"
<< kInterVmSharedShmPrefix
<< "') is not supported. No portable implementation exists yet. Rejecting path: "
<< path;
return nullptr;
if (intervm_memory_ptr_ == nullptr)
{
score::mw::log::LogError("shm")
<< "Creating or opening inter-VM shared memory (path prefix '" << kInterVmSharedShmPrefix
<< "') requires an InterVM memory provider set via SetInterVMMemoryProvider(). Rejecting path: "
<< path;
return nullptr;
}
}

std::lock_guard<std::mutex> lock{mutex_};
auto resource = GetResourceIfAlreadyOpened(path, resources_);
if (resource == nullptr)
{
if ((prefer_typed_memory) && (typed_memory_ptr_ == nullptr))
std::shared_ptr<TypedMemory> effective_provider = nullptr;
if (IsInterVmShmPath(path))
{
score::mw::log::LogError("shm")
<< "Shared memory has to be created in typed memory but no typed memory instance "
"has been provided using the public interface SetTypedMemoryProvider ";
return nullptr;
effective_provider = intervm_memory_ptr_;
}
else if (prefer_typed_memory)
{
if (typed_memory_ptr_ == nullptr)
{
score::mw::log::LogError("shm")
<< "Shared memory has to be created in typed memory but no typed memory instance "
"has been provided using the public interface SetTypedMemoryProvider ";
return nullptr;
}
effective_provider = typed_memory_ptr_;
}

const auto typed_memory_ptr = prefer_typed_memory ? typed_memory_ptr_ : nullptr;
const auto result = SharedMemoryResource::CreateOrOpen(path,
user_space_to_reserve,
std::move(cb),
access_control.permissions_,
&CreateAccessControlList,
typed_memory_ptr);
effective_provider);
if (!result.has_value())
{
score::mw::log::LogWarn("shm") << __func__ << __LINE__ << "Could not create or open Shared Memory " << path
Expand Down Expand Up @@ -382,4 +412,9 @@ auto SharedMemoryFactoryImpl::SetTypedMemoryProvider(std::shared_ptr<TypedMemory
typed_memory_ptr_ = typed_memory_ptr;
}

auto SharedMemoryFactoryImpl::SetInterVMMemoryProvider(std::shared_ptr<TypedMemory> intervm_memory_ptr) noexcept -> void
{
intervm_memory_ptr_ = intervm_memory_ptr;
}

} // namespace score::memory::shared
3 changes: 3 additions & 0 deletions score/memory/shared/shared_memory_factory_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class SharedMemoryFactoryImpl final : public ISharedMemoryFactory

void SetTypedMemoryProvider(std::shared_ptr<score::memory::shared::TypedMemory> typed_memory_ptr) noexcept override;

void SetInterVMMemoryProvider(std::shared_ptr<score::memory::shared::TypedMemory> intervm_memory_ptr) noexcept;

std::size_t GetControlBlockSize() noexcept override
{
return sizeof(score::memory::shared::SharedMemoryResource::ControlBlock);
Expand All @@ -73,6 +75,7 @@ class SharedMemoryFactoryImpl final : public ISharedMemoryFactory
std::mutex mutex_{};
std::unordered_map<std::string, std::weak_ptr<score::memory::shared::SharedMemoryResource>> resources_{};
std::shared_ptr<score::memory::shared::TypedMemory> typed_memory_ptr_{memory::shared::TypedMemory::Default()};
std::shared_ptr<score::memory::shared::TypedMemory> intervm_memory_ptr_{nullptr};
};

} // namespace score::memory::shared
Expand Down
126 changes: 120 additions & 6 deletions score/memory/shared/shared_memory_factory_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1185,36 +1185,43 @@ TEST_F(SharedMemoryFactoryDeathTest, FailingToInsertResourceIntoRegistryTerminat
EXPECT_DEATH(resource_attorney.mapMemoryIntoProcess(), ".*");
}

TEST_F(SharedMemoryFactoryTest, OpenWithInterVmPathReturnsNullptr)
TEST_F(SharedMemoryFactoryTest, OpenWithInterVmPathReturnsNullptrWhenProviderNotSet)
{
// Given a shared memory path using the inter-VM prefix
const std::string inter_vm_path{"/intervm-shared-shmem/lola-data-0000000000004660-00001"};

// And no InterVM memory provider has been set
// (SharedMemoryFactory is fresh after TearDown/SetUp)

// When opening shared memory using this path
// Then nullptr is returned and no OS calls are made (no mock expectations set)
// Then nullptr is returned because InterVM provider is required but not set
const auto result = SharedMemoryFactory::Open(inter_vm_path, false);
EXPECT_EQ(result, nullptr);
}

TEST_F(SharedMemoryFactoryTest, CreateWithInterVmPathReturnsNullptr)
TEST_F(SharedMemoryFactoryTest, CreateWithInterVmPathReturnsNullptrWhenProviderNotSet)
{
// Given a shared memory path using the inter-VM prefix
const std::string inter_vm_path{"/intervm-shared-shmem/lola-ctl-0000000000004660-00001-b"};

// And no InterVM memory provider has been set

// When creating shared memory using this path
// Then nullptr is returned and no OS calls are made (no mock expectations set)
// Then nullptr is returned because InterVM provider is required but not set
const auto result = SharedMemoryFactory::Create(
inter_vm_path, [](std::shared_ptr<ISharedMemoryResource>) {}, kSharedMemorySize, {}, false);
EXPECT_EQ(result, nullptr);
}

TEST_F(SharedMemoryFactoryTest, CreateOrOpenWithInterVmPathReturnsNullptr)
TEST_F(SharedMemoryFactoryTest, CreateOrOpenWithInterVmPathReturnsNullptrWhenProviderNotSet)
{
// Given a shared memory path using the inter-VM prefix
const std::string inter_vm_path{"/intervm-shared-shmem/lola-methods-0000000000004660-00001-00002-00003"};

// And no InterVM memory provider has been set

// When creating or opening shared memory using this path
// Then nullptr is returned and no OS calls are made (no mock expectations set)
// Then nullptr is returned because InterVM provider is required but not set
const auto result = SharedMemoryFactory::CreateOrOpen(
inter_vm_path,
[](std::shared_ptr<ISharedMemoryResource>) {},
Expand All @@ -1224,4 +1231,111 @@ TEST_F(SharedMemoryFactoryTest, CreateOrOpenWithInterVmPathReturnsNullptr)
EXPECT_EQ(result, nullptr);
}

TEST_F(SharedMemoryFactoryTest, OpenWithInterVmPathSucceedsWhenProviderIsSet)
{
InSequence sequence{};

// Given a shared memory path using the inter-VM prefix (use standard test path for simplicity)
// Note: The key test is that SetInterVMMemoryProvider allows the operation, not the path format
const std::string test_path = TestValues::sharedMemorySegmentPath;

// And an InterVM memory provider has been set
auto intervm_mock = std::make_shared<TypedMemoryMock>();
SharedMemoryFactory::SetInterVMMemoryProvider(intervm_mock);

// And the shared memory can be opened successfully
constexpr bool is_read_write = false;
expectSharedMemorySuccessfullyOpened(kFileDescriptor, is_read_write);

// When opening shared memory using this path
const auto result = SharedMemoryFactory::Open(test_path, is_read_write);

// Then the resource is successfully opened
ASSERT_NE(result, nullptr);
EXPECT_EQ(*result->getPath(), test_path);

// Cleanup
SharedMemoryFactory::SetInterVMMemoryProvider(nullptr);
}

TEST_F(SharedMemoryFactoryTest, CreateWithInterVmPathSucceedsWhenProviderIsSet)
{
InSequence sequence{};

// Given a shared memory path (use standard test path for simplicity)
const std::string test_path = TestValues::sharedMemorySegmentPath;

// And an InterVM memory provider has been set
auto intervm_mock = std::make_shared<TypedMemoryMock>();
SharedMemoryFactory::SetInterVMMemoryProvider(intervm_mock);

// And shared memory can be created successfully
std::array<std::uint8_t, kSharedMemorySize> dataRegion{};
const bool prefer_typed_memory = false;
expectSharedMemorySuccessfullyCreated(
kFileDescriptor, kLockFileDescriptor, dataRegion.data(), prefer_typed_memory);

EXPECT_CALL(*mman_mock_, munmap(_, _));
EXPECT_CALL(*unistd_mock_, close(kFileDescriptor));

// When creating shared memory using this path
const auto result = SharedMemoryFactory::Create(
test_path, [](std::shared_ptr<ISharedMemoryResource>) {}, kSharedMemorySize, {}, prefer_typed_memory);

// Then the resource is successfully created
ASSERT_NE(result, nullptr);
EXPECT_EQ(*result->getPath(), test_path);

// Cleanup
SharedMemoryFactory::SetInterVMMemoryProvider(nullptr);
}

TEST_F(SharedMemoryFactoryTest, CreateOrOpenWithInterVmPathSucceedsWhenProviderIsSet)
{
InSequence sequence{};

// Given a shared memory path (use standard test path for simplicity)
const std::string test_path = TestValues::sharedMemorySegmentPath;

// And an InterVM memory provider has been set
auto intervm_mock = std::make_shared<TypedMemoryMock>();
SharedMemoryFactory::SetInterVMMemoryProvider(intervm_mock);

// And shared memory can be created successfully (CreateOrOpen tries Create after Open fails)
std::array<std::uint8_t, kSharedMemorySize> dataRegion{};
const bool prefer_typed_memory = false;

// CreateOrOpen will first try to Open (which fails), then Create
constexpr std::int32_t open_lock_file_descriptor = 10;
constexpr bool is_read_write = true;

// Expect the Open attempt to fail
expectCreateLockFileReturns(TestValues::sharedMemorySegmentLockPath, open_lock_file_descriptor);
expectShmOpenReturns(test_path, score::cpp::make_unexpected(Error::createFromErrno(ENOENT)), is_read_write);
EXPECT_CALL(*unistd_mock_, close(open_lock_file_descriptor));
EXPECT_CALL(*unistd_mock_, unlink(StrEq(TestValues::sharedMemorySegmentLockPath)));

// Then expect the Create to succeed
expectSharedMemorySuccessfullyCreated(
kFileDescriptor, kLockFileDescriptor, dataRegion.data(), prefer_typed_memory);

EXPECT_CALL(*mman_mock_, munmap(_, _));
EXPECT_CALL(*unistd_mock_, close(kFileDescriptor));

// When creating or opening shared memory using this path
const auto result = SharedMemoryFactory::CreateOrOpen(
test_path,
[](std::shared_ptr<ISharedMemoryResource>) {},
kSharedMemorySize,
SharedMemoryResource::AccessControl{{}, {}},
prefer_typed_memory);

// Then the resource is successfully created or opened
ASSERT_NE(result, nullptr);
EXPECT_EQ(*result->getPath(), test_path);

// Cleanup
SharedMemoryFactory::SetInterVMMemoryProvider(nullptr);
}

} // namespace score::memory::shared::test
Loading