# 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)));
}
```
が、やはりテンプレートが入ると非常にわかりにくい。