blob: b1740b0e0e023ab52bb89697953dbe4841d167a8 [file] [log] [blame]
Primiano Tucci4f9b6d72017-12-05 20:59:16 +00001/*
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
27namespace perfetto {
28namespace 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
65template <typename T>
66class Deferred;
67
68class 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
93template <typename T = ProtoMessage>
94class 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) {
Primiano Tucci5b65c9f2018-01-29 22:58:25 +0000109 if (!callback)
110 return;
111
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000112 // 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) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000117 // 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_