blob: e694d83e10033a8b71ea0d608b7bbaa8b143fb09 [file] [log] [blame]
// 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_