2015/03/07: Chrome Threading

Chrome はマルチスレッドなのだけれど、色々辛い。マルチスレッドがらみのバグはよくコードレビューで指摘されるし(気づくのは大したもんだおともうが)、TSAN (Thread Sanitizer: レースコンディション動的検出ツール) ボットが見つけてくれることもある。でも正直 reason-able なコードが書ける気がしない。皆なんで平気なのか謎。まあ平気じゃないから TSAN みたいなものが必要になるわけだけれど。

スレッド同士で間接的直接的に同じ変数を触ってレースが発生する。根本的にはそういうデザインを強いられるアーキテクチャがまずい。なぜオブジェクトをスレッドに縛り付けてメッセージパッシンする作りにしなかったのか。プロセス同士はそうなってるんだから同じことをすれば良いはずなのに・・・。

何がこのまずいデザインを招いたのか。なぜ人々は頑張れなかったのか。社会的な要素が大きいとわかりつつ、どんなデザインになっていれば平和だったかを想像してみる。

  • ある概念(たとえば HTML の Frame、ウィンドウ、クッキーのストア)などが、それぞれのスレッドで独立したアイデンティティを持つ、スレッド単位のミラーリングを行う、ためのパターンを決めておけばよかった : たとえば FrameIO, FrameUI みたいなかんじで担当スレッドと概念の対応付けがわかる名前にするとかね。そして対応づけられたスレッドでインスタンスを作り、別スレッドの相棒とチャンネルを接続して作業完了、となるまでが簡単に書ければよかった。スレッドに紐付けるオブジェクトのコードは部分的に見られるけれど、それを支援するインフラがない。結果として主にオブジェクトの寿命管理でバグがおこる。たとえばデストラクタが望ましくないスレッドで呼ばれたりする。これを避ける仕組みはあるっちゃあるんだけど使い難いし忘れがち。
  • スレッド間でクライアント・サーバ的なメッセージングをやる基盤が欲しかった。IPC/RPC みたいの。Chrome の IPC はすごく原始的なので、どうしても使わざるを得ない場面(つまりプロセス間通信)でしか使う気が起きない。もうちょっと汎用的なメッセージング機構として整理されていてほしかった。Go の Channel みたいなのは昔からあったし使われてたよ・・・。
  • そうしたメッセージングの仕組みが存在し、かつスレッドに対する PostTask() より簡単であるべきだった。別スレッドでコードを実行する PostTask() はすごく便利で、another_thread_->PostTask(Bind(&MyObject::DoSomething, this)) みたいな, 同じオブジェクトを別スレッドで動かすコードが大量に書かれる。そしてある日バグる。同じ手軽さでスレッド間通信を書ければ「ある日バグる」がおきずに済んだ。

などなど・・・。将来マルチスレッドなコードを書く日が来たら教訓にしたい。

・・・といいたいとこだけど自分じゃこんなコードにならないね・・・。自分はスレッドは人類には早すぎた道具なので近づかずにすむよう(デザインで)全力を尽くす派。日常的にロックをつかって排他制御する Chrome のコードには共感できない。もしかしたらボンクラばかりのチームの方が少数のエリートが決まりを作って守らせたかもしれない。でもそれなりにコード書ける人がその自負でもって各々勝手にコードを書いていくと、さっさと仕事を片付ける最短パス = PostTask() と lock, に収束してしまうのだろうなあ。Chrome は大掛かりなアーキテクチャ的制約みたいのが少ない。それは良いところもあるけど、辛い事もあるね。

それにしてもウェブの世界は非同期かつメッセージングなコードを強制されているのに、その実装がこんな shared state だとは皮肉なものであることよ。たぶん Servo とかはもっとずっとマシなのだろう。ちゃんと UI がついたころに見てみたい。