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