blob: 6df3f33a80413b0da38fa4ceb0f57d76ce694175 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVELOPER_FORENSICS_UTILS_FIDL_ONESHOT_PTR_H_
#define SRC_DEVELOPER_FORENSICS_UTILS_FIDL_ONESHOT_PTR_H_
#include <lib/async/dispatcher.h>
#include <lib/fidl/cpp/interface_ptr.h>
#include <lib/fit/function.h>
#include <lib/fpromise/promise.h>
#include <lib/sys/cpp/service_directory.h>
#include <lib/syslog/cpp/macros.h>
#include "src/developer/forensics/utils/errors.h"
#include "src/developer/forensics/utils/fit/bridge.h"
#include "src/developer/forensics/utils/fit/timeout.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace forensics {
namespace fidl {
// Wrapper around InterfacePtr<Interface> that provides 'one shot' to make a call to the interface.
//
// For example, if we wished to fetch a device's update channel from
// fuchsia::update::channel::Provider then we would use OneShotPtr as follows:
//
// ::fpromise::promise<std::string> GetChannel() {
// OneShotPtr<fuchsia::update::channel::Provider, std::string> channel_ptr(dispatcher,
// services);
//
// channel_ptr->GetCurrent([&](std::string channel) {
// if (channel_ptr.IsAlreadyDone()) {
// return;
// }
//
// channel_ptr.CompleteOk(channel);
// });
//
// return channel_ptr.WaitForDone()
// }
//
// Any additional attempts to dereference |channel_ptr| in GetChannel() would cause the program to
// check-fail.
template <typename Interface, typename V = void>
class OneShotPtr {
public:
OneShotPtr(async_dispatcher_t* dispatcher, std::shared_ptr<sys::ServiceDirectory> services,
std::string name = Interface::Name_)
: services_(services),
bridge_(dispatcher, fxl::StringPrintf("call on %s", name.c_str())),
name_(std::move(name)) {}
bool IsAlreadyDone() const { return bridge_.IsAlreadyDone(); }
template <typename VV = V, typename = std::enable_if_t<std::is_void_v<VV>>>
void CompleteOk() {
bridge_.CompleteOk();
}
template <typename VV = V, typename = std::enable_if_t<!std::is_void_v<VV>>>
void CompleteOk(VV value) {
bridge_.CompleteOk(std::move(value));
}
void CompleteError(Error error) { bridge_.CompleteError(error); }
::fpromise::promise<V, Error> WaitForDone() { return bridge_.WaitForDone(); }
::fpromise::promise<V, Error> WaitForDone(fit::Timeout timeout) {
return bridge_.WaitForDone(std::move(timeout));
}
Interface* operator->() {
// We use FXL_ and not FX_ so the messages goes to stderr and can be intercepted by
// ASSERT_DEATH.
FX_CHECK(!oneshot_used_) << fxl::StringPrintf(
"You've only got one shot to use -> on a OneShotPtr (%s)", name_.c_str());
oneshot_used_ = true;
connection_ = services_->Connect<Interface>(name_);
connection_.set_error_handler([this](zx_status_t status) {
if (bridge_.IsAlreadyDone()) {
return;
}
FX_PLOGS(WARNING, status) << "Lost connection to " << name_;
bridge_.CompleteError(Error::kConnectionError);
});
return connection_.get();
}
protected:
::fidl::InterfacePtr<Interface> connection_;
private:
const std::shared_ptr<sys::ServiceDirectory> services_;
fit::Bridge<V> bridge_;
std::string name_;
bool oneshot_used_ = false;
};
} // namespace fidl
} // namespace forensics
#endif // SRC_DEVELOPER_FORENSICS_UTILS_FIDL_ONESHOT_PTR_H_