| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UTIL_ALARM_H_ |
| #define UTIL_ALARM_H_ |
| |
| #include <utility> |
| |
| #include "platform/api/task_runner.h" |
| #include "platform/api/time.h" |
| |
| namespace openscreen { |
| |
| // A simple mechanism for running one Task in the future, but also allow for |
| // canceling the Task before it runs and/or re-scheduling a replacement Task to |
| // run at a different time. This mechanism is also scoped to its lifetime: if an |
| // Alarm is destroyed while it is scheduled, the Task is automatically canceled. |
| // It is safe for the client's Task to make re-entrant calls into all Alarm |
| // methods. |
| // |
| // Example use case: When using a TaskRunner, an object can safely schedule a |
| // callback into one of its instance methods (without the possibility of the |
| // Task executing after the object is destroyed). |
| // |
| // Design: In order to support efficient, arbitrary canceling and re-scheduling |
| // by the client, the Alarm posts a cancelable functor to the TaskRunner which, |
| // when invoked, then checks to see whether the Alarm instance still exists and, |
| // if so, calls its TryInvoke() method. The TryInvoke() method then determines: |
| // a) whether the invocation time of the client's Task has changed; and b) |
| // whether the Alarm was canceled in the meantime. From this, it either: a) does |
| // nothing; b) re-posts a new cancelable functor to the TaskRunner, to try |
| // running the client's Task later; or c) runs the client's Task. |
| class Alarm { |
| public: |
| Alarm(ClockNowFunctionPtr now_function, TaskRunner* task_runner); |
| ~Alarm(); |
| |
| // The design requires that Alarm instances not be copied or moved. |
| Alarm(const Alarm&) = delete; |
| Alarm& operator=(const Alarm&) = delete; |
| Alarm(Alarm&&) = delete; |
| Alarm& operator=(Alarm&&) = delete; |
| |
| // Schedule the |functor| to be invoked at |alarm_time|. If this Alarm was |
| // already scheduled, the prior scheduling is canceled. The Functor can be any |
| // callable target (e.g., function, lambda-expression, std::bind result, |
| // etc.). If |alarm_time| is on or before "now," such as kImmediately, it is |
| // scheduled to run as soon as possible. |
| template <typename Functor> |
| inline void Schedule(Functor functor, Clock::time_point alarm_time) { |
| ScheduleWithTask(TaskRunner::Task(std::move(functor)), alarm_time); |
| } |
| |
| // Same as Schedule(), but invoke the functor at the given |delay| after right |
| // now. |
| template <typename Functor> |
| inline void ScheduleFromNow(Functor functor, Clock::duration delay) { |
| ScheduleWithTask(TaskRunner::Task(std::move(functor)), |
| now_function_() + delay); |
| } |
| |
| // Cancels an already-scheduled task from running, or no-op. |
| void Cancel(); |
| |
| // See comments for Schedule(). Generally, callers will want to call |
| // Schedule() instead of this, for more-convenient caller-side syntax, unless |
| // they already have a Task to pass-in. |
| void ScheduleWithTask(TaskRunner::Task task, Clock::time_point alarm_time); |
| |
| // A special time_point value representing "as soon as possible." |
| static constexpr Clock::time_point kImmediately = Clock::time_point::min(); |
| |
| private: |
| // A move-only functor that holds a raw pointer back to |this| and can be |
| // canceled before its call operator is invoked. When canceled, its call |
| // operator becomes a no-op. |
| class CancelableFunctor; |
| |
| // Posts a delayed call to TryInvoke() to the TaskRunner. |
| void InvokeLater(Clock::time_point now, Clock::time_point fire_time); |
| |
| // Examines whether to invoke the client's Task now; or try again later; or |
| // just do nothing. See class-level design comments. |
| void TryInvoke(); |
| |
| const ClockNowFunctionPtr now_function_; |
| TaskRunner* const task_runner_; |
| |
| // This is the task the client wants to have run at a specific point-in-time. |
| // This is NOT the task that Alarm provides to the TaskRunner. |
| TaskRunner::Task scheduled_task_; |
| Clock::time_point alarm_time_{}; |
| |
| // When non-null, there is a task in the TaskRunner's queue that will call |
| // TryInvoke() some time in the future. This member is exclusively maintained |
| // by the CancelableFunctor class methods. |
| CancelableFunctor* queued_fire_ = nullptr; |
| |
| // When the CancelableFunctor is scheduled to run. It may possibly execute |
| // later than this, if the TaskRunner is falling behind. |
| Clock::time_point next_fire_time_{}; |
| }; |
| |
| } // namespace openscreen |
| |
| #endif // UTIL_ALARM_H_ |