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 <type_traits> 21 #include <utility> 22 23 #include <base/bind.h> 24 #include <brillo/message_loops/message_loop.h> 25 26 namespace 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. 30 class ScopedTaskId { 31 using MessageLoop = brillo::MessageLoop; 32 33 public: 34 // Move only type similar to unique_ptr. 35 ScopedTaskId(const ScopedTaskId&) = delete; 36 ScopedTaskId& operator=(const ScopedTaskId&) = delete; 37 38 constexpr ScopedTaskId() = default; 39 ScopedTaskId(ScopedTaskId && other)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 ~ScopedTaskId()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 Cancel()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 IsScheduled()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: 93 template <typename Callable> PostTask(const base::Location & from_here,Callable && callback,base::TimeDelta delay)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> ExecuteTask(Callable && callback)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 124