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
Shmems, 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