【问题标题】:Cap'n'proto premature destruction of interface?Cap'n'proto过早破坏接口?
【发布时间】:2020-06-29 23:55:15
【问题描述】:

假设我有一个这样的流式 API:

interface MyInterface {
  addListener @0 (listener: Listener) -> (registration: RegisteredListener);

  interface Listener {
    update @0 () -> stream;
  }
  interface RegisteredListener {
  }
}

我遇到了一个挑战,即MyInterface 的服务器实现的析构函数在最后一个注册接口释放之前运行。如何正确地将关系传达给 cap'n'proto RPC,以便即使客户端在 RegisteredListener::Client 之前释放它的 MyInterface::ClientMyInterface::Server 的生命周期也会延长,或者 RegisteredListener::Server 足够聪明,可以识别跟踪注册的原始服务器实例已失效。或者,我是否以某种基本方式滥用 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);
}

由于这都是单线程的,因此很容易发现 use-after-free。我将调试语句放在~MyInterfaceImpl~RegisteredListener 中,第二个侦听器在~MyInterfaceImpl 之后被取消注册。我知道我的添加侦听器请求没有实际的客户端对象,但我希望这实际上不是一个重要的细节。

【问题讨论】:

    标签: c++ capnproto


    【解决方案1】:

    我建议让Registered 类持有一个指向父级的MyInterface::Client,除了它当前持有的普通指针。

    MyInterfaceImpl *parent_;
    MyInterface::Client ownParent_;
    uint64_t id_;
    

    这确保只要Registered 仍然存在,MyInterfaceImpl 就不会被销毁。

    addListener()的实现内部,你可以通过调用thisCap()获得一个指向thisMyInterface::Client

    context.getResult().setRegistration(kj::heap<Registered>(
        this, thisCap(), registrationId));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-15
      • 2016-01-03
      • 2017-04-26
      • 1970-01-01
      • 1970-01-01
      • 2017-06-13
      • 2010-12-04
      • 1970-01-01
      相关资源
      最近更新 更多