古代のコンピュータでは1台のコンピュータでひとつのタスクを実行するのが普通でした。*1 現代のコンピュータでは1台のコンピュータで複数のタスクを同時に実行するのが当たり前となり、さらには複数の OS を並列に動かす技術さえあります。
複数のタスクを同時実行するためには、タスクの間を調停する機能が必要となります。
マルチタスキングの種類
ハードウェアでマルチタスキングを実現する機能としてマルチプロセッシング (SMP: Symmetric Multiprocessing) やハードウェアマルチスレッディング (SMT: Simultaneous Multi-Threading /HTTなど) があり、これは既に myos にも実装されています。いずれ詳しく取り上げるかもしれません。 一方、ソフトウェアでマルチタスキングを実現する方法は大きく分けると2種類あってそれぞれプリエンプティブマルチタスキングと協調的マルチタスキングと呼ばれます。
プリエンプティブマルチタスキングはタイマー割り込みなどを使ってごく短時間で実行するタスクを次々切り替える方式です。 現在実行中のタスクの実行権を強制的に取り上げて他のタスクに切り替えることをプリエンプションと言います。 実行中のタスクがどんな状態でも強制的に他のタスクに切り替えできることから応答性は良くなりますが、タスク切り替えのタイミングが把握しづらく、中途半端な状態で切り替わっても問題ないように排他処理などの実装のコストは大きくなります。
もう一方の協調的マルチタスキングはタスク側が明示的に実行権を明け渡した時にタスクを切り替える方式です。 メリットデメリットはプリエンプティブマルチタスキングの正反対で、実装コストが比較的小さくタスク側でタスク切り替えのタイミングを制御できる反面、ひとつのタスクが長時間実行されると他のタスクに切り替えできなくなって応答性が悪くなります。
現代の OS では主にプリエンプティブマルチタスキングを採用していますが、ハードウェアの I/O 待ち合わせ処理や GUI プログラミングにおけるイベントループ処理は一般的に協調的マルチタスキングかそれに近い動作をします。
myos でもこれに倣ってプリエンプティブマルチタスキングを実装していますが、協調的マルチタスキングを採用することで効率よく処理できるタスクも多く存在するため対応したいです。
幸いにして Rust には async / await という協調的マルチタスキングをサポートする言語機能が備わっているため、これを利用して効率的なタスク管理を実装します。
async / await
Rust の協調的マルチタスキングは Future
という trait を実装したオブジェクトを使います。
協調動作する関数には async
という特別なキーワードを付けて定義すると関数のコンテキスト (変数などの状態) を保存するために内部的な struct を作って dyn Future
に変換します。
また、 await
キーワードは async
関数の中でのみ利用でき、 await
キーワードが現れた前後で関数のコンテキスト用 struct を分離してタスクを中断できるようにしています。
実は Rust が言語としてサポートしているのはここまでで、実際のタスクをどのように管理して実行するかは外部のライブラリに任されています。
いくつかメジャーなライブラリがあるようですが、自作 OS ですし Executor 自体は簡単なサンプルがあるのでそれをもとに実装します。
いざ実装
myos の場合、以下のようなオブジェクトが登場します。
Task
dyn Future
に一意の TaskId
を付けてラッピングしたもので、個別のタスクに相当します。
Executor
タスクの管理と実行を行う中心部です。
最も単純な Executor は配列に入れた dyn Future
に対して順番に poll
を呼び出し、 poll
の中ではタスクが完了したら Poll::Ready<T>
を、タスクが待ち状態に入ったら Poll::Pending
を返すのを繰り返します。
myos の Executor は各スレッドにひとつ持つことができます。
TaskWaker
await で待ち状態に入ったタスクに対して再開可能になったことを通知します。
よかったね
ここまで実装してウィンドウメッセージの処理は async / await で処理できるようになり、ひとつのスレッドで複数のウィンドウのメッセージループを処理することができるようになりました。
↑何が変わったか伝わりづらいスクリーンショット
実はタイマーとスケジューラーの内部処理に問題があってそっちの対応の方が手間がかかってました笑
*1:大規模なコンピュータではタイムシェアリングなどありましたが