ScalaとFuture その３ ~globalのExecutionContextソースコードリーディング~

# ScalaとFuture その３
- [その１](https://qiita.com/4245Ryomt/items/63bcf0bf0bab3a99f6b5)
- [その2](https://qiita.com/4245Ryomt/items/f923630d832ca5e6d9c6)
- `Future`は`Runnable`になって`ExecuteContext`で実行される
- いつも適当にimportしている`scala.concurrent.ExecutionContext.Implicits.global`はどうやってFutureを実行するんだろう？
- やっていくこと -> １つの例として`scala.concurrent.ExecutionContext.Implicits.global`のExecutionContextがFutureを実行するコードをみていく
    - Futureが失敗したときのアレソレは意図的にスルーしていく

## ExecutionContextのおさらい
- [ソースコード](https://github.com/scala/scala/blob/cc7acdde787fe32ba5894c35d1019963ed094c87/src/library/scala/concurrent/ExecutionContext.scala#L59)
- Futureを実行する。traitで定義されている
	- `execute`と`reportFailure`くらいしかない
- executeは`Runnable`を取る

```scala
trait ExecutionContext {
  def execute(runnable: Runnable): Unit

  def reportFailure(cause: Throwable): Unit
}
```
- ExecutionContextそのものはRunnableの実行の方法を定めていないんだね。ExecutionContextによってはThreadが新しく作られることも、処理が非同期じゃないこともある。
- つまり、Futureである = 非同期である　ではないんだね
- また、Futureである = 別のThreadで実行される　でもないんだね



## globalのExecutionContextのソースコードから読み解く
- [いつもインポートしてるヤツ](https://github.com/scala/scala/blob/ff32c8a4129b58839568c70a8ef76a52436366f5/src/library/scala/concurrent/ExecutionContext.scala#L131)
- `impl.ExecutionContextImpl.fromExecutor`に`null`を渡した返り値がいつも使ってる`ExecutionContext`のようだね
- `lazy`で宣言されているから一度呼び出されると使い回されるね

```scala
  object Implicits {
    implicit lazy val global: ExecutionContext = impl.ExecutionContextImpl.fromExecutor(null: Executor)
  }
```

## fromExecutor
- [fromExecutorの定義](https://github.com/scala/scala/blob/ff32c8a4129b58839568c70a8ef76a52436366f5/src/library/scala/concurrent/impl/ExecutionContextImpl.scala#L151)を見ると`ExecutionContextImpl`を生成しているようだ。

```scala
  def fromExecutor(e: Executor, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextImpl =
    new ExecutionContextImpl(Option(e).getOrElse(createDefaultExecutorService(reporter)), reporter)
```
- `Option(e).getOrElse(createDefaultExecutorService(reporter))`を見ると`global.executionContext`では`createDefaultExecutorService`から返る`ExecutorService`を使った`ExecutionContextImpl`が生成されるのがわかる

## ExecutionContextImpl
- ExecutionContextとExecutorを継承したトレイト
- `execute`の実装はコンストラクタにとった`Executor`に処理を移譲するようになっている

```scala
private[scala] class ExecutionContextImpl private[impl] (val executor: Executor, val reporter: Throwable => Unit) extends ExecutionContextExecutor {
  override def execute(runnable: Runnable) = executor execute runnable
}
```



## createDefaultExecutorService
- `global.executionContext`が処理を移譲するExecutorServiceを返す関数
- [ソースコードを見ると](https://github.com/scala/scala/blob/9acab45aeeadef2f63da69faf81465cc15599789/src/library/scala/concurrent/impl/ExecutionContextImpl.scala#L91)`ForkJoinPool`の`execute`を拡張したものを生成して返しているのがわかる

```scala
    new ForkJoinPool(desiredParallelism, threadFactory, uncaughtExceptionHandler, true) {
      override def execute(runnable: Runnable): Unit = {
        val fjt: ForkJoinTask[_] = runnable match {
          case t: ForkJoinTask[_] => t
          case r                  => new ExecutionContextImpl.AdaptedForkJoinTask(r)
        }
        Thread.currentThread match {
          case fjw: ForkJoinWorkerThread if fjw.getPool eq this => fjt.fork()
          case _                                                => super.execute(fjt)
        }
      }
    }
```

- `ForkJoinPool`を生成する際に引数で渡している`desiredParallelism`, `threadFactory`と第４引数のtrueが実行に関連しそうだ
- overrideされた`execute`を見ると引数にとったRunnableの型と`execute`が実行されているThreadによって処理が変わっている
	- 引数のRunnable(以下r)が`ForkJoinTask`でなかった場合コンストラクタにrをわたして`AdaptedForkJoinTask`を生成している
	- 次の処理で`execute`が実行されているThreadが`ForkJoinThread`で実行されているThreadと同一の`Thread`だった場合、`ForkJoinTask`を`fork`している
	- もし、`ForkJoinThread`でないもしくは`ForkJoinThread`だが、現在実行されている`Thread`とは別の`Thread`だった場合には`ForkJoinPool`の`execute`に`ForkJoinTask`を渡している。
	
- ***↑なるほどわからん***
- わかったことは
	- ForkJoinPoolによってFutureは実行される
	- 引数でわたってきたRunnableはForkJoinTaskではなかった場合にForkJoinTaskにラップされる
	- 実行時のThreadがForkJoinPoolだったらTaskはforkされる

### ForkJoinPool
- 複数のタスクを無駄なくスレッドに割り当てるThreadPoolの実装
- forkによってタスクの処理が中断されたときには、他のスレッドに割り当てられている実行待ちのタスクを処理しだすことが、joinによってできる(Work-stealing)
	- 勝手なイメージだが、コンビニのレジみたいな感じ。温め待ちのときもレジの人は行列をさばき、隣のレジにある行列から「次のお客様どうぞー」だったりと積極的に仕事をする

#### fork(分割)
- Task(A)がTask(B)を生み出して処理を分割する
- forkしてjoinする場合にはTask(B)を生み出したTask(A)はBの終了まで中断され、Aを実行していたThreadはまた別のTaskをさばく

#### join
- TaskからforkされたTaskをjoinすることで、forkされたjoinの結果をfork元は待つことができる
	- ThreadはforkされたTaskの結果を待つことなく、中断としてまた別のタスクを実行する

#### しかし
- globalのExecutionContextを読んでいてjoinしている様子はない

## まとめ
- `scala.concurrent.ExecutionContext.Implicits.global`の`ExecutionContext`は、実行ThreadがForkJoinTaskだった場合にはforkによって処理は実行される。
- `join`をしない、`ForkJoinPool`によって実行されてるようだ。