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_
|