The mozilla::ipc::Shmem
type represents a block of memory that IPDL messages can transfer between processes without having to copy the contents. IPDL messages can pass and Shmem
arguments, as in these examples from PWebGPU.ipdl
:
async BufferReturnShmem(RawId selfId, Shmem shmem);
async BufferMap(RawId selfId, WGPUHostMap hostMap, uint64_t offset, uint64_t size) returns (Shmem sm);
Given a Shmem
s
, you can call s.Size<T>
to get the length of the shared memory block considered as an array of T
values, and s.get<T>
to get a T*
pointing to the first element.
A Shmem
is usually first constructed with no memory allocated to it, using the default constructor. For example, theDevice::CreateBuffer
method in Firefox's WebGPU implementation declares a Shmem
like this:
ipc::Shmem shmem;
Calling get
or Size
on a Shmem
in this state will assert. To allocate some memory to a Shmem
, call IProtocol::AllocShmem
:
class IProtocol {
public:
bool AllocShmem(size_t aSize, Shmem* aOutMem);
...
};
IProtocol
is the base class for all IPDL-generated actor classes, so the AllocShmem
method is available on any actor object.
The WebGPU code above continues:
if (!mBridge->AllocShmem(size, ipc::Shmem::SharedMemory::TYPE_BASIC,
&shmem)) {
aRv.ThrowAbortError(
nsPrintfCString("Unable to allocate shmem of size %" PRIuPTR, size));
return nullptr;
}
// zero out memory
memset(shmem.get<uint8_t>(), 0, size);
This calls AllocShmem
on the IPDL child actor mBridge
to populate shmem
with size
bytes, calls shmem.get
to obtain a pointer to the shared memory block, and then zeros out its contents. Then it calls an IPDL Send
method to transmit the shared buffer to its parent actor:
mBridge->SendBufferReturnShmem(id, std::move(shmem));
The IPDL Recv
method receives the shared buffer like this:
ipc::IPCResult WebGPUParent::RecvBufferReturnShmem(RawId aSelfId,
Shmem&& aShmem) {
...
}
Sending a Shmem
in an IPDL message causes the sender to lose all access to the Shmem
's pages. Receiving a Shmem
grants the receiver access to those pages. (On Linux, these are mprotect
system calls.)
Even though IPDL generates rvalue references for sending and receiving Shmem
s, it doesn't actually matter: Shmem
doesn't define move constructors or assignment operators, only a defaulted copy constructor and assignment operator.
When a process calls actor->AllocShmem
to allocate memory for a Shmem
, that immediately sends a message to the other process carrying a platform-specific handle to the shared memory. The recipient uses this handle to map the shared memory into its own address space, initially prohibiting all access to the pages.
(On Linux, IPDL uses the sendmsg
system call's SCM_RIGHTS
message type to send a file descriptor created with memfd_create
. This file descriptor is closed in both processes once initial setup is complete.)
AllocShmem
also assigns the Shmem
a unique id, and registers it under that id in IToplevelProtocol::mShmemMap
. The other process does the same when it receives notification of the new Shmem
. Once the Shmem
has been allocated, sending it in IPDL messages actually just sends this id back and forth. The recipient uses the mShmemMap
entry to reconstruct the Shmem
and permit access to its pages.
TODO