# Barrier Closure Barrier ClosureはJavascriptでいう[Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)のようなやつで、各処理に対しコールバックを渡してそれらが指定回数呼ばれたら完了コールバックを呼ぶことで複数の非同期処理を待ってくれる。 ## base::BarrierClosure BarrierClosureは以下のような関数として定義されている。 ```cpp= BASE_EXPORT RepeatingClosure BarrierClosure(size_t num_closures, OnceClosure done_closure); ``` 実体は[RepeatingClosure](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/callback_forward.h;l=20;drc=35406c8d4b7301ede262aeedfc6a63e5e3cf555d)。 RepeatingClosureはOnceClosureに対する概念で、何度でも呼べるタイプのコールバック。 `num_closures`で、barrier closureが呼ばれるべき回数を指定し、その回数呼ばれると`done_closure`として渡したコールバックが呼ばれるようになっている。 以下のように、BarrierClosureをつくってそれを指定回数呼ぶことでunblockされる。 ```cpp= base::RepeatingClosure barrier_closure = base::BarrierClosure( actions_count, base::BindOnce(...)); for (size_t idx = 0; idx < actions_count; ++idx) { ConvertTextSelectionAction(converted_action, std::move(action), barrier_closure, gfx::ImageSkia()); ...; } ``` ### 実装 BarrierClosureがどう作られるかを見る前に、まずバリアのデータを持っているクラスを見てみる。 [BarrierInfo](https://source.chromium.org/chromium/chromium/src/+/main:base/barrier_closure.cc;l=18;drc=63e1f9974bc57b0ca12d790b2a73e5ba7f5cec6e)は以下のようなクラス。 ```cpp class BarrierInfo { public: BarrierInfo(size_t num_callbacks_left, OnceClosure done_closure); BarrierInfo(const BarrierInfo&) = delete; BarrierInfo& operator=(const BarrierInfo&) = delete; void Run(); private: AtomicRefCount num_callbacks_left_; OnceClosure done_closure_; }; ``` [Run](https://source.chromium.org/chromium/chromium/src/+/main:base/barrier_closure.cc;l=34;drc=63e1f9974bc57b0ca12d790b2a73e5ba7f5cec6e)関数は`num_callbacks_left_`をdecrementし、もしそれが0になったなら`done_closure_`を走らせる。 BarrierInfoを踏まえた上で、BarrierClosureは以下のように作られる。 ```cpp= RepeatingClosure BarrierClosure(size_t num_callbacks_left, OnceClosure done_closure) { if (num_callbacks_left == 0) { std::move(done_closure).Run(); return BindRepeating(&ShouldNeverRun); } return BindRepeating(&BarrierInfo::Run, std::make_unique<BarrierInfo>(num_callbacks_left, std::move(done_closure))); } ``` [ShouldNeverRun](https://source.chromium.org/chromium/chromium/src/+/main:base/barrier_closure.cc;l=40;drc=63e1f9974bc57b0ca12d790b2a73e5ba7f5cec6e)は中身に`CHECK(false)`があるだけの関数で、名前の通り絶対に走らない関数。 そうでないときは、受け取った`num_callbacks_left`と`done_closure`からBarrierInfo型を作り、それに対する[BarrierInfo::Run](https://source.chromium.org/chromium/chromium/src/+/main:base/barrier_closure.cc;l=40;drc=63e1f9974bc57b0ca12d790b2a73e5ba7f5cec6e)をRepeatingClosureとして返している。 つまり、ここで帰ってきたRepeatingClosureは一つのBarrierInfoクラスに対してRunを各々1回走らせるように設計されており、なので`num_callbacks_left`回はしると`done_closure`が走るようになっている。 ## BarrierCallback BarrierClosureの偉い版に[BarrierCallback](https://source.chromium.org/chromium/chromium/src/+/main:base/barrier_callback.h;l=103;drc=35a22f14c107c9eb0223a0be5adcdd491913a90a)がある。 用途はほぼ同じ。 OnceClosureとOnceCallbackの関係と同じで、Closureが結果を返せないのに対し返り値の型を指定できるのがCallback。 中身を見てみるとBarrierClosureと同じであることがわかる。 ```cpp template <typename T, typename RawArg = std::remove_cvref_t<T>, typename DoneArg = std::vector<RawArg>, template <typename> class CallbackType> requires(std::same_as<std::vector<RawArg>, std::remove_cvref_t<DoneArg>> && IsBaseCallback<CallbackType<void()>>) RepeatingCallback<void(T)> BarrierCallback( size_t num_callbacks, CallbackType<void(DoneArg)> done_callback) { if (num_callbacks == 0) { std::move(done_callback).Run({}); return BindRepeating(&internal::ShouldNeverRun<T>); } return BindRepeating( &internal::BarrierCallbackInfo<T, DoneArg>::Run, std::make_unique<internal::BarrierCallbackInfo<T, DoneArg>>( num_callbacks, std::move(done_callback))); } ``` が、やはりテンプレートが入ると非常にわかりにくい。