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