xref: /aosp_15_r20/external/cronet/base/barrier_callback.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_BARRIER_CALLBACK_H_
6 #define BASE_BARRIER_CALLBACK_H_
7 
8 #include <concepts>
9 #include <memory>
10 #include <type_traits>
11 #include <utility>
12 #include <vector>
13 
14 #include "base/check.h"
15 #include "base/check_op.h"
16 #include "base/functional/bind.h"
17 #include "base/functional/callback.h"
18 #include "base/functional/callback_helpers.h"
19 #include "base/synchronization/lock.h"
20 #include "base/template_util.h"
21 #include "base/thread_annotations.h"
22 
23 namespace base {
24 
25 namespace internal {
26 
27 template <typename T, typename DoneArg>
28 class BarrierCallbackInfo {
29  public:
BarrierCallbackInfo(size_t num_callbacks,OnceCallback<void (DoneArg)> done_callback)30   BarrierCallbackInfo(size_t num_callbacks,
31                       OnceCallback<void(DoneArg)> done_callback)
32       : num_callbacks_left_(num_callbacks),
33         done_callback_(std::move(done_callback)) {
34     results_.reserve(num_callbacks);
35   }
36 
Run(T t)37   void Run(T t) LOCKS_EXCLUDED(mutex_) {
38     base::ReleasableAutoLock lock(&mutex_);
39     DCHECK_NE(num_callbacks_left_, 0U);
40     results_.push_back(std::move(t));
41     --num_callbacks_left_;
42 
43     if (num_callbacks_left_ == 0) {
44       std::vector<std::remove_cvref_t<T>> results = std::move(results_);
45       lock.Release();
46       std::move(done_callback_).Run(std::move(results));
47     }
48   }
49 
50  private:
51   Lock mutex_;
52   size_t num_callbacks_left_ GUARDED_BY(mutex_);
53   std::vector<std::remove_cvref_t<T>> results_ GUARDED_BY(mutex_);
54   OnceCallback<void(DoneArg)> done_callback_;
55 };
56 
57 template <typename T>
ShouldNeverRun(T t)58 void ShouldNeverRun(T t) {
59   CHECK(false);
60 }
61 
62 }  // namespace internal
63 
64 // BarrierCallback<T> is an analog of BarrierClosure for which each `Run()`
65 // invocation takes a `T` as an argument. After `num_callbacks` such
66 // invocations, BarrierCallback invokes `Run()` on its `done_callback`, passing
67 // the vector of `T`s as an argument. (The ordering of the vector is
68 // unspecified.)
69 //
70 // `T`s that are movable are moved into the callback's storage; otherwise the T
71 // is copied. (BarrierCallback does not support `T`s that are neither movable
72 // nor copyable.) If T is a reference, the reference is removed, and the
73 // callback moves or copies the underlying value per the previously stated rule.
74 // Beware of creating dangling references. Types that contain references but are
75 // not references themselves are not modified for callback storage, e.g.
76 // `std::pair<int, const Foo&>`. Dangling references will be passed to
77 // `done_callback` if the referenced `Foo` objects have already been deleted.
78 //
79 // If `num_callbacks` is 0, `done_callback` is executed immediately.
80 //
81 // `done_callback` may accept a `std::vector<T>`, `const std::vector<T>`, or
82 // `const std::vector<T>&`.
83 //
84 // BarrierCallback is thread-safe - the internals are protected by a
85 // `base::Lock`. `done_callback` will be run on the thread that calls the final
86 // Run() on the returned callbacks, or the thread that constructed the
87 // BarrierCallback (in the case where `num_callbacks` is 0).
88 //
89 // BarrierCallback is copyable. Copies share state.
90 //
91 // `done_callback` is also cleared on the thread that runs it (by virtue of
92 // being a OnceCallback).
93 //
94 // See also
95 // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md
96 template <typename T,
97           typename RawArg = std::remove_cvref_t<T>,
98           typename DoneArg = std::vector<RawArg>,
99           template <typename>
100           class CallbackType>
requires(std::same_as<std::vector<RawArg>,std::remove_cvref_t<DoneArg>> && IsBaseCallback<CallbackType<void ()>>)101   requires(std::same_as<std::vector<RawArg>, std::remove_cvref_t<DoneArg>> &&
102            IsBaseCallback<CallbackType<void()>>)
103 RepeatingCallback<void(T)> BarrierCallback(
104     size_t num_callbacks,
105     CallbackType<void(DoneArg)> done_callback) {
106   if (num_callbacks == 0) {
107     std::move(done_callback).Run({});
108     return BindRepeating(&internal::ShouldNeverRun<T>);
109   }
110 
111   return BindRepeating(
112       &internal::BarrierCallbackInfo<T, DoneArg>::Run,
113       std::make_unique<internal::BarrierCallbackInfo<T, DoneArg>>(
114           num_callbacks, std::move(done_callback)));
115 }
116 
117 }  // namespace base
118 
119 #endif  // BASE_BARRIER_CALLBACK_H_
120