File: cached_callback.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (134 lines) | stat: -rw-r--r-- 4,678 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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 CHROME_BROWSER_ASH_GUEST_OS_INFRA_CACHED_CALLBACK_H_
#define CHROME_BROWSER_ASH_GUEST_OS_INFRA_CACHED_CALLBACK_H_

#include <memory>
#include <type_traits>

#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"

namespace guest_os {

// Manages several racing callbacks which all attempt to access the same
// (logical) object.
//
// This class is used when you have multiple potential callers for an
// asynchronous operation, and you want them all to agree on the outcome of that
// operation. The first callback that comes in triggers the "real" async
// operation, and subsequent callbacks are queued. If the "real" operation
// succeeds, all currently queued and future callbacks are invoked with a handle
// to that success. If it fails, all currently queued callbacks are notified of
// the failure but future ones will retry the "real" async operation.
//
// Internally this class tries to create the "real" result T as a unique_ptr,
// but exposes it to its own clients as a T*. For the errors E it is advisable
// to use something movable and copyable, preferably an enum.
template <typename T, typename E>
  requires(std::is_default_constructible_v<E>)
class CachedCallback {
 public:
  // As a convenience this class provides a default implementation of the
  // Reject() method, which is used when the cache is deleted while callbacks
  // are in-flight. In this case we default-construct an E for them.
  using Result = base::expected<T*, E>;
  using Callback = base::OnceCallback<void(Result result)>;

  virtual ~CachedCallback() {
    if (!queued_callbacks_.empty()) {
      Finish(base::unexpected(Reject()));
    }
  }

  // Request access to the cached result.
  void Get(Callback callback) {
    if (real_object_) {
      std::move(callback).Run(Result(real_object_.get()));
      return;
    }
    queued_callbacks_.push_back(std::move(callback));
    // If this is the first callback, spawn the real one. This must happen after
    // enqueueing the callback in case the factory returns synchronously.
    if (queued_callbacks_.size() == 1) {
      Build(base::BindOnce(&CachedCallback<T, E>::OnRealResultFound,
                           weak_factory_.GetWeakPtr()));
    }
  }

  // Returns the cached result if it exists, nullptr otherwise.
  T* MaybeGet() const {
    if (real_object_) {
      return real_object_.get();
    }
    return nullptr;
  }

  // Clears the stored real result (if one exists) and returns it (or null).
  std::unique_ptr<T> Invalidate() {
    std::unique_ptr<T> real = std::move(real_object_);
    real_object_.reset();
    return std::move(real);
  }

  void CacheForTesting(std::unique_ptr<T> real) {
    OnRealResultFound(RealResult(std::move(real)));
  }

 protected:
  using RealResult = base::expected<std::unique_ptr<T>, E>;

  using RealCallback = base::OnceCallback<void(RealResult)>;

  // Used to construct the "real" result, which will be owned by this class.
  virtual void Build(RealCallback callback) = 0;

  // In cases where the cache determines that a request can not be fulfilled,
  // this error is returned to callers automatically. For example, it is used if
  // the cache is destroyed while requests are in flight.
  //
  // Unless overridden, a default-constructed E is used.
  virtual E Reject() { return E{}; }

  // Helper template to construct successful RealResults more conveniently. It
  // is static so that lambdas defined by the subclass can use them.
  template <typename... TT>
  static RealResult Success(TT&&... tt) {
    return base::ok(std::make_unique<T>(std::forward<TT>(tt)...));
  }

  // Similar to Success, but for error results.
  template <typename... EE>
  static RealResult Failure(EE&&... ee) {
    return base::unexpected(std::forward<EE>(ee)...);
  }

 private:
  void OnRealResultFound(RealResult real_result) {
    if (!real_result.has_value()) {
      Finish(base::unexpected(real_result.error()));
      return;
    }
    real_object_ = std::move(real_result.value());
    Finish(base::ok(real_object_.get()));
  }

  void Finish(Result res) {
    while (!queued_callbacks_.empty()) {
      std::move(queued_callbacks_.back()).Run(res);
      queued_callbacks_.pop_back();
    }
  }

  std::unique_ptr<T> real_object_;
  std::vector<Callback> queued_callbacks_;
  base::WeakPtrFactory<CachedCallback<T, E>> weak_factory_{this};
};

}  // namespace guest_os

#endif  // CHROME_BROWSER_ASH_GUEST_OS_INFRA_CACHED_CALLBACK_H_