зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
ac1337afd8
Коммит
a1971c2e74
|
@ -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
|
|
@ -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")
|
||||
|
|
Загрузка…
Ссылка в новой задаче