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