blob: 91a2986051b495957c818bfb726bf4c1ffcd3175 [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
20#include <type_traits>
21#include <utility>
22
23#include <base/bind.h>
24#include <brillo/message_loops/message_loop.h>
25
26namespace chromeos_update_engine {
27
28// This class provides unique_ptr like semantic for |MessageLoop::TaskId|, when
29// instance of this class goes out of scope, underlying task will be cancelled.
30class ScopedTaskId {
31 using MessageLoop = brillo::MessageLoop;
32
33 public:
Kelvin Zhang1d99ae12021-05-12 13:29:27 -040034 // Move only type similar to unique_ptr.
35 ScopedTaskId(const ScopedTaskId&) = delete;
36 ScopedTaskId& operator=(const ScopedTaskId&) = delete;
37
Kelvin Zhang8704c832021-05-10 17:53:14 -040038 constexpr ScopedTaskId() = default;
39
40 constexpr ScopedTaskId(ScopedTaskId&& other) noexcept {
41 *this = std::move(other);
42 }
43
44 constexpr ScopedTaskId& operator=(ScopedTaskId&& other) noexcept {
45 std::swap(task_id_, other.task_id_);
46 return *this;
47 }
48
49 // Post a callback on current message loop, return true if succeeded, false if
50 // the previous callback hasn't run yet, or scheduling failed at MessageLoop
51 // side.
52 [[nodiscard]] bool PostTask(const base::Location& from_here,
53 base::OnceClosure&& callback,
54 base::TimeDelta delay = {}) noexcept {
55 return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
56 }
57 [[nodiscard]] bool PostTask(const base::Location& from_here,
58 std::function<void()>&& callback,
59 base::TimeDelta delay = {}) noexcept {
60 return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
61 }
62
63 ~ScopedTaskId() noexcept { Cancel(); }
64
65 // Cancel the underlying managed task, true if cancel successful. False if no
66 // task scheduled or task cancellation failed
67 bool Cancel() noexcept {
68 if (task_id_ != MessageLoop::kTaskIdNull) {
69 if (MessageLoop::current()->CancelTask(task_id_)) {
70 LOG(INFO) << "Cancelled task id " << task_id_;
71 task_id_ = MessageLoop::kTaskIdNull;
72 return true;
73 }
74 }
75 return false;
76 }
77
78 [[nodiscard]] constexpr bool IsScheduled() const noexcept {
79 return task_id_ != MessageLoop::kTaskIdNull;
80 }
81
82 [[nodiscard]] constexpr bool operator==(const ScopedTaskId& other) const
83 noexcept {
84 return other.task_id_ == task_id_;
85 }
86
87 [[nodiscard]] constexpr bool operator<(const ScopedTaskId& other) const
88 noexcept {
89 return task_id_ < other.task_id_;
90 }
91
92 private:
Kelvin Zhang8704c832021-05-10 17:53:14 -040093 template <typename Callable>
94 [[nodiscard]] bool PostTask(const base::Location& from_here,
95 Callable&& callback,
96 base::TimeDelta delay) noexcept {
97 if (task_id_ != MessageLoop::kTaskIdNull) {
98 LOG(ERROR) << "Scheduling another task but task id " << task_id_
99 << " isn't executed yet! This can cause the old task to leak.";
100 return false;
101 }
102 task_id_ = MessageLoop::current()->PostDelayedTask(
103 from_here,
104 base::BindOnce(&ScopedTaskId::ExecuteTask<decltype(callback)>,
105 base::Unretained(this),
106 std::move(callback)),
107 delay);
108 return task_id_ != MessageLoop::kTaskIdNull;
109 }
110 template <typename Callable>
111 void ExecuteTask(Callable&& callback) {
112 task_id_ = MessageLoop::kTaskIdNull;
113 if constexpr (std::is_same_v<Callable&&, base::OnceClosure&&>) {
114 std::move(callback).Run();
115 } else {
116 std::move(callback)();
117 }
118 }
119 MessageLoop::TaskId task_id_{MessageLoop::kTaskIdNull};
120};
121} // namespace chromeos_update_engine
122
123#endif