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 return 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 actually allocate some memory to the Shmem
, you need an IPDL actor.
In the WebGPU content process code, mBridge
is a PWebGPUChild
actor:
if (!mBridge->AllocShmem(size, &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.
The AllocShmem
method is defined on IProtocol::AllocShmem
,
the base class for all IPDL-generated actor classes:
class IProtocol {
public:
bool AllocShmem(size_t aSize, Shmem* aOutMem);
...
};
The WebGPU code above goes on to call 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.
TODO: explain what copy construction and assignment actually does
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
.
Once initial setup is complete,
this file descriptor is closed in both processes.)
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