1 // Copyright 2019 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef UTIL_ALARM_H_ 6 #define UTIL_ALARM_H_ 7 8 #include <utility> 9 10 #include "platform/api/task_runner.h" 11 #include "platform/api/time.h" 12 13 namespace openscreen { 14 15 // A simple mechanism for running one Task in the future, but also allow for 16 // canceling the Task before it runs and/or re-scheduling a replacement Task to 17 // run at a different time. This mechanism is also scoped to its lifetime: if an 18 // Alarm is destroyed while it is scheduled, the Task is automatically canceled. 19 // It is safe for the client's Task to make re-entrant calls into all Alarm 20 // methods. 21 // 22 // Example use case: When using a TaskRunner, an object can safely schedule a 23 // callback into one of its instance methods (without the possibility of the 24 // Task executing after the object is destroyed). 25 // 26 // Design: In order to support efficient, arbitrary canceling and re-scheduling 27 // by the client, the Alarm posts a cancelable functor to the TaskRunner which, 28 // when invoked, then checks to see whether the Alarm instance still exists and, 29 // if so, calls its TryInvoke() method. The TryInvoke() method then determines: 30 // a) whether the invocation time of the client's Task has changed; and b) 31 // whether the Alarm was canceled in the meantime. From this, it either: a) does 32 // nothing; b) re-posts a new cancelable functor to the TaskRunner, to try 33 // running the client's Task later; or c) runs the client's Task. 34 class Alarm { 35 public: 36 Alarm(ClockNowFunctionPtr now_function, TaskRunner* task_runner); 37 ~Alarm(); 38 39 // The design requires that Alarm instances not be copied or moved. 40 Alarm(const Alarm&) = delete; 41 Alarm& operator=(const Alarm&) = delete; 42 Alarm(Alarm&&) = delete; 43 Alarm& operator=(Alarm&&) = delete; 44 45 // Schedule the |functor| to be invoked at |alarm_time|. If this Alarm was 46 // already scheduled, the prior scheduling is canceled. The Functor can be any 47 // callable target (e.g., function, lambda-expression, std::bind result, 48 // etc.). If |alarm_time| is on or before "now," such as kImmediately, it is 49 // scheduled to run as soon as possible. 50 template <typename Functor> Schedule(Functor functor,Clock::time_point alarm_time)51 inline void Schedule(Functor functor, Clock::time_point alarm_time) { 52 ScheduleWithTask(TaskRunner::Task(std::move(functor)), alarm_time); 53 } 54 55 // Same as Schedule(), but invoke the functor at the given |delay| after right 56 // now. 57 template <typename Functor> ScheduleFromNow(Functor functor,Clock::duration delay)58 inline void ScheduleFromNow(Functor functor, Clock::duration delay) { 59 ScheduleWithTask(TaskRunner::Task(std::move(functor)), 60 now_function_() + delay); 61 } 62 63 // Cancels an already-scheduled task from running, or no-op. 64 void Cancel(); 65 66 // See comments for Schedule(). Generally, callers will want to call 67 // Schedule() instead of this, for more-convenient caller-side syntax, unless 68 // they already have a Task to pass-in. 69 void ScheduleWithTask(TaskRunner::Task task, Clock::time_point alarm_time); 70 71 // A special time_point value representing "as soon as possible." 72 static constexpr Clock::time_point kImmediately = Clock::time_point::min(); 73 74 private: 75 // A move-only functor that holds a raw pointer back to |this| and can be 76 // canceled before its call operator is invoked. When canceled, its call 77 // operator becomes a no-op. 78 class CancelableFunctor; 79 80 // Posts a delayed call to TryInvoke() to the TaskRunner. 81 void InvokeLater(Clock::time_point now, Clock::time_point fire_time); 82 83 // Examines whether to invoke the client's Task now; or try again later; or 84 // just do nothing. See class-level design comments. 85 void TryInvoke(); 86 87 const ClockNowFunctionPtr now_function_; 88 TaskRunner* const task_runner_; 89 90 // This is the task the client wants to have run at a specific point-in-time. 91 // This is NOT the task that Alarm provides to the TaskRunner. 92 TaskRunner::Task scheduled_task_; 93 Clock::time_point alarm_time_{}; 94 95 // When non-null, there is a task in the TaskRunner's queue that will call 96 // TryInvoke() some time in the future. This member is exclusively maintained 97 // by the CancelableFunctor class methods. 98 CancelableFunctor* queued_fire_ = nullptr; 99 100 // When the CancelableFunctor is scheduled to run. It may possibly execute 101 // later than this, if the TaskRunner is falling behind. 102 Clock::time_point next_fire_time_{}; 103 }; 104 105 } // namespace openscreen 106 107 #endif // UTIL_ALARM_H_ 108