| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_ |
| #define INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_ |
| |
| #include <functional> |
| #include <memory> |
| #include <utility> |
| |
| #include "perfetto/ext/ipc/async_result.h" |
| #include "perfetto/ext/ipc/basic_types.h" |
| |
| namespace perfetto { |
| namespace ipc { |
| |
| // This class is a wrapper for a callback handling async results. |
| // The problem this is solving is the following: For each result argument of the |
| // methods generated from the .proto file: |
| // - The client wants to see something on which it can Bind() a callback, which |
| // is invoked asynchronously once reply is received from the host. |
| // - The host wants to expose something to user code that implements the IPC |
| // methods to allow them to provide an asynchronous reply back to the client. |
| // Eventually even more than once, for the case streaming replies. |
| // |
| // In both cases we want to make sure that callbacks don't get lost along the |
| // way. To address this, this class will automatically reject the callbacks |
| // if they are not resolved at destructor time (or the object is std::move()'d). |
| // |
| // The client is supposed to use this class as follows: |
| // class GreeterProxy { |
| // void SayHello(const HelloRequest&, Deferred<HelloReply> reply) |
| // } |
| // ... |
| // Deferred<HelloReply> reply; |
| // reply.Bind([] (AsyncResult<HelloReply> reply) { |
| // std::cout << reply.success() ? reply->message : "failure"; |
| // }); |
| // host_proxy_instance.SayHello(req, std::move(reply)); |
| // |
| // The host instead is supposed to use this as follows: |
| // class GreeterImpl : public Greeter { |
| // void SayHello(const HelloRequest& req, Deferred<HelloReply> reply) { |
| // AsyncResult<HelloReply> reply = AsyncResult<HelloReply>::Create(); |
| // reply->set_greeting("Hello " + req.name) |
| // reply.Resolve(std::move(reply)); |
| // } |
| // } |
| // Or for more complex cases, the deferred object can be std::move()'d outside |
| // and the reply can continue asynchronously later. |
| |
| template <typename T> |
| class Deferred; |
| |
| class DeferredBase { |
| public: |
| explicit DeferredBase( |
| std::function<void(AsyncResult<ProtoMessage>)> callback = nullptr); |
| |
| template <typename T> |
| explicit DeferredBase(Deferred<T> other) |
| : callback_(std::move(other.callback_)) {} |
| |
| ~DeferredBase(); |
| DeferredBase(DeferredBase&&) noexcept; |
| DeferredBase& operator=(DeferredBase&&); |
| void Bind(std::function<void(AsyncResult<ProtoMessage>)> callback); |
| bool IsBound() const; |
| void Resolve(AsyncResult<ProtoMessage>); |
| void Reject(); |
| |
| protected: |
| template <typename T> |
| friend class Deferred; |
| void Move(DeferredBase&); |
| |
| std::function<void(AsyncResult<ProtoMessage>)> callback_; |
| }; |
| |
| template <typename T> // T : ProtoMessage subclass |
| class Deferred : public DeferredBase { |
| public: |
| explicit Deferred(std::function<void(AsyncResult<T>)> callback = nullptr) { |
| Bind(std::move(callback)); |
| } |
| |
| // This move constructor (and the similar one in DeferredBase) is meant to be |
| // called only by the autogenerated code. The caller has to guarantee that the |
| // moved-from and moved-to types match. The behavior is otherwise undefined. |
| explicit Deferred(DeferredBase&& other) { |
| callback_ = std::move(other.callback_); |
| other.callback_ = nullptr; |
| } |
| |
| void Bind(std::function<void(AsyncResult<T>)> callback) { |
| if (!callback) |
| return; |
| |
| // Here we need a callback adapter to downcast the callback to a generic |
| // callback that takes an AsyncResult<ProtoMessage>, so that it can be |
| // stored in the base class |callback_|. |
| auto callback_adapter = [callback]( |
| AsyncResult<ProtoMessage> async_result_base) { |
| // Upcast the async_result from <ProtoMessage> -> <T : ProtoMessage>. |
| static_assert(std::is_base_of<ProtoMessage, T>::value, "T:ProtoMessage"); |
| AsyncResult<T> async_result( |
| std::unique_ptr<T>(static_cast<T*>(async_result_base.release_msg())), |
| async_result_base.has_more(), async_result_base.fd()); |
| callback(std::move(async_result)); |
| }; |
| DeferredBase::Bind(callback_adapter); |
| } |
| |
| // If no more messages are expected, |callback_| is released. |
| void Resolve(AsyncResult<T> async_result) { |
| // Convert the |async_result| to the generic base one (T -> ProtoMessage). |
| AsyncResult<ProtoMessage> async_result_base( |
| std::unique_ptr<ProtoMessage>(async_result.release_msg()), |
| async_result.has_more(), async_result.fd()); |
| DeferredBase::Resolve(std::move(async_result_base)); |
| } |
| }; |
| |
| } // namespace ipc |
| } // namespace perfetto |
| |
| #endif // INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_ |