chromium/media/base/callback_timeout_helpers.h

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_BASE_CALLBACK_TIMEOUT_HELPERS_H_
#define MEDIA_BASE_CALLBACK_TIMEOUT_HELPERS_H_

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"

// This file provides callback wrappers to help handle the case where a
// base::OnceCallback has not run before a given timeout. This can help identify
// programming errors (e.g. a callback was saved without ever running), as well
// as performance issues (e.g. slow operation).
//
// The timeout timer starts when the callback is wrapped. If the wrapped
// callback runs before the timeout, the original callback will run exactly the
// same as if without wrapping. Otherwise:
// - `WrapCallbackWithTimeoutHandler`: On timeout, the `timeout_callback` runs.
// WARNING: The original callback is not affected: it will still run if the
// wrapped callback runs after the timeout. Do NOT handle the result in both
// the original callback and the `timeout_callback`.
// - `WrapCallbackWithDefaultInvokeIfTimeout`: On timeout, the original callback
// runs with the default arguments passed when wrapping the callback. It will be
// a no-op if the wrapped callback runs after the timeout.
//
// Example:
//   // If `OnResult()` doesn't run after 10 seconds, it'll be run with "false".
//   foo->DoWorkAndReturnResult(
//       WrapCallbackWithDefaultInvokeIfTimeout(
//           base::BindOnce(&Foo::OnResult, this), base::Seconds(10), false));
//
//   // If `OnResult()` doesn't run after 10 seconds, LogError() will run.
//   // `OnResult()` may still run after that.
//   foo->DoWorkAndReturnResult(
//       WrapCallbackWithTimeoutHandler(
//           base::BindOnce(&Foo::OnResult, this),
//           base::BindOnce(&Foo::LogError, this, TIMEOUT)));

namespace media {

// Enum class for reporting callback timeout status to UMA.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class CallbackTimeoutStatus {};

// Callback time for the timeout handler in `WrapCallbackWithTimeoutHandler`.
// If `called_on_destruction` is true, the timeout callback was called because
// the original callback was destructed without running. Otherwise, it was
// called because the original callback timed out.
TimeoutCallback;

namespace internal {

// First, tell the compiler CallbackWithTimeoutHelper is a class template with
// one type parameter. Then define specializations where the type is a function
// returning void and taking zero or more arguments.
template <typename Signature>
class CallbackWithTimeoutHelper;

// Only support callbacks that return void because otherwise it is odd to call
// the callback in the destructor and drop the return value immediately.
CallbackWithTimeoutHelper<void (Args...)>;

}  // namespace internal

template <typename T, typename... Args>
inline base::OnceCallback<T> WrapCallbackWithTimeoutHandler(
    base::OnceCallback<T> callback,
    base::TimeDelta timeout_delay,
    TimeoutCallback timeout_callback) {}

template <typename T, typename... Args>
inline base::OnceCallback<T> WrapCallbackWithDefaultInvokeIfTimeout(
    base::OnceCallback<T> callback,
    base::TimeDelta timeout_delay,
    Args&&... args) {}

}  // namespace media

#endif  // MEDIA_BASE_CALLBACK_TIMEOUT_HELPERS_H_