// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_BARRIER_CALLBACK_H_ #define BASE_BARRIER_CALLBACK_H_ #include #include #include #include #include "base/bind.h" #include "base/callback.h" #include "base/callback_helpers.h" #include "base/check.h" #include "base/check_op.h" #include "base/synchronization/lock.h" #include "base/template_util.h" #include "base/thread_annotations.h" namespace base { namespace internal { template class BarrierCallbackInfo { public: BarrierCallbackInfo(size_t num_callbacks, OnceCallback done_callback) : num_callbacks_left_(num_callbacks), done_callback_(std::move(done_callback)) { results_.reserve(num_callbacks); } void Run(T t) LOCKS_EXCLUDED(mutex_) { base::ReleasableAutoLock lock(&mutex_); DCHECK_NE(num_callbacks_left_, 0U); results_.push_back(std::move(t)); --num_callbacks_left_; if (num_callbacks_left_ == 0) { std::vector> results = std::move(results_); lock.Release(); std::move(done_callback_).Run(std::move(results)); } } private: Lock mutex_; size_t num_callbacks_left_ GUARDED_BY(mutex_); std::vector> results_ GUARDED_BY(mutex_); OnceCallback done_callback_; }; template void ShouldNeverRun(T t) { CHECK(false); } } // namespace internal // BarrierCallback is an analog of BarrierClosure for which each `Run()` // invocation takes a `T` as an argument. After `num_callbacks` such // invocations, BarrierCallback invokes `Run()` on its `done_callback`, passing // the vector of `T`s as an argument. (The ordering of the vector is // unspecified.) // // `T`s that are movable are moved into the callback's storage; otherwise the T // is copied. (BarrierCallback does not support `T`s that are neither movable // nor copyable.) If T is a reference, the reference is removed, and the // callback moves or copies the underlying value per the previously stated rule. // Beware of creating dangling references. Types that contain references but are // not references themselves are not modified for callback storage, e.g. // `std::pair`. Dangling references will be passed to // `done_callback` if the referenced `Foo` objects have already been deleted. // // If `num_callbacks` is 0, `done_callback` is executed immediately. // // `done_callback` may accept a `std::vector`, `const std::vector`, or // `const std::vector&`. // // BarrierCallback is thread-safe - the internals are protected by a // `base::Lock`. `done_callback` will be run on the thread that calls the final // Run() on the returned callbacks, or the thread that constructed the // BarrierCallback (in the case where `num_callbacks` is 0). // // BarrierCallback is copyable. Copies share state. // // `done_callback` is also cleared on the thread that runs it (by virtue of // being a OnceCallback). // // See also // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md template , typename DoneArg = std::vector, template class CallbackType, typename std::enable_if, base::remove_cvref_t>::value>::type* = nullptr, typename = base::EnableIfIsBaseCallback> RepeatingCallback BarrierCallback( size_t num_callbacks, CallbackType done_callback) { if (num_callbacks == 0) { std::move(done_callback).Run({}); return BindRepeating(&internal::ShouldNeverRun); } return BindRepeating( &internal::BarrierCallbackInfo::Run, std::make_unique>( num_callbacks, std::move(done_callback))); } } // namespace base #endif // BASE_BARRIER_CALLBACK_H_