blob: 8127d378a9df46ffeb12bf97cee0d53f9b5884ac [file] [log] [blame]
Kelvin Zhang8704c832021-05-10 17:53:14 -04001//
2// Copyright (C) 2021 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#ifndef UPDATE_ENGINE_SCOPED_TASK_ID_H_
18#define UPDATE_ENGINE_SCOPED_TASK_ID_H_
19
Kelvin Zhang8bcb2212023-11-16 10:45:29 -080020#include <ostream>
Kelvin Zhang8704c832021-05-10 17:53:14 -040021#include <type_traits>
22#include <utility>
23
24#include <base/bind.h>
25#include <brillo/message_loops/message_loop.h>
26
27namespace chromeos_update_engine {
28
29// This class provides unique_ptr like semantic for |MessageLoop::TaskId|, when
30// instance of this class goes out of scope, underlying task will be cancelled.
31class ScopedTaskId {
32 using MessageLoop = brillo::MessageLoop;
33
34 public:
Kelvin Zhang1d99ae12021-05-12 13:29:27 -040035 // Move only type similar to unique_ptr.
36 ScopedTaskId(const ScopedTaskId&) = delete;
37 ScopedTaskId& operator=(const ScopedTaskId&) = delete;
38
Kelvin Zhang8bcb2212023-11-16 10:45:29 -080039 friend std::ostream& operator<<(std::ostream& out, const ScopedTaskId& task) {
40 out << task.task_id_;
41 return out;
42 }
43
Kelvin Zhang8704c832021-05-10 17:53:14 -040044 constexpr ScopedTaskId() = default;
45
46 constexpr ScopedTaskId(ScopedTaskId&& other) noexcept {
47 *this = std::move(other);
48 }
49
50 constexpr ScopedTaskId& operator=(ScopedTaskId&& other) noexcept {
51 std::swap(task_id_, other.task_id_);
52 return *this;
53 }
54
55 // Post a callback on current message loop, return true if succeeded, false if
56 // the previous callback hasn't run yet, or scheduling failed at MessageLoop
57 // side.
58 [[nodiscard]] bool PostTask(const base::Location& from_here,
59 base::OnceClosure&& callback,
60 base::TimeDelta delay = {}) noexcept {
61 return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
62 }
63 [[nodiscard]] bool PostTask(const base::Location& from_here,
64 std::function<void()>&& callback,
65 base::TimeDelta delay = {}) noexcept {
66 return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
67 }
68
69 ~ScopedTaskId() noexcept { Cancel(); }
70
71 // Cancel the underlying managed task, true if cancel successful. False if no
72 // task scheduled or task cancellation failed
73 bool Cancel() noexcept {
74 if (task_id_ != MessageLoop::kTaskIdNull) {
75 if (MessageLoop::current()->CancelTask(task_id_)) {
76 LOG(INFO) << "Cancelled task id " << task_id_;
77 task_id_ = MessageLoop::kTaskIdNull;
78 return true;
79 }
80 }
81 return false;
82 }
83
84 [[nodiscard]] constexpr bool IsScheduled() const noexcept {
85 return task_id_ != MessageLoop::kTaskIdNull;
86 }
87
88 [[nodiscard]] constexpr bool operator==(const ScopedTaskId& other) const
89 noexcept {
90 return other.task_id_ == task_id_;
91 }
92
93 [[nodiscard]] constexpr bool operator<(const ScopedTaskId& other) const
94 noexcept {
95 return task_id_ < other.task_id_;
96 }
97
Kelvin Zhang8704c832021-05-10 17:53:14 -040098 template <typename Callable>
99 [[nodiscard]] bool PostTask(const base::Location& from_here,
100 Callable&& callback,
101 base::TimeDelta delay) noexcept {
102 if (task_id_ != MessageLoop::kTaskIdNull) {
103 LOG(ERROR) << "Scheduling another task but task id " << task_id_
104 << " isn't executed yet! This can cause the old task to leak.";
105 return false;
106 }
107 task_id_ = MessageLoop::current()->PostDelayedTask(
108 from_here,
109 base::BindOnce(&ScopedTaskId::ExecuteTask<decltype(callback)>,
110 base::Unretained(this),
111 std::move(callback)),
112 delay);
113 return task_id_ != MessageLoop::kTaskIdNull;
114 }
Kelvin Zhang8bcb2212023-11-16 10:45:29 -0800115
116 private:
Kelvin Zhang8704c832021-05-10 17:53:14 -0400117 template <typename Callable>
118 void ExecuteTask(Callable&& callback) {
119 task_id_ = MessageLoop::kTaskIdNull;
120 if constexpr (std::is_same_v<Callable&&, base::OnceClosure&&>) {
121 std::move(callback).Run();
Kelvin Zhang8bcb2212023-11-16 10:45:29 -0800122 } else if constexpr (std::is_same_v<Callable&&,
123 base::RepeatingCallback<void()>&&>) {
124 std::move(callback).Run();
Kelvin Zhang8704c832021-05-10 17:53:14 -0400125 } else {
126 std::move(callback)();
127 }
128 }
129 MessageLoop::TaskId task_id_{MessageLoop::kTaskIdNull};
130};
131} // namespace chromeos_update_engine
132
133#endif