// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_RENDERER_CHROMEOS_DELAYED_CALLBACK_GROUP_H_
#define CHROME_RENDERER_CHROMEOS_DELAYED_CALLBACK_GROUP_H_
#include <functional>
#include <list>
#include <queue>
#include "base/cancelable_callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
namespace base {
class SequencedTaskRunner;
} // namespace base
// Manage a collection of callbacks to be run en mass when RunAll() is called.
// This class is thread-safe.
class DelayedCallbackGroup
: public base::RefCountedThreadSafe<DelayedCallbackGroup> {
public:
// The reason for the callback being invokes.
enum class RunReason {
// The callback is being run normally i.e. RunAll() was called.
NORMAL,
// The timeout period elapsed before RunAll() was invoked.
TIMEOUT
};
using Callback = base::OnceCallback<void(RunReason)>;
// All callbacks will be run when RunAll() is called or after the expiration
// delay specified by |expiration_delay|.
DelayedCallbackGroup(
base::TimeDelta expiration_delay,
scoped_refptr<base::SequencedTaskRunner> expiration_task_runner);
DelayedCallbackGroup(const DelayedCallbackGroup&) = delete;
DelayedCallbackGroup& operator=(const DelayedCallbackGroup&) = delete;
// Add a |callback| to the queue to be called at a later time on the calling
// sequence task runner. |callback| will either be called when RunAll() is
// called or if a delay of |expiration_delay| has elapsed after calling
// Add() without RunAll() being called first.
//
// Callbacks are called in the same order they were added.
void Add(Callback callback);
// Run all non-expired callback managed by this instance in the order in which
// they were added via Add(). All callbacks will be passed the
// RunReason::NORMAL parameter value.
void RunAll();
private:
friend class base::RefCountedThreadSafe<DelayedCallbackGroup>;
struct CallbackEntry {
CallbackEntry(
Callback callback,
const scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
base::TimeTicks expiration_time);
~CallbackEntry();
Callback callback_;
const scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
base::TimeTicks expiration_time_;
};
~DelayedCallbackGroup();
void StartExpirationTimer();
void CancelExpirationTimer();
void OnExpirationTimer();
// Call all expired callbacks with the RunReason::TIMEOUT parameter value.
void ProcessExpiredCallbacks(base::TimeTicks expiration_time);
// Call all remaining callbacks with the RunReason::TIMEOUT parameter value.
void ExpireAllCallbacks() EXCLUSIVE_LOCKS_REQUIRED(callbacks_lock_);
std::queue<CallbackEntry, std::list<CallbackEntry>> callbacks_
GUARDED_BY(callbacks_lock_);
base::TimeDelta expiration_delay_ GUARDED_BY(callbacks_lock_);
base::CancelableOnceClosure expiration_timeout_;
mutable base::Lock callbacks_lock_;
scoped_refptr<base::SequencedTaskRunner> expiration_task_runner_;
SEQUENCE_CHECKER(sequence_checker_);
};
#endif // CHROME_RENDERER_CHROMEOS_DELAYED_CALLBACK_GROUP_H_