分享
三行代码  ›  专栏  ›  技术社区  ›  Vitali

Cap'n'proto过早破坏界面?

  •  0
  • Vitali  · 技术社区  · 1 周前

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

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

    我遇到了一个问题,服务器实现的析构函数 MyInterface 在释放最后一个注册的接口之前正在运行。我如何正确地与cap'n'proto-RPC通信,以便即使客户端发布它 MyInterface::Client RegisteredListener::Client ,或者 MyInterface::Server 寿命延长或 RegisteredListener::Server 足够聪明,可以识别其跟踪注册的原始服务器实例已死亡。或者,我在用一些基本的API?

    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 第二个听众在 . 我不希望我的客户有一个真正的细节,但我不希望这是一个重要的对象。

    1 回复  |  直到 1 周前
        1
  •  0
  •   Kenton Varda    1 周前

    我建议 Registered 班级保持a MyInterface::Client

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

    注册 仍然存在 MyInterfaceImpl 不会被摧毁。

    内部实现 addListener() MyInterface::客户端 指向 this thisCap() .

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