blob: 5c89935189123ad816d6049cc76d7917242da89e [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:
34 constexpr ScopedTaskId() = default;
35
36 constexpr ScopedTaskId(ScopedTaskId&& other) noexcept {
37 *this = std::move(other);
38 }
39
40 constexpr ScopedTaskId& operator=(ScopedTaskId&& other) noexcept {
41 std::swap(task_id_, other.task_id_);
42 return *this;
43 }
44
45 // Post a callback on current message loop, return true if succeeded, false if
46 // the previous callback hasn't run yet, or scheduling failed at MessageLoop
47 // side.
48 [[nodiscard]] bool PostTask(const base::Location& from_here,
49 base::OnceClosure&& callback,
50 base::TimeDelta delay = {}) noexcept {
51 return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
52 }
53 [[nodiscard]] bool PostTask(const base::Location& from_here,
54 std::function<void()>&& callback,
55 base::TimeDelta delay = {}) noexcept {
56 return PostTask<decltype(callback)>(from_here, std::move(callback), delay);
57 }
58
59 ~ScopedTaskId() noexcept { Cancel(); }
60
61 // Cancel the underlying managed task, true if cancel successful. False if no
62 // task scheduled or task cancellation failed
63 bool Cancel() noexcept {
64 if (task_id_ != MessageLoop::kTaskIdNull) {
65 if (MessageLoop::current()->CancelTask(task_id_)) {
66 LOG(INFO) << "Cancelled task id " << task_id_;
67 task_id_ = MessageLoop::kTaskIdNull;
68 return true;
69 }
70 }
71 return false;
72 }
73
74 [[nodiscard]] constexpr bool IsScheduled() const noexcept {
75 return task_id_ != MessageLoop::kTaskIdNull;
76 }
77
78 [[nodiscard]] constexpr bool operator==(const ScopedTaskId& other) const
79 noexcept {
80 return other.task_id_ == task_id_;
81 }
82
83 [[nodiscard]] constexpr bool operator<(const ScopedTaskId& other) const
84 noexcept {
85 return task_id_ < other.task_id_;
86 }
87
88 private:
89 ScopedTaskId(const ScopedTaskId&) = delete;
90 ScopedTaskId& operator=(const ScopedTaskId&) = delete;
91 template <typename Callable>
92 [[nodiscard]] bool PostTask(const base::Location& from_here,
93 Callable&& callback,
94 base::TimeDelta delay) noexcept {
95 if (task_id_ != MessageLoop::kTaskIdNull) {
96 LOG(ERROR) << "Scheduling another task but task id " << task_id_
97 << " isn't executed yet! This can cause the old task to leak.";
98 return false;
99 }
100 task_id_ = MessageLoop::current()->PostDelayedTask(
101 from_here,
102 base::BindOnce(&ScopedTaskId::ExecuteTask<decltype(callback)>,
103 base::Unretained(this),
104 std::move(callback)),
105 delay);
106 return task_id_ != MessageLoop::kTaskIdNull;
107 }
108 template <typename Callable>
109 void ExecuteTask(Callable&& callback) {
110 task_id_ = MessageLoop::kTaskIdNull;
111 if constexpr (std::is_same_v<Callable&&, base::OnceClosure&&>) {
112 std::move(callback).Run();
113 } else {
114 std::move(callback)();
115 }
116 }
117 MessageLoop::TaskId task_id_{MessageLoop::kTaskIdNull};
118};
119} // namespace chromeos_update_engine
120
121#endif