blob: c8f51d116cc3bf866df7b89f4b4c4b8c839d10f1 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2021 The Chromium Authors
cfredrica0464ebf2021-07-24 01:26:392// 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
Daniel Cheng915d2552023-11-13 22:25:138#include <concepts>
Sumaid Syed22f60eeb2021-08-26 05:16:269#include <memory>
cfredrica0464ebf2021-07-24 01:26:3910#include <type_traits>
Sumaid Syed22f60eeb2021-08-26 05:16:2611#include <utility>
cfredrica0464ebf2021-07-24 01:26:3912#include <vector>
13
David Sanders8cfb63a2022-04-14 19:36:3014#include "base/check.h"
David Sandersf84c9f42022-04-15 13:42:3215#include "base/check_op.h"
Avi Drissman63e1f992023-01-13 18:54:4316#include "base/functional/bind.h"
17#include "base/functional/callback.h"
18#include "base/functional/callback_helpers.h"
Peter Boström54119652024-11-14 00:16:3819#include "base/notreached.h"
cfredrica0464ebf2021-07-24 01:26:3920#include "base/synchronization/lock.h"
21#include "base/thread_annotations.h"
22
23namespace base {
24
cfredric8c677a12021-08-03 21:48:3025namespace internal {
cfredrica0464ebf2021-07-24 01:26:3926
cfredricc8cdd1d2021-08-11 00:01:4827template <typename T, typename DoneArg>
cfredrica0464ebf2021-07-24 01:26:3928class BarrierCallbackInfo {
29 public:
30 BarrierCallbackInfo(size_t num_callbacks,
cfredricc8cdd1d2021-08-11 00:01:4831 OnceCallback<void(DoneArg)> done_callback)
cfredrica0464ebf2021-07-24 01:26:3932 : num_callbacks_left_(num_callbacks),
33 done_callback_(std::move(done_callback)) {
34 results_.reserve(num_callbacks);
35 }
36
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) {
punithnayak35a22f12023-12-11 20:24:4244 std::vector<std::remove_cvref_t<T>> results = std::move(results_);
cfredrica0464ebf2021-07-24 01:26:3945 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_);
punithnayak35a22f12023-12-11 20:24:4253 std::vector<std::remove_cvref_t<T>> results_ GUARDED_BY(mutex_);
cfredricc8cdd1d2021-08-11 00:01:4854 OnceCallback<void(DoneArg)> done_callback_;
cfredrica0464ebf2021-07-24 01:26:3955};
56
cfredriceaafc6b2021-07-27 21:12:0157template <typename T>
58void ShouldNeverRun(T t) {
Peter Boström54119652024-11-14 00:16:3859 NOTREACHED();
cfredriceaafc6b2021-07-27 21:12:0160}
61
cfredric8c677a12021-08-03 21:48:3062} // namespace internal
cfredrica0464ebf2021-07-24 01:26:3963
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//
cfredricc8cdd1d2021-08-11 00:01:4870// `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.
Dan McArdlea4afa602021-11-15 21:04:0274// 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.
cfredricc8cdd1d2021-08-11 00:01:4878//
cfredriceaafc6b2021-07-27 21:12:0179// If `num_callbacks` is 0, `done_callback` is executed immediately.
cfredrica0464ebf2021-07-24 01:26:3980//
cfredricc8cdd1d2021-08-11 00:01:4881// `done_callback` may accept a `std::vector<T>`, `const std::vector<T>`, or
82// `const std::vector<T>&`.
83//
cfredrica0464ebf2021-07-24 01:26:3984// 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//
Roland Bock6269edb2022-01-04 19:34:1589// BarrierCallback is copyable. Copies share state.
90//
cfredrica0464ebf2021-07-24 01:26:3991// `done_callback` is also cleared on the thread that runs it (by virtue of
92// being a OnceCallback).
Roland Bock6269edb2022-01-04 19:34:1593//
94// See also
95// https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md
Daniel Cheng915d2552023-11-13 22:25:1396template <typename T,
punithnayak35a22f12023-12-11 20:24:4297 typename RawArg = std::remove_cvref_t<T>,
Daniel Cheng915d2552023-11-13 22:25:1398 typename DoneArg = std::vector<RawArg>,
99 template <typename>
100 class CallbackType>
101 requires(std::same_as<std::vector<RawArg>, std::remove_cvref_t<DoneArg>> &&
102 IsBaseCallback<CallbackType<void()>>)
cfredrica0464ebf2021-07-24 01:26:39103RepeatingCallback<void(T)> BarrierCallback(
104 size_t num_callbacks,
cfredricc8cdd1d2021-08-11 00:01:48105 CallbackType<void(DoneArg)> done_callback) {
cfredriceaafc6b2021-07-27 21:12:01106 if (num_callbacks == 0) {
107 std::move(done_callback).Run({});
cfredric8c677a12021-08-03 21:48:30108 return BindRepeating(&internal::ShouldNeverRun<T>);
cfredriceaafc6b2021-07-27 21:12:01109 }
cfredrica0464ebf2021-07-24 01:26:39110
cfredricc8cdd1d2021-08-11 00:01:48111 return BindRepeating(
112 &internal::BarrierCallbackInfo<T, DoneArg>::Run,
113 std::make_unique<internal::BarrierCallbackInfo<T, DoneArg>>(
114 num_callbacks, std::move(done_callback)));
cfredrica0464ebf2021-07-24 01:26:39115}
116
117} // namespace base
118
119#endif // BASE_BARRIER_CALLBACK_H_