【Android】launch(Dispatcher.Main.immediate) の呼び出し元も launch(Dispatcher.Main.immediate) だった場合、即時実行されない

:::note info
この記事では、簡単のため、[`CoroutineScope`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/) として [`GlobalScope`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/) を使用する。
:::

## 要約

次のコードを実行すると、

```Kotlin
GlobalScope.launch(Dispatchers.Main.immediate) {
    launch(Dispatchers.Main.immediate) {
        Log.i("", "in    launch")
    }
    Log.i("", "after launch")
}
```

期待に反して、[`launch`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html) ブロックが先に実行されず、次の順番でログが出力された。

```
after launch
in    launch
```

仕様かどうかは不明。


## 前提

メインスレッドにおいて、[`CoroutineDispatcher`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/) として [`Dispatchers.Main`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html) を使用して `launch` を行う次のようなコードを実行すると、

```Kotlin
GlobalScope.launch(Dispatchers.Main) {
    Log.i("", "in    launch")
}
Log.i("", "after launch")
```

`launch` 呼び出しが処理を返してから `launch` のブロックが実行されるため、
ログは次のようになる。

```
after launch
in    launch
```

しかし [`Dispatchers.Main.immediate`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-coroutine-dispatcher/immediate.html) を使用した場合は、

```Kotlin
GlobalScope.launch(Dispatchers.Main.immediate) {
    Log.i("", "in    launch")
}
Log.i("", "after launch")
```

`launch` のブロックが実行されてから `launch` 呼び出しが処理を返すため、
ログは次のようになる。

```
in    launch
after launch
```

`Dispatchers.Main.immediate` は、メインスレッドで処理を実行するが、既にメインスレッドにいる場合はディスパッチを行わないためである。

参考：

http://y-anz-m.blogspot.com/2020/12/dispatchersmainimmediate.html

同様に、次のコードも `launch(Dispatchers.Main.immediate)` のブロックが先に実行される。

```Kotlin
GlobalScope.launch(Dispatchers.Main) {
    launch(Dispatchers.Main.immediate) {
        Log.i("", "in    launch")
    }
    Log.i("", "after launch")
}
```

```
in    launch
after launch
```

## 本題


次のコードを実行すると、

```Kotlin
GlobalScope.launch(Dispatchers.Main.immediate) {
    launch(Dispatchers.Main.immediate) {
        Log.i("", "in    launch")
    }
    Log.i("", "after launch")
}
```

期待に反して次の順番でログが出力された。

```
after launch
in    launch
```

次のように、外側の `launch` を `Dispatchers.Main` にして、内側の `launch` を `withContext(Dispatchers.Main.immediate)` で囲んだ場合は

```Kotlin
GlobalScope.launch(Dispatchers.Main) {
    withContext(Dispatchers.Main.immediate) {
        launch(Dispatchers.Main.immediate) {
            Log.i("", "in    launch")
        }
        Log.i("", "after launch")
    }
}
```

期待どおり `launch` ブロックが先に実行された。

```
in    launch
after launch
```

どうやら `launch(Dispatchers.Main.immediate)` の呼び出し元の直近の `launch` も `launch(Dispatchers.Main.immediate)` だと、`launch` のブロックが先に実行されないようだ。

次のように関数に切り出しても同じ。

```Kotlin
@MainThread
fun launchWithImmediate() {
    GlobalScope.launch(Dispatchers.Main.immediate) {
        Log.i(tag, "in    launch")
    }
    Log.i(tag, "after launch")
}
```

```Kotlin
GlobalScope.launch(Dispatchers.Main.immediate) {
    launchWithImmediate()
}
```

```
after launch
in    launch
```

そのため、このような関数は呼び出し元によっては期待通りの動作をしない可能性がある。

---

仕様かどうかは不明（バグのように思える）。
なにかご存じの方がいらっしゃれば、ご教示いただけると幸いです。

／以上