blob: bcc795af769e72a4f655bc53e99cef7556eca7bc [file] [log] [blame]
// Copyright 2018 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 PERIDOT_BIN_LEDGER_FIDL_ERROR_NOTIFIER_ERROR_NOTIFIER_BINDING_H_
#define PERIDOT_BIN_LEDGER_FIDL_ERROR_NOTIFIER_ERROR_NOTIFIER_BINDING_H_
#include <map>
#include <utility>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/enum.h>
#include <lib/fit/function.h>
#include <lib/fxl/logging.h>
#include "peridot/bin/ledger/fidl/include/types.h"
#include "peridot/bin/ledger/sync_helper/sync_helper.h"
namespace ledger {
// Class for binding of FIDL interface implementing the ErrorNotifier interface
// and using the error notifier delegate interface |D|.
// For a FIDL interface Foo, |D| is an interface named FooErrorNotifierDelegate
// that needs to be implemented by the user and passed to ErrorNotifierBinding.
//
// This class internally handles the following features:
// - Implement the |Sync| method.
// - Provides a factory for passing a callback to the companion implementation
// that will handle reporting the error and closing the connection.
// - Provides a |WrapOperation| method that needs to be called on all callback
// before passing to the companion implementation so that |Sync| can keep
// track of what operations are currently in progress.
//
// This class exposes the following features:
// - Access to the methods of the underlying bindings.
// - Implement the |set_on_empty| method to be usable with AutoCleanableSet.
template <typename D>
class ErrorNotifierBinding {
public:
ErrorNotifierBinding(D* delegate) : impl_(delegate, this), binding_(&impl_) {
binding_.set_error_handler(
[this](zx_status_t /* status */) { CheckEmpty(); });
sync_helper_.set_on_empty([this] { CheckEmpty(); });
}
ErrorNotifierBinding(
D* delegate, fidl::InterfaceRequest<typename D::FidlInterface> request,
async_dispatcher_t* dispatcher = nullptr)
: ErrorNotifierBinding(delegate) {
binding_.Bind(std::move(request), dispatcher);
}
void set_on_empty(fit::closure on_empty) { on_empty_ = std::move(on_empty); }
bool empty() { return !binding_.is_bound() && sync_helper_.empty(); }
fidl::InterfaceRequest<typename D::FidlInterface> Unbind() {
return binding_.Unbind();
}
fidl::InterfaceHandle<typename D::FidlInterface> NewBinding(
async_dispatcher_t* dispatcher = nullptr) {
return binding_.NewBinding(dispatcher);
}
void Close(zx_status_t status) { binding_.Close(status); }
void Close(ledger::Status status) { Close(static_cast<zx_status_t>(status)); }
private:
friend typename D::Impl;
void Sync(fit::function<void()> callback) {
sync_helper_.RegisterSynchronizationCallback(std::move(callback));
}
// Wraps a callback in another one that preprends a Status arguments and
// handles the status in case of error.
template <typename... Args>
auto WrapOperation(const char* function_name,
fit::function<void(Args...)> callback) {
return sync_helper_.WrapOperation(
[this, function_name, callback = std::move(callback)](
::fuchsia::ledger::Status status, Args&&... args) {
if (status == ::fuchsia::ledger::Status::OK) {
callback(std::forward<Args>(args)...);
return;
}
FXL_LOG(INFO) << "FIDL call " << D::Impl::kInterfaceName
<< "::" << function_name
<< " failed with status: " << fidl::ToUnderlying(status)
<< ". Sending the epitaph and closing the connection.";
Close(status);
});
}
// Returns a new callback taking a ledger::Status. This callback will be
// responsible, in case of error, to send the status back as an event and
// close the connection to the client.
auto NewErrorCallback(const char* function_name) {
return WrapOperation(function_name, fit::closure([] {}));
}
void CheckEmpty() {
if (empty() && on_empty_) {
on_empty_();
}
}
typename D::Impl impl_;
fidl::Binding<typename D::FidlInterface> binding_;
fit::closure on_empty_;
SyncHelper sync_helper_;
};
} // namespace ledger
#endif // PERIDOT_BIN_LEDGER_FIDL_ERROR_NOTIFIER_ERROR_NOTIFIER_BINDING_H_