Cap'n'proto 接口过早破坏?
Cap'n'proto premature destruction of interface?
假设我有这样的流式传输 API:
interface MyInterface {
addListener @0 (listener: Listener) -> (registration: RegisteredListener);
interface Listener {
update @0 () -> stream;
}
interface RegisteredListener {
}
}
我遇到了一个挑战,即 MyInterface
服务器实现的析构函数是 运行 在最后一个注册接口被释放之前。我如何正确传达与 cap'n'proto RPC 的关系,以便即使客户端在 RegisteredListener::Client
之前释放它的 MyInterface::Client
,MyInterface::Server
生命周期也会延长或 [=17] =] 足够聪明,可以识别出其跟踪注册的原始服务器实例已失效。或者,我是否以某种基本方式滥用了 API?
C++ 代码大致如下所示。服务器:
class MyInterfaceImpl : public MyInterface {
class Registered final : public MyInterface::RegisteredListener::Server {
public:
Registered(MyInterfaceImpl* parent, uint64_t registrationId) : parent_(parent), id_(registrationId) {}
~Registered() {
parent->unregister(id_);
}
private:
MyInterfaceImpl *parent_;
uint64_t id_;
};
public:
kj::Promise<void> addListener(MyInterface::AddListenerContext context) {
auto registrationId = ++registrationId_;
clients_.emplace_back(context.getParams().getListener());
registrations_.emplace_back(registrationId);
context.getResult().setRegistration(kj::heap<Registered>(this, registrationId));
return kj::READY_NOW;
}
void unregister(uint64_t registrationId) {
auto found = std::find(registrations_.begin(), registrations_.end(), registrationId);
clients_.erase(clients_.begin() + (found - registrations_.begin()));
}
private:
std::vector<MyInterface::Listener::Client> clients_;
std::vector<uint64_t> registrations_;
uint64_t registrationId_ = 0;
};
客户端代码如下所示:
capnp::EzRpcClient client("localhost:5923");
MyInterface::Client intf = client.getMain<MyInterface>();
auto& waitScope = client.getWaitScope();
auto listenerRequest = intf.addListenerRequest();
auto listenerPromise = listenerRequest.send();
listenerPromise.wait(waitScope);
{
auto listenerRequest2 = intf.addListenerRequest();
auto listenerPromise2 = listenerRequest2.send();
listenerPromise2.wait(waitScope);
}
因为这都是单线程的,所以很容易发现释放后使用。我将调试语句放在 ~MyInterfaceImpl
和 ~RegisteredListener
中,第二个侦听器在 ~MyInterfaceImpl
之后注销。我知道我的添加侦听器请求没有实际的客户端对象,但我希望这实际上不是一个重要的细节。
我建议让 Registered
class 持有一个 MyInterface::Client
指向父级,除了它当前持有的普通指针。
MyInterfaceImpl *parent_;
MyInterface::Client ownParent_;
uint64_t id_;
这确保只要 Registered
仍然存在,MyInterfaceImpl
就不会被销毁。
在addListener()
的实现中,可以通过调用thisCap()
.
获得指向this
的MyInterface::Client
context.getResult().setRegistration(kj::heap<Registered>(
this, thisCap(), registrationId));
假设我有这样的流式传输 API:
interface MyInterface {
addListener @0 (listener: Listener) -> (registration: RegisteredListener);
interface Listener {
update @0 () -> stream;
}
interface RegisteredListener {
}
}
我遇到了一个挑战,即 MyInterface
服务器实现的析构函数是 运行 在最后一个注册接口被释放之前。我如何正确传达与 cap'n'proto RPC 的关系,以便即使客户端在 RegisteredListener::Client
之前释放它的 MyInterface::Client
,MyInterface::Server
生命周期也会延长或 [=17] =] 足够聪明,可以识别出其跟踪注册的原始服务器实例已失效。或者,我是否以某种基本方式滥用了 API?
C++ 代码大致如下所示。服务器:
class MyInterfaceImpl : public MyInterface {
class Registered final : public MyInterface::RegisteredListener::Server {
public:
Registered(MyInterfaceImpl* parent, uint64_t registrationId) : parent_(parent), id_(registrationId) {}
~Registered() {
parent->unregister(id_);
}
private:
MyInterfaceImpl *parent_;
uint64_t id_;
};
public:
kj::Promise<void> addListener(MyInterface::AddListenerContext context) {
auto registrationId = ++registrationId_;
clients_.emplace_back(context.getParams().getListener());
registrations_.emplace_back(registrationId);
context.getResult().setRegistration(kj::heap<Registered>(this, registrationId));
return kj::READY_NOW;
}
void unregister(uint64_t registrationId) {
auto found = std::find(registrations_.begin(), registrations_.end(), registrationId);
clients_.erase(clients_.begin() + (found - registrations_.begin()));
}
private:
std::vector<MyInterface::Listener::Client> clients_;
std::vector<uint64_t> registrations_;
uint64_t registrationId_ = 0;
};
客户端代码如下所示:
capnp::EzRpcClient client("localhost:5923");
MyInterface::Client intf = client.getMain<MyInterface>();
auto& waitScope = client.getWaitScope();
auto listenerRequest = intf.addListenerRequest();
auto listenerPromise = listenerRequest.send();
listenerPromise.wait(waitScope);
{
auto listenerRequest2 = intf.addListenerRequest();
auto listenerPromise2 = listenerRequest2.send();
listenerPromise2.wait(waitScope);
}
因为这都是单线程的,所以很容易发现释放后使用。我将调试语句放在 ~MyInterfaceImpl
和 ~RegisteredListener
中,第二个侦听器在 ~MyInterfaceImpl
之后注销。我知道我的添加侦听器请求没有实际的客户端对象,但我希望这实际上不是一个重要的细节。
我建议让 Registered
class 持有一个 MyInterface::Client
指向父级,除了它当前持有的普通指针。
MyInterfaceImpl *parent_;
MyInterface::Client ownParent_;
uint64_t id_;
这确保只要 Registered
仍然存在,MyInterfaceImpl
就不会被销毁。
在addListener()
的实现中,可以通过调用thisCap()
.
this
的MyInterface::Client
context.getResult().setRegistration(kj::heap<Registered>(
this, thisCap(), registrationId));