• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_EXT_IPC_DEFERRED_H_
18 #define INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_
19 
20 #include <functional>
21 #include <memory>
22 #include <utility>
23 
24 #include "perfetto/ext/ipc/async_result.h"
25 #include "perfetto/ext/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>
DeferredBase(Deferred<T> other)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>  // T : ProtoMessage subclass
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.
Deferred(DeferredBase && other)103   explicit Deferred(DeferredBase&& other) {
104     callback_ = std::move(other.callback_);
105     other.callback_ = nullptr;
106   }
107 
Bind(std::function<void (AsyncResult<T>)> callback)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.
Resolve(AsyncResult<T> async_result)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_EXT_IPC_DEFERRED_H_
141