---
# System prepended metadata

title: Barrier Closure
tags: [Chromium-General, elkurin-daily-note]

---

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

