diff options
Diffstat (limited to 'include/perfetto/ipc/deferred.h')
-rw-r--r-- | include/perfetto/ipc/deferred.h | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/include/perfetto/ipc/deferred.h b/include/perfetto/ipc/deferred.h new file mode 100644 index 000000000..b1740b0e0 --- /dev/null +++ b/include/perfetto/ipc/deferred.h @@ -0,0 +1,140 @@ +/* + * 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_IPC_DEFERRED_H_ +#define INCLUDE_PERFETTO_IPC_DEFERRED_H_ + +#include <functional> +#include <memory> +#include <utility> + +#include "perfetto/ipc/async_result.h" +#include "perfetto/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 = ProtoMessage> +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_IPC_DEFERRED_H_ |