Bug 1795311 - A new shared memory class with more flexible memory management. r=nika,jld

Differential Revision: https://phabricator.services.mozilla.com/D159398
This commit is contained in:
Nicolas Silva 2022-11-10 15:52:31 +00:00
Родитель ac1337afd8
Коммит a1971c2e74
5 изменённых файлов: 288 добавлений и 0 удалений

114
ipc/glue/RawShmem.cpp Normal file
Просмотреть файл

@ -0,0 +1,114 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RawShmem.h"
#include "mozilla/ipc/ProtocolUtils.h"
namespace mozilla::ipc {
UnsafeSharedMemoryHandle::UnsafeSharedMemoryHandle()
: mHandle(ipc::SharedMemoryBasic::NULLHandle()), mSize(0) {}
UnsafeSharedMemoryHandle::UnsafeSharedMemoryHandle(
UnsafeSharedMemoryHandle&& aOther) noexcept
: mHandle(std::move(aOther.mHandle)), mSize(aOther.mSize) {
aOther.mHandle = ipc::SharedMemoryBasic::NULLHandle();
aOther.mSize = 0;
}
UnsafeSharedMemoryHandle& UnsafeSharedMemoryHandle::operator=(
UnsafeSharedMemoryHandle&& aOther) noexcept {
if (this == &aOther) {
return *this;
}
mHandle = std::move(aOther.mHandle);
mSize = aOther.mSize;
aOther.mHandle = ipc::SharedMemoryBasic::NULLHandle();
aOther.mSize = 0;
return *this;
}
Maybe<std::pair<UnsafeSharedMemoryHandle, WritableSharedMemoryMapping>>
UnsafeSharedMemoryHandle::CreateAndMap(size_t aSize) {
if (aSize == 0) {
return Some(std::make_pair(UnsafeSharedMemoryHandle(),
WritableSharedMemoryMapping()));
}
RefPtr<ipc::SharedMemoryBasic> shm = MakeAndAddRef<ipc::SharedMemoryBasic>();
if (NS_WARN_IF(!shm->Create(aSize)) || NS_WARN_IF(!shm->Map(aSize))) {
return Nothing();
}
// TODO(bug 1797039): At the moment the handle/mapping distinction is
// implemented on top of a single class SharedMemoryBasic. It leads to a few
// awkward or sub-optimal things such as how the following few lines clone
// then close the handle. It would be better to separate the underlying
// implementation or steal the handle to avoid cloning it.
auto handle = shm->CloneHandle();
shm->CloseHandle();
auto size = shm->Size();
return Some(std::make_pair(UnsafeSharedMemoryHandle(std::move(handle), size),
WritableSharedMemoryMapping(std::move(shm))));
}
WritableSharedMemoryMapping::WritableSharedMemoryMapping(
RefPtr<ipc::SharedMemoryBasic>&& aRef)
: mRef(aRef) {}
Maybe<WritableSharedMemoryMapping> WritableSharedMemoryMapping::Open(
UnsafeSharedMemoryHandle aHandle) {
if (aHandle.mSize == 0) {
return Some(WritableSharedMemoryMapping(nullptr));
}
RefPtr<ipc::SharedMemoryBasic> shm = MakeAndAddRef<ipc::SharedMemoryBasic>();
if (NS_WARN_IF(!shm->SetHandle(std::move(aHandle.mHandle),
ipc::SharedMemory::RightsReadWrite)) ||
NS_WARN_IF(!shm->Map(aHandle.mSize))) {
return Nothing();
}
shm->CloseHandle();
return Some(WritableSharedMemoryMapping(std::move(shm)));
}
size_t WritableSharedMemoryMapping::Size() const {
if (!mRef) {
return 0;
}
return mRef->Size();
}
Span<uint8_t> WritableSharedMemoryMapping::Bytes() {
if (!mRef) {
return Span<uint8_t>();
}
uint8_t* mem = static_cast<uint8_t*>(mRef->memory());
return Span(mem, mRef->Size());
}
} // namespace mozilla::ipc
namespace IPC {
auto ParamTraits<mozilla::ipc::UnsafeSharedMemoryHandle>::Write(
IPC::MessageWriter* aWriter, paramType&& aVar) -> void {
IPC::WriteParam(aWriter, std::move(aVar.mHandle));
IPC::WriteParam(aWriter, aVar.mSize);
}
auto ParamTraits<mozilla::ipc::UnsafeSharedMemoryHandle>::Read(
IPC::MessageReader* aReader, paramType* aVar) -> bool {
return IPC::ReadParam(aReader, &aVar->mHandle) &&
IPC::ReadParam(aReader, &aVar->mSize);
}
} // namespace IPC

113
ipc/glue/RawShmem.h Normal file
Просмотреть файл

@ -0,0 +1,113 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_IPC_RAWSHMEM_H_
#define MOZILLA_IPC_RAWSHMEM_H_
#include "mozilla/ipc/SharedMemoryBasic.h"
#include "mozilla/Span.h"
#include <utility>
namespace mozilla::ipc {
class WritableSharedMemoryMapping;
/// A handle to shared memory.
///
/// See the doc comment for `WritableSharedMemoryMapping` below.
class UnsafeSharedMemoryHandle {
friend class WritableSharedMemoryMapping;
friend struct IPC::ParamTraits<UnsafeSharedMemoryHandle>;
public:
UnsafeSharedMemoryHandle();
UnsafeSharedMemoryHandle(UnsafeSharedMemoryHandle&& aOther) noexcept;
UnsafeSharedMemoryHandle& operator=(
UnsafeSharedMemoryHandle&& aOther) noexcept;
/// Attempts to allocate a shmem.
///
/// Returns `Nothing()` if allocation fails.
/// If `aSize` is zero, a valid empty WritableSharedMemoryMapping is returned.
static Maybe<std::pair<UnsafeSharedMemoryHandle, WritableSharedMemoryMapping>>
CreateAndMap(size_t aSize);
private:
UnsafeSharedMemoryHandle(SharedMemoryBasic::Handle&& aHandle, uint64_t aSize)
: mHandle(std::move(aHandle)), mSize(aSize) {}
SharedMemoryBasic::Handle mHandle;
uint64_t mSize;
};
/// A Shared memory buffer mapping.
///
/// Unlike `ipc::Shmem`, the underlying shared memory buffer on each side of
/// the process boundary is only deallocated with there respective
/// `WritableSharedMemoryMapping`.
///
/// ## Usage
///
/// Typical usage goes as follows:
/// - Allocate the memory using `UnsafeSharedMemoryHandle::Create`, returning a
/// handle and a mapping.
/// - Send the handle to the other process using an IPDL message.
/// - On the other process, map the shared memory by creating
/// WritableSharedMemoryMapping via `WritableSharedMemoryMapping::Open` and
/// the received handle.
///
/// Do not send the shared memory handle again, it is only intended to establish
/// the mapping on each side during initialization. The user of this class is
/// responsible for managing the lifetime of the buffers on each side, as well
/// as their identity, by for example storing them in hash map and referring to
/// them via IDs in IPDL message if need be.
///
/// ## Empty shmems
///
/// An empty WritableSharedMemoryMapping is one that was created with size zero.
/// It is analogous to a null RefPtr. It can be used like a non-empty shmem,
/// including sending the handle and openning it on another process (resulting
/// in an empty mapping on the other side).
class WritableSharedMemoryMapping {
friend class UnsafeSharedMemoryHandle;
public:
WritableSharedMemoryMapping() = default;
WritableSharedMemoryMapping(WritableSharedMemoryMapping&& aMoved) =
default;
WritableSharedMemoryMapping& operator=(WritableSharedMemoryMapping&& aMoved) =
default;
/// Open the shmem and immediately close the handle.
static Maybe<WritableSharedMemoryMapping> Open(
UnsafeSharedMemoryHandle aHandle);
// Returns the size in bytes.
size_t Size() const;
// Returns the shared memory as byte range.
Span<uint8_t> Bytes();
private:
explicit WritableSharedMemoryMapping(RefPtr<ipc::SharedMemoryBasic>&& aRef);
RefPtr<ipc::SharedMemoryBasic> mRef;
};
} // namespace mozilla::ipc
namespace IPC {
template <>
struct ParamTraits<mozilla::ipc::UnsafeSharedMemoryHandle> {
typedef mozilla::ipc::UnsafeSharedMemoryHandle paramType;
static void Write(IPC::MessageWriter* aWriter, paramType&& aVar);
static bool Read(IPC::MessageReader* aReader, paramType* aVar);
};
} // namespace IPC
#endif

Просмотреть файл

@ -52,6 +52,7 @@ EXPORTS.mozilla.ipc += [
"ProtocolUtils.h",
"ProtocolUtilsFwd.h",
"RandomAccessStreamUtils.h",
"RawShmem.h",
"ScopedPort.h",
"ScopedXREEmbed.h",
"SerializedStructuredCloneBuffer.h",
@ -180,6 +181,7 @@ UNIFIED_SOURCES += [
"ProcessUtils_common.cpp",
"ProtocolUtils.cpp",
"RandomAccessStreamUtils.cpp",
"RawShmem.cpp",
"ScopedPort.cpp",
"ScopedXREEmbed.cpp",
"SerializedStructuredCloneBuffer.cpp",

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "chrome/common/ipc_message.h"
#include "chrome/common/ipc_message_utils.h"
#include "gtest/gtest.h"
#include "mozilla/RandomNum.h"
#include "mozilla/ipc/RawShmem.h"
namespace mozilla::ipc {
static bool SerializeAndDeserialize(UnsafeSharedMemoryHandle&& aIn,
UnsafeSharedMemoryHandle* aOut) {
IPC::Message msg(MSG_ROUTING_NONE, 0);
{
IPC::MessageWriter writer(msg);
IPC::WriteParam(&writer, std::move(aIn));
}
IPC::MessageReader reader(msg);
return IPC::ReadParam(&reader, aOut);
}
void TestUnsafeSharedMemoryHandle(size_t aSize) {
auto maybeHandle = UnsafeSharedMemoryHandle::CreateAndMap(aSize);
ASSERT_TRUE(maybeHandle);
auto [handle, mapping] = maybeHandle.extract();
EXPECT_EQ(mapping.Size(), aSize);
auto inBytes = mapping.Bytes();
for (size_t i = 0; i < aSize; ++i) {
inBytes[i] = static_cast<uint8_t>(i % 255);
}
UnsafeSharedMemoryHandle out;
ASSERT_TRUE(SerializeAndDeserialize(std::move(handle), &out));
auto mapping2 = WritableSharedMemoryMapping::Open(std::move(out)).value();
EXPECT_EQ(mapping2.Size(), aSize);
auto outBytes = mapping2.Bytes();
for (size_t i = 0; i < aSize; ++i) {
EXPECT_EQ(outBytes[i], static_cast<uint8_t>(i % 255));
}
}
TEST(UnsafeSharedMemoryHandle, Empty)
{ TestUnsafeSharedMemoryHandle(0); }
TEST(UnsafeSharedMemoryHandle, SmallSize)
{ TestUnsafeSharedMemoryHandle(2048); }
} // namespace mozilla::ipc

Просмотреть файл

@ -12,6 +12,7 @@ SOURCES += [
"TestLogging.cpp",
"TestRandomAccessStreamUtils.cpp",
"TestSharedMemory.cpp",
"TestUnsafeSharedMemoryHandle.cpp",
]
include("/ipc/chromium/chromium-config.mozbuild")