Home | History | Annotate | Download | only in ipc
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef INCLUDE_PERFETTO_IPC_DEFERRED_H_
     18 #define INCLUDE_PERFETTO_IPC_DEFERRED_H_
     19 
     20 #include <functional>
     21 #include <memory>
     22 #include <utility>
     23 
     24 #include "perfetto/ipc/async_result.h"
     25 #include "perfetto/ipc/basic_types.h"
     26 
     27 namespace perfetto {
     28 namespace ipc {
     29 
     30 // This class is a wrapper for a callback handling async results.
     31 // The problem this is solving is the following: For each result argument of the
     32 // methods generated from the .proto file:
     33 // - The client wants to see something on which it can Bind() a callback, which
     34 //   is invoked asynchronously once reply is received from the host.
     35 // - The host wants to expose something to user code that implements the IPC
     36 //   methods to allow them to provide an asynchronous reply back to the client.
     37 //   Eventually even more than once, for the case streaming replies.
     38 //
     39 // In both cases we want to make sure that callbacks don't get lost along the
     40 // way. To address this, this class will automatically reject the callbacks
     41 // if they are not resolved at destructor time (or the object is std::move()'d).
     42 //
     43 // The client is supposed to use this class as follows:
     44 //   class GreeterProxy {
     45 //      void SayHello(const HelloRequest&, Deferred<HelloReply> reply)
     46 //   }
     47 //  ...
     48 //  Deferred<HelloReply> reply;
     49 //  reply.Bind([] (AsyncResult<HelloReply> reply) {
     50 //    std::cout << reply.success() ? reply->message : "failure";
     51 //  });
     52 //  host_proxy_instance.SayHello(req, std::move(reply));
     53 //
     54 // The host instead is supposed to use this as follows:
     55 //   class GreeterImpl : public Greeter {
     56 //     void SayHello(const HelloRequest& req, Deferred<HelloReply> reply) {
     57 //        AsyncResult<HelloReply> reply = AsyncResult<HelloReply>::Create();
     58 //        reply->set_greeting("Hello " + req.name)
     59 //        reply.Resolve(std::move(reply));
     60 //     }
     61 //   }
     62 // Or for more complex cases, the deferred object can be std::move()'d outside
     63 // and the reply can continue asynchronously later.
     64 
     65 template <typename T>
     66 class Deferred;
     67 
     68 class DeferredBase {
     69  public:
     70   explicit DeferredBase(
     71       std::function<void(AsyncResult<ProtoMessage>)> callback = nullptr);
     72 
     73   template <typename T>
     74   explicit DeferredBase(Deferred<T> other)
     75       : callback_(std::move(other.callback_)) {}
     76 
     77   ~DeferredBase();
     78   DeferredBase(DeferredBase&&) noexcept;
     79   DeferredBase& operator=(DeferredBase&&);
     80   void Bind(std::function<void(AsyncResult<ProtoMessage>)> callback);
     81   bool IsBound() const;
     82   void Resolve(AsyncResult<ProtoMessage>);
     83   void Reject();
     84 
     85  protected:
     86   template <typename T>
     87   friend class Deferred;
     88   void Move(DeferredBase&);
     89 
     90   std::function<void(AsyncResult<ProtoMessage>)> callback_;
     91 };
     92 
     93 template <typename T = ProtoMessage>
     94 class Deferred : public DeferredBase {
     95  public:
     96   explicit Deferred(std::function<void(AsyncResult<T>)> callback = nullptr) {
     97     Bind(std::move(callback));
     98   }
     99 
    100   // This move constructor (and the similar one in DeferredBase) is meant to be
    101   // called only by the autogenerated code. The caller has to guarantee that the
    102   // moved-from and moved-to types match. The behavior is otherwise undefined.
    103   explicit Deferred(DeferredBase&& other) {
    104     callback_ = std::move(other.callback_);
    105     other.callback_ = nullptr;
    106   }
    107 
    108   void Bind(std::function<void(AsyncResult<T>)> callback) {
    109     if (!callback)
    110       return;
    111 
    112     // Here we need a callback adapter to downcast the callback to a generic
    113     // callback that takes an AsyncResult<ProtoMessage>, so that it can be
    114     // stored in the base class |callback_|.
    115     auto callback_adapter = [callback](
    116                                 AsyncResult<ProtoMessage> async_result_base) {
    117       // Upcast the async_result from <ProtoMessage> -> <T : ProtoMessage>.
    118       static_assert(std::is_base_of<ProtoMessage, T>::value, "T:ProtoMessage");
    119       AsyncResult<T> async_result(
    120           std::unique_ptr<T>(static_cast<T*>(async_result_base.release_msg())),
    121           async_result_base.has_more(), async_result_base.fd());
    122       callback(std::move(async_result));
    123     };
    124     DeferredBase::Bind(callback_adapter);
    125   }
    126 
    127   // If no more messages are expected, |callback_| is released.
    128   void Resolve(AsyncResult<T> async_result) {
    129     // Convert the |async_result| to the generic base one (T -> ProtoMessage).
    130     AsyncResult<ProtoMessage> async_result_base(
    131         std::unique_ptr<ProtoMessage>(async_result.release_msg()),
    132         async_result.has_more(), async_result.fd());
    133     DeferredBase::Resolve(std::move(async_result_base));
    134   }
    135 };
    136 
    137 }  // namespace ipc
    138 }  // namespace perfetto
    139 
    140 #endif  // INCLUDE_PERFETTO_IPC_DEFERRED_H_
    141