Skip to content

Commit 361c02b

Browse files
authored
Merge pull request #6233 from cloudflare/ketan/exmem-rpc
Add external memory accounting to JsRpcStub deserialization
2 parents 5971aaa + f31d336 commit 361c02b

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

src/workerd/api/worker-rpc.c++

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -731,10 +731,12 @@ kj::Maybe<jsg::Ref<JsRpcProperty>> JsRpcPromise::getProperty(jsg::Lock& js, kj::
731731
return js.alloc<JsRpcProperty>(JSG_THIS, kj::mv(name));
732732
}
733733

734-
JsRpcStub::JsRpcStub(
735-
IoOwn<rpc::JsRpcTarget::Client> capnpClient, RpcStubDisposalGroup& disposalGroup)
734+
JsRpcStub::JsRpcStub(IoOwn<rpc::JsRpcTarget::Client> capnpClient,
735+
RpcStubDisposalGroup& disposalGroup,
736+
jsg::ExternalMemoryAdjustment externalMemoryAdjustment)
736737
: capnpClient(kj::mv(capnpClient)),
737-
disposalGroup(disposalGroup) {
738+
disposalGroup(disposalGroup),
739+
externalMemoryAdjustment(kj::mv(externalMemoryAdjustment)) {
738740
disposalGroup.list.add(*this);
739741
}
740742

@@ -834,6 +836,7 @@ jsg::Ref<JsRpcStub> JsRpcStub::dup(jsg::Lock& js) {
834836

835837
void JsRpcStub::dispose() {
836838
capnpClient = kj::none;
839+
externalMemoryAdjustment = kj::none;
837840
KJ_IF_SOME(d, disposalGroup) {
838841
d.list.remove(*this);
839842
disposalGroup = kj::none;
@@ -916,8 +919,13 @@ jsg::Ref<JsRpcStub> JsRpcStub::deserialize(
916919
KJ_REQUIRE(reader.isRpcTarget(), "external table slot type doesn't match serialization tag");
917920

918921
auto& ioctx = IoContext::current();
919-
return js.alloc<JsRpcStub>(
920-
ioctx.addObject(kj::heap(reader.getRpcTarget())), externalHandler->getDisposalGroup());
922+
923+
// Account for membrane/promise memory in the KJ heap (~1600 bytes per stub from profiling).
924+
static constexpr size_t ESTIMATED_EXTERNAL_MEMORY_PER_STUB = 1600;
925+
auto externalMemory = js.getExternalMemoryAdjustment(ESTIMATED_EXTERNAL_MEMORY_PER_STUB);
926+
927+
return js.alloc<JsRpcStub>(ioctx.addObject(kj::heap(reader.getRpcTarget())),
928+
externalHandler->getDisposalGroup(), kj::mv(externalMemory));
921929
}
922930

923931
static bool isFunctionForRpc(jsg::Lock& js, v8::Local<v8::Function> func) {

src/workerd/api/worker-rpc.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,13 @@ class JsRpcProperty: public JsRpcClientProvider {
370370
// `JsRpcStub::sendJsRpc()`.
371371
class JsRpcStub: public JsRpcClientProvider {
372372
public:
373+
// Only deserialize() needs ExternalMemoryAdjustment — it extracts a new capability from an RPC
374+
// response, creating MembraneHook + forked promise allocations. The other callers (dup() and
375+
// constructor()) just refcount existing capabilities or create local loopbacks.
373376
JsRpcStub(IoOwn<rpc::JsRpcTarget::Client> capnpClient): capnpClient(kj::mv(capnpClient)) {}
374-
JsRpcStub(IoOwn<rpc::JsRpcTarget::Client> capnpClient, RpcStubDisposalGroup& disposalGroup);
377+
JsRpcStub(IoOwn<rpc::JsRpcTarget::Client> capnpClient,
378+
RpcStubDisposalGroup& disposalGroup,
379+
jsg::ExternalMemoryAdjustment externalMemoryAdjustment);
375380
~JsRpcStub() noexcept(false);
376381

377382
rpc::JsRpcTarget::Client getClient();
@@ -414,6 +419,7 @@ class JsRpcStub: public JsRpcClientProvider {
414419

415420
kj::Maybe<RpcStubDisposalGroup&> disposalGroup;
416421
kj::ListLink<JsRpcStub> disposalGroupLink;
422+
kj::Maybe<jsg::ExternalMemoryAdjustment> externalMemoryAdjustment;
417423

418424
friend class RpcStubDisposalGroup;
419425
};

0 commit comments

Comments
 (0)