1 // 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 <ostream> 21 #include <type_traits> 22 #include <utility> 23 24 #include <base/bind.h> 25 #include <brillo/message_loops/message_loop.h> 26 27 namespace 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. 31 class ScopedTaskId { 32 using MessageLoop = brillo::MessageLoop; 33 34 public: 35 // Move only type similar to unique_ptr. 36 ScopedTaskId(const ScopedTaskId&) = delete; 37 ScopedTaskId& operator=(const ScopedTaskId&) = delete; 38 39 friend std::ostream& operator<<(std::ostream& out, const ScopedTaskId& task) { 40 out << task.task_id_; 41 return out; 42 } 43 44 constexpr ScopedTaskId() = default; 45 ScopedTaskId(ScopedTaskId && other)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 ~ScopedTaskId()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 Cancel()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 IsScheduled()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 98 template <typename Callable> PostTask(const base::Location & from_here,Callable && callback,base::TimeDelta delay)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 } 115 116 private: 117 template <typename Callable> ExecuteTask(Callable && callback)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(); 122 } else if constexpr (std::is_same_v<Callable&&, 123 base::RepeatingCallback<void()>&&>) { 124 std::move(callback).Run(); 125 } else { 126 std::move(callback)(); 127 } 128 } 129 MessageLoop::TaskId task_id_{MessageLoop::kTaskIdNull}; 130 }; 131 } // namespace chromeos_update_engine 132 133 #endif 134