|  | // Copyright 2023 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef COMPONENTS_UNEXPORTABLE_KEYS_BACKGROUND_TASK_IMPL_H_ | 
|  | #define COMPONENTS_UNEXPORTABLE_KEYS_BACKGROUND_TASK_IMPL_H_ | 
|  |  | 
|  | #include <optional> | 
|  |  | 
|  | #include "base/functional/callback.h" | 
|  | #include "base/location.h" | 
|  | #include "base/memory/scoped_refptr.h" | 
|  | #include "base/memory/weak_ptr.h" | 
|  | #include "base/task/sequenced_task_runner.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/timer/elapsed_timer.h" | 
|  | #include "components/unexportable_keys/background_task.h" | 
|  | #include "components/unexportable_keys/background_task_priority.h" | 
|  | #include "components/unexportable_keys/background_task_type.h" | 
|  |  | 
|  | namespace unexportable_keys::internal { | 
|  |  | 
|  | // A template class implementing `BackgroundTask`. Background task is | 
|  | // represented by a `task_` callback with a specific `ReturnType` that is passed | 
|  | // from the background thread to a `reply_` callback. | 
|  | template <typename T> | 
|  | class BackgroundTaskImpl : public BackgroundTask { | 
|  | public: | 
|  | using ReturnType = T; | 
|  |  | 
|  | // `task` is a callback that runs on the background thread and returns a | 
|  | // value. | 
|  | // `reply` is invoked on the posting thread with the return result of | 
|  | // `task` and the number or retries it took to compute this result. | 
|  | BackgroundTaskImpl(base::RepeatingCallback<ReturnType()> task, | 
|  | base::OnceCallback<void(ReturnType, size_t)> reply, | 
|  | BackgroundTaskPriority priority, | 
|  | BackgroundTaskType type, | 
|  | size_t max_retries) | 
|  | : task_(std::move(task)), | 
|  | reply_(std::move(reply)), | 
|  | priority_(priority), | 
|  | type_(type), | 
|  | max_retries_(max_retries) { | 
|  | DCHECK(task_); | 
|  | DCHECK(reply_); | 
|  | scheduled_timer_ = base::ElapsedTimer(); | 
|  | } | 
|  | ~BackgroundTaskImpl() override = default; | 
|  |  | 
|  | // BackgroundTask: | 
|  | void Run(scoped_refptr<base::SequencedTaskRunner> background_task_runner, | 
|  | base::OnceCallback<void(BackgroundTask* task)> on_complete_callback) | 
|  | override { | 
|  | CHECK(!result_.has_value()); | 
|  | run_timer_ = base::ElapsedTimer(); | 
|  | background_task_runner->PostTaskAndReplyWithResult( | 
|  | FROM_HERE, task_, | 
|  | base::BindOnce(&BackgroundTaskImpl::OnTaskComplete, | 
|  | weak_ptr_factory_.GetWeakPtr(), | 
|  | std::move(on_complete_callback))); | 
|  | } | 
|  |  | 
|  | void ReplyWithResult() override { | 
|  | CHECK(result_.has_value()); | 
|  | std::move(reply_).Run(std::move(result_).value(), retries_); | 
|  | } | 
|  |  | 
|  | void ResetStateBeforeRetry() override { | 
|  | result_.reset(); | 
|  | run_timer_.reset(); | 
|  | scheduled_timer_ = base::ElapsedTimer(); | 
|  | ++retries_; | 
|  | } | 
|  |  | 
|  | BackgroundTask::Status GetStatus() const override { | 
|  | if (run_timer_.has_value()) { | 
|  | // `run_timer_` is started just before posting the task. | 
|  | return BackgroundTask::Status::kPosted; | 
|  | } | 
|  |  | 
|  | return reply_.IsCancelled() ? BackgroundTask::Status::kCanceled | 
|  | : BackgroundTask::Status::kPending; | 
|  | } | 
|  |  | 
|  | BackgroundTaskPriority GetPriority() const override { return priority_; } | 
|  |  | 
|  | BackgroundTaskType GetType() const override { return type_; } | 
|  |  | 
|  | base::TimeDelta GetElapsedTimeSinceScheduled() const override { | 
|  | CHECK(scheduled_timer_.has_value()); | 
|  | return scheduled_timer_->Elapsed(); | 
|  | } | 
|  |  | 
|  | std::optional<base::TimeDelta> GetElapsedTimeSinceRun() const override { | 
|  | if (run_timer_.has_value()) { | 
|  | return run_timer_->Elapsed(); | 
|  | } | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | size_t GetRetryCount() const override { return retries_; } | 
|  |  | 
|  | bool ShouldRetry() const override { | 
|  | CHECK(result_.has_value()); | 
|  | return !reply_.IsCancelled() && retries_ < max_retries_ && | 
|  | ShouldRetryBasedOnResult(*result_); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Allows subclasses to specify whether the task should be retried based on | 
|  | // `result`. | 
|  | virtual bool ShouldRetryBasedOnResult(const ReturnType& result) const { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void OnTaskComplete( | 
|  | base::OnceCallback<void(BackgroundTask*)> on_complete_callback, | 
|  | ReturnType result) { | 
|  | result_ = std::move(result); | 
|  | std::move(on_complete_callback).Run(this); | 
|  | // `this` might be destroyed after running the callback. | 
|  | } | 
|  |  | 
|  | base::RepeatingCallback<ReturnType()> task_; | 
|  | base::OnceCallback<void(ReturnType, size_t)> reply_; | 
|  |  | 
|  | size_t retries_ = 0; | 
|  | std::optional<ReturnType> result_; | 
|  |  | 
|  | const BackgroundTaskPriority priority_; | 
|  | const BackgroundTaskType type_; | 
|  | const size_t max_retries_; | 
|  | std::optional<base::ElapsedTimer> scheduled_timer_; | 
|  | std::optional<base::ElapsedTimer> run_timer_; | 
|  |  | 
|  | base::WeakPtrFactory<BackgroundTaskImpl> weak_ptr_factory_{this}; | 
|  | }; | 
|  |  | 
|  | }  // namespace unexportable_keys::internal | 
|  |  | 
|  | #endif  // COMPONENTS_UNEXPORTABLE_KEYS_BACKGROUND_TASK_IMPL_H_ |