xref: /aosp_15_r20/external/openscreen/util/alarm.h (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
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