You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
4.4 KiB
108 lines
4.4 KiB
// 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_
|