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