blob: a90222f99597ac17f81a52ff6f6d05cc5bccd6c4 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 MISSIVE_UTIL_TASK_RUNNER_CONTEXT_H_
#define MISSIVE_UTIL_TASK_RUNNER_CONTEXT_H_
#include <utility>
#include <base/bind.h>
#include <base/callback.h>
#include <base/callback_helpers.h>
#include <base/check.h>
#include <base/logging.h>
#include <base/memory/scoped_refptr.h>
#include <base/sequence_checker.h>
#include <base/sequenced_task_runner.h>
#include <base/time/time.h>
namespace reporting {
// This class defines context for multiple actions executed on a sequenced task
// runner with the ability to make asynchronous calls to other threads and
// resuming sequenced execution by calling |Schedule| or |ScheduleAfter|.
// Multiple actions can be scheduled at once; they will be executed on the same
// sequenced task runner. Ends execution and self-destructs when one of the
// actions calls |Response| (all previously scheduled actions must be completed
// or cancelled by then, otherwise they will crash).
//
// Code snippet:
//
// Declaration:
// class SeriesOfActionsContext {
// public:
// SeriesOfActionsContext(
// ...,
// base::OnceCallback<void(...)> callback,
// scoped_refptr<base::SequencedTaskRunner> task_runner)
// : TaskRunnerContext<...>(std::move(callback),
// std::move(task_runner)) {}
//
// private:
// // Context can only be deleted by calling Response method.
// ~SeriesOfActionsContext() override = default;
//
// void Action1(...) {
// ...
// if (...) {
// Response(...);
// return;
// }
// Schedule(&SeriesOfActionsContext::Action2,
// base::Unretained(this),
// ...);
// ...
// ScheduleAfter(delay,
// &SeriesOfActionsContext::Action3,
// base::Unretained(this),
// ...);
// }
//
// void OnStart() override { Action1(...); }
// };
//
// Usage:
// Start<SeriesOfActionsContext>(
// ...,
// returning_callback,
// base::SequencedTaskRunnerHandle::Get());
//
template <typename ResponseType>
class TaskRunnerContext {
public:
TaskRunnerContext(const TaskRunnerContext& other) = delete;
TaskRunnerContext& operator=(const TaskRunnerContext& other) = delete;
// Schedules next execution (can be called from any thread).
template <class Function, class... Args>
void Schedule(Function&& proc, Args&&... args) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::forward<Function>(proc),
std::forward<Args>(args)...));
}
// Schedules next execution with delay (can be called from any thread).
template <class Function, class... Args>
void ScheduleAfter(base::TimeDelta delay, Function&& proc, Args&&... args) {
task_runner_->PostDelayedTask(FROM_HERE,
base::BindOnce(std::forward<Function>(proc),
std::forward<Args>(args)...),
delay);
}
// Responds to the caller once completed the work sequence
// (can only be called by action scheduled to the sequenced task runner).
void Response(ResponseType result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OnCompletion();
// Respond to the caller.
DCHECK(!callback_.is_null()) << "Already responded";
std::move(callback_).Run(std::forward<ResponseType>(result));
// Self-destruct.
delete this;
}
// Helper method checks that the caller runs on valid sequence.
// Can be used by any scheduled action.
// No need to call it by OnStart, OnCompletion and destructor.
// For non-debug builds it is a no-op.
void CheckOnValidSequence() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
protected:
// Constructor is protected, for derived class to refer to.
TaskRunnerContext(base::OnceCallback<void(ResponseType)> callback,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: callback_(std::move(callback)), task_runner_(std::move(task_runner)) {
// Constructor can be called from any thread.
DETACH_FROM_SEQUENCE(sequence_checker_);
}
// Context can only be deleted by calling Response method.
virtual ~TaskRunnerContext() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(callback_.is_null()) << "Deleted without responding to the caller";
}
private:
template <typename ContextType /* derived from TaskRunnerContext*/,
class... Args>
friend void Start(Args&&... args);
// Hook for execution start. Should be overridden to do non-trivial work.
virtual void OnStart() { Response(ResponseType()); }
// Finalization action before responding and deleting the context.
// May be overridden, if necessary.
virtual void OnCompletion() {}
// Wrapper for OnStart to mandate sequence checker.
void OnStartWrap() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
OnStart();
}
// User callback to deliver result.
base::OnceCallback<void(ResponseType)> callback_;
// Sequential task runner (guarantees that each action is executed
// sequentially in order of submission).
scoped_refptr<base::SequencedTaskRunner> task_runner_;
SEQUENCE_CHECKER(sequence_checker_);
};
// Constructs the context and starts execution on the assigned sequential task
// runner. Can be called from any thread to schedule the first action in the
// sequence.
template <typename ContextType /* derived from TaskRunnerContext*/,
class... Args>
void Start(Args&&... args) {
ContextType* const context = new ContextType(std::forward<Args>(args)...);
// Start execution handing |context| over to the callback, in order
// to make sure final |OnStart| (with possible |Response| and self-destruct)
// can only happen on |task_runner|.
context->task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ContextType::OnStartWrap, base::Unretained(context)));
}
} // namespace reporting
#endif // MISSIVE_UTIL_TASK_RUNNER_CONTEXT_H_