blob: 8748219a78f8918b20fdf20d1b17b33a2aa86f90 [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_COROUTINE_COROUTINE_MANAGER_H_
#define PERIDOT_BIN_LEDGER_COROUTINE_COROUTINE_MANAGER_H_
#include <algorithm>
#include <list>
#include <lib/fit/function.h>
#include "peridot/bin/ledger/coroutine/coroutine.h"
namespace coroutine {
// CoroutineManager manages the lifetime of coroutines.
class CoroutineManager {
public:
explicit CoroutineManager(CoroutineService* service) : service_(service) {}
~CoroutineManager() {
// Interrupt any active handlers.
while (!handlers_.empty()) {
(*handlers_.begin())->Resume(coroutine::ContinuationStatus::INTERRUPTED);
}
}
CoroutineManager(const CoroutineManager&) = delete;
const CoroutineManager& operator=(const CoroutineManager&) = delete;
// Starts a managed coroutine. This coroutine will be automatically
// interrupted if this |CoroutineManager| object is destroyed.
//
// |callback| must be a callable object
// |runnable| must be a callable object with the following signature:
// void(CoroutineHandler*, fit::function<void(Args...)>)
// When the second argument of |runnable| is called, the coroutine is
// unregistered from the manager object and |callback| is called with the same
// arguments. It is an error to exit the coroutine without calling
// |runnable|'s callback.
template <typename Callback, typename Runnable>
void StartCoroutine(Callback callback, Runnable runnable) {
service_->StartCoroutine(
[this, callback = std::move(callback),
runnable = std::move(runnable)](CoroutineHandler* handler) mutable {
auto iter = handlers_.insert(handlers_.cend(), handler);
auto final_callback = [this, iter,
callback = std::move(callback)](auto... args) {
// Remove the handler before calling the final callback. Otherwise
// the handler might be unnecessarily interrupted, if this object
// destructor is called in the callback.
handlers_.erase(iter);
callback(std::move(args)...);
};
runnable(handler, std::move(final_callback));
// Verify that the handler is correctly unregistered. It would be a
// bug otherwise.
FXL_DCHECK(std::find(handlers_.begin(), handlers_.end(), handler) ==
handlers_.end());
});
}
// Starts a managed coroutine. This coroutine will be automatically
// interrupted if this |CoroutineManager| object is destroyed.
//
// |runnable| must be a callable object with the following signature:
// void(CoroutineHandler*)
template <typename Runnable>
void StartCoroutine(Runnable runnable) {
service_->StartCoroutine([this, runnable = std::move(runnable)](
CoroutineHandler* handler) mutable {
auto iter = handlers_.insert(handlers_.cend(), handler);
runnable(handler);
handlers_.erase(iter);
});
}
private:
std::list<coroutine::CoroutineHandler*> handlers_;
CoroutineService* const service_;
};
} // namespace coroutine
#endif // PERIDOT_BIN_LEDGER_COROUTINE_COROUTINE_MANAGER_H_