blob: aa4cbbdb5efa1c0d7c38005e8dacb3896754cafd [file] [log] [blame]
/*
* 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_