node-capnp
node-capnp copied to clipboard
close() on pipeline capability does not cause remote object to be immediately dropped
Similar to #4.
# test.capnp
@0x8314444a113b636e;
interface Grault {}
interface Foo {
bar @0 () -> ();
baz @1 () -> (grault : Grault);
}
// test-server.c++
#include "test.capnp.h"
#include <capnp/ez-rpc.h>
#include <iostream>
class GraultImpl final: public Grault::Server {
public:
~GraultImpl() {
std::cout << "Destroying a GraultImpl\n";
}
};
class FooImpl final: public Foo::Server {
public:
kj::Promise<void> baz(BazContext context) override {
std::cout << "inside of baz\n";
auto results = context.getResults();
results.setGrault(kj::heap<GraultImpl>());
return kj::READY_NOW;
}
};
int main(int argc, const char* argv[]) {
capnp::EzRpcServer server(kj::heap<FooImpl>(), "127.0.0.1:1234");
auto& waitScope = server.getWaitScope();
kj::NEVER_DONE.wait(waitScope);
}
// test.js
Capnp = require('capnp');
Foo = Capnp.import('test.capnp').Foo;
const conn = Capnp.connect("127.0.0.1:1234");
const cap = conn.restore(null, Foo);
const grault = cap.baz().grault;
grault.close();
console.log("called close()");
If I run test-server
in one terminal and node test.js
in another, I expect to immediately see "Destroying a GraultImpl` in the first terminal output. Instead, that does not get printed until I ctrl-c the javascript program.
The problem is observable in Sandstorm's proxy.js, which keeps a pipeline cap of newSession().session
and calls close()
on it when the proxy closes. @mrdomino noticed that this does not actually cause the websession object in the grain to be dropped immediately.
It does appear, however, that the remote object does eventually get dropped due to garbage collection. If I run the following client (with node --expose-gc
), then the GraultImpl
does get dropped right away:
Capnp = require('capnp');
Foo = Capnp.import('test.capnp').Foo;
const conn = Capnp.connect("127.0.0.1:1234");
{
const cap = conn.restore(null, Foo);
const grault = cap.baz().grault;
grault.close();
console.log("closed");
}
setTimeout(() => {
const conn1 = conn; // prevent segfault
console.log("garbage collecting...");
global.gc();
console.log("gc'ed");
}, 200);