JavaScriptエンジンのマルチスレッド対応

JavaScriptエンジン本体(SpiderMonkey)は比較的楽勝にWindows CEへポーティングが可能だったが、これはシングルスレッドが前提。
マルチスレッドで使えるようにするには、JS_THREADSAFEを有効にしてビルドをする必要があるが、これを有効にするとnspr (Netscape Portable Runtime)ライブラリとのリンクが必要になる。


ところが、このlibnsprが曲者で、こいつは簡単にはポーティングされてくれない。
以前も一度試みたことがあったが、ちょっと手直したくらいではビルドエラーの洪水はそう簡単に消えてくれないし、libnsprのつくり自体もいまいち完全に理解しきれないのでめんどくさくて、ずっと投げっぱなしになっていた。


今日は久しぶりに思い出して再挑戦してみたわけだが、今回は考え方を変えてみた。よく考えてみればJavaScriptエンジンで使うためにlibnsprを完全にポーティングする必要はないわけだ。エンジン側から呼び出されるAPIだけに絞って移植すれば何とかなるのではないか。


というわけでまずはリンクエラーとなる(エンジン側から呼び出される)APIをリストアップしてみる。
心配したほど多くはなかった。

PR_NewLock
PR_DestroyLock
PR_Lock
PR_Unlock
PR_NewCondVar
PR_DestroyCondVar
PR_WaitCondVar
PR_NotifyCondVar
PR_NotifyAllCondVar
PR_AtomicIncrement
PR_AtomicDecrement
PR_GetCurrentThread

この12個。


幸いなことに全部名前から機能が想像つくものばかりだ。
しかもそんなに複雑なコードを書かなくても実装できそうなものが多い。


関数名から検索するとソース上に簡単なコメントがあり、機能が説明されている。
上記の12個だけを実装するスタブライブラリを作って済ませることにした。

■PR_NewLock, PR_DestroyLock, PR_Lock, PR_Unlock

ロックオブジェクト。CreateMutex/CloseHandle/WaitForSingleObject/ReleaseMutexで簡単に代替。

■PR_NewCondVar, PR_DestroyCondVar, PR_WaitCondVar, PR_NotifyCondVar, PR_NotifyAllCondVar

同期オブジェクト。イベントで代替。CreateEvent/CloseHandle/WaitForSingleObject/SetEventあたりで簡単に代替・・・と思ったのだが、PR_NotifyAllCondVarが厄介者。このAPIは、待ち状態のすべてのスレッドに通知するというもの。簡単にSetEventだけでは置き換えはできなそうだったので、ちょっとだけ作りこみが必要になった。

■PR_AtomicIncrement, PR_AtomicDecrement

InterlockedIncrement, InterlockedDecrementで代替。

■PR_GetCurrentThread

libnspr内部管理のスレッドオブジェクトを返す関数だが、JavaScriptエンジンでは戻り値のポインタを単なるスレッド識別用の数値としてしか使っていないので、Win32のGetCurrentThreadでそのまま代替することにした。


かなり適当なつくりだけど、とりあえずビルドは通った!
マルチスレッドの動作も試してみたいが、まだjsshellにスレッドをforkする機能を作ってないので近いうちに作って確認しようと思っている。