blob: ebbec5e3b5cf03f9574db308f57e50708e253964 [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_FIT_BRIDGE_H_
#define SRC_DEVELOPER_FORENSICS_UTILS_FIT_BRIDGE_H_
#include <lib/async/cpp/task.h>
#include <lib/async/dispatcher.h>
#include <lib/fit/function.h>
#include <lib/fpromise/bridge.h>
#include <lib/fpromise/result.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <type_traits>
#include "src/developer/forensics/utils/errors.h"
#include "src/developer/forensics/utils/fit/timeout.h"
namespace forensics {
namespace fit {
// Wrapper around ::fpromise::bridge with the ability to post a task that that will complete the
// bridge at a certain point in the future if the bridge hasn't already been completed.
template <typename V = void>
class Bridge {
public:
Bridge(async_dispatcher_t* dispatcher, const std::string& task_name)
: dispatcher_(dispatcher), task_name_(task_name) {}
template <typename VV = V, typename = std::enable_if_t<std::is_void_v<VV>>>
void CompleteOk() {
if (bridge_.completer) {
bridge_.completer.complete_ok();
}
}
template <typename VV = V, typename = std::enable_if_t<!std::is_void_v<VV>>>
void CompleteOk(VV value) {
if (bridge_.completer) {
bridge_.completer.complete_ok(std::move(value));
}
}
void CompleteError(Error error) {
if (bridge_.completer) {
bridge_.completer.complete_error(error);
}
}
bool IsAlreadyDone() const { return !bridge_.completer; }
// Get the promise that will be ungated when |bridge_| is completed.
::fpromise::promise<V, Error> WaitForDone() { return bridge_.consumer.promise(); }
// Start the timeout and get the promise that will be ungated when |bridge_| is completed.
::fpromise::promise<V, Error> WaitForDone(Timeout timeout) {
timeout_ = std::move(timeout);
if (zx_status_t status = timeout_task_.PostDelayed(dispatcher_, timeout_.value);
status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to post timeout task, aborting " << task_name_;
return ::fpromise::make_result_promise<V, Error>(
::fpromise::error(Error::kAsyncTaskPostFailure));
}
return WaitForDone();
}
private:
void AtTimeout() {
if (IsAlreadyDone()) {
return;
}
FX_LOGS(WARNING) << task_name_ << " timed out";
if (timeout_.action) {
(*timeout_.action)();
}
bridge_.completer.complete_error(Error::kTimeout);
}
async_dispatcher_t* dispatcher_;
const std::string task_name_;
::fpromise::bridge<V, Error> bridge_;
async::TaskClosureMethod<Bridge, &Bridge::AtTimeout> timeout_task_{this};
Timeout timeout_;
};
} // namespace fit
} // namespace forensics
#endif // SRC_DEVELOPER_FORENSICS_UTILS_FIT_BRIDGE_H_