読者です 読者をやめる 読者になる 読者になる

昔のMacintoshの仕組み3 意外と原始的なイベントドリブンとマルチタスク化

パソコン 雑談 歴史

GUIな環境では基本的に上から下への手続き、バッチ処理的なプログラムではなく、マウスクリックやウィンドウの表示、サイズ変更といった各種イベントに従って動作を記述する「イベントドリブン」というプログラミング手法が必要になる。当然初期のMacもイベントドリブンなのだが、そのイベントの扱い方が今想像されるものとは若干異なっていた。

 

現代的なGUI OSにおいてイベント処理はどうするのが普通だろうか。各種イベントをOS側で監視して、アプリケーションが設定した適切なイベントハンドラを呼び出すのが普通の考え方だろう。それがオブジェクト指向っぽいし。

 

ところが昔のMacでは、OS側がやってくれるのはイベントをキューに追加するところまでである。アプリケーション側でGetNextEvent()という関数を呼んで、キューからイベントを取り出し、それがどんなイベントであるかイベントマスクで検査し、「あ、マウスクリックだね、じゃあこれ」「ウィンドウ隠れたのね、じゃあこれ」みたいにいちいち自分で分岐処理を書かなければいけなかった。なので一般的なアプリケーションは、初期化処理のあとで無限ループを書いて、その中でGetNextEvent()を呼び、必要な処理を行うということになる。こういう形だとスムーズなタスク切り替え出来ないんじゃないかと思うでしょう。ご安心下さい。Macはもともとシングルタスクでした。Macを起動すると、デスクトップを表示し、アイコンを操作するFinderというアプリケーションが自動的に起動するけど、これは単にディスクのブートブロックに書かれているので優先的に起動するだけの、ただのアプリケーション。Finderの上でアイコンをダブルクリックすると、FInderは終了し、ダブルクリックしたアプリケーションが起動する。アプリケーションはメモリもCPUも、すべての資源を独占して動作するわけで、マルチタスク的な気を使う部分はなかったわけである。Macの前のLisaがマルチタスクだったのに、Macがいろいろ捨ててたのはなんでか。それはやはりメモリが128KBと、とても少なかったことに起因するのだと思う。

 

Macのメモリが512KBになった頃 Switcherというアプリケーションがリリースされる。これはFInderを作ったアンディ・ハーツフェルドという天才プログラマが作ったもので、複数のアプリケーションを同時に立ち上げて、素早く切り替えられるようにしたものだ。それまでMacの全メモリをアプリケーションに割り当てていたのを、Switcherが適切なメモリを分割して割当てるのだが、もともとマルチタスクに対応していなかったMacで極力互換性の問題が生じないようにしたためだろう。画面には同時に立ちあがっているプログラムの一つだけが表示される仕組みだった。これはシングルタスクのMacの画面そのものである。メニューバーの右端にタスク切り替えボタンが表示され、これをクリックすると、メニューバーを含む画面全体が「ズバッ!」と横にスライドして次の画面が現れるのである。なお、これは正確にはマルチタスクとはいえない。複数のアプリケーションが同時にメモリ上に読み込まれて入るが、バックグランドに行っているアプリケーションは停止状態で待機することになる。しかし、Macの多くのアプリケーションはバックグラウンドで長時間計算し続けるようなたぐいのものではなく、ユーザーがインタラクティブに操作するものであったので、十分実用的だった。

 

Switcherは大変安定動作したうえ、増えたメモリを有効活用できたため、Appleから商品化されることになり、それなりに普及していた。その後アンディ・ハーツフェルドはさらに考えを進めたServantというソフトを作り上げる。

Switcherの場合、「Switcherというアプリケーションの設定画面」で同時起動するアプリケーションを登録する必要があった。だが、Servantは、Finderを置き換える本格的なGUIシェルであり、かつFinderとほとんど画面も操作も一緒でいわば拡張されたFinderであった。デスクトップから普通に次々アプリケーションを立ち上げ、後ろには他のアプリケーションのウィンドウがそのまま見えていて、クリックすれば前面にきて操作ができる。今のMacWindowsとほぼ同じである。ただし、やはり後ろに回ったアプリケーションはCPUタイムをもらえず。停止状態であった。まあ、OSそのものに時分割機能もないし、アプリケーションからタスクを他に回すAPIなんかもServantというアプリケーションでは追加しようがなかったし。しょうがない。

 

Servantではバックに回ったアプリケーションにイベントもいかないし、前面のウィンドウによって隠れたりした部分がどうなるか、白く抜けてしまってウィンドウ動かしたらみっともないことになるんじゃないかと思ったが、そういうことは起きず。前面ウィンドウの移動に従って隠れていた部分がきれいに復帰するのだ。おそらくアプリケーションの前後関係が変更されるタイミングでアプリケーション配下のウィンドウのビットマップを保存していたのだろうと思う。

 

Switcherでは各アプリケーションが使用するメモリ割り当てはSwitcherが勝手に行っていたが、Servantではユーザーがアプリケーションの情報ウィンドウで最大メモリサイズを設定することができるようになった。

 

他にもServantは、ファインダー上のアイコンのサイズを手軽に変更できたり、スクロールバー以外の部分をハンドツールでスクロールできるようにしたり、さらにはファイルの中に収められたリソースを覗くことができたりと、ずいぶんラジカルに機能を追加していた。ただ、このソフトはついに正式版になることはなく、β版として流通した後開発は中止される。

 

Servantがすっかり過去のものになった頃、ついにMacマルチタスク化されることになった。Finderが MultiFinderになったのだ。これが出た時驚いた。Servantのラジカルすぎる部分をそぎ落として、今までのFinderにマルチタスク機能を足したものにしか見えなかったからだ。起動アプリの一覧表示、「情報を見る」からのメモリ割り当てなど、MultiFinderとServantは驚くほど似ていた。ただし、こんどはバックグラウンドアプリにもタスクが回されるようになって、ほんとに一応マルチタスクになってはいたのだけど。当時のAppleの公式見解として、「Servantとは独立して開発された」というものがあった。逆にそう言わざるをえないほど似ていたという話だ。

「一応マルチタスクになった」と書いたのはわけがある。このときのマルチタスク化は、今のMacOSWindowsのような、プリエンプティブ・マルチタスクではなく、コオペラティブ・マルチタスクだった。プリエンプティブ・マルチタスクというのは、OSが強制的にアプリケーションのタスクを区切って次次タスクを切り替える方式だが、コオペラティブ・マルチタスクというのは、アプリケーションが「はい一旦スタジオにお返しします」とOSに制御を戻し、OSが次のアプリケーションに仕事を回すという手順が必要な方式だ。協調的マルチタスクとも言う。

さて、もともとシングルタスクだったMacのアプリケーションは、行儀よく協調的に書かれているわけではない。他のアプリケーションが同時に動くことなんかそもそも想定してすらいない。それなのにこの方式で動いたのはなぜか。

どんなMacアプリでもまず確実に、超頻繁に呼び出すAPIがある。GetNextEvent()だ。なので、GetNextEvent()を呼び出されたタイミングでOSが他のアプリに切り替えるようにしたのだ。これでスムーズにタスクスイッチングが可能になった。ただし、もともとGetNextEvent()は、「今最新のイベントを返して」っていうAPIであり、この呼び出しに時間がかかることは想定されていない。アプリケーションとしては即座に返事が帰ると思ってるAPIであまり長々よそ見をしてもらっては困る。なのでOSもこのAPIで呼ばれたときには、タスク切り替え一周したらできるだけ速やかに戻ってくるようにしていた。

ただ、イベント取得は実際には必ずしもすぐに来なくてもいいものだ。「キー入力が来るまで待ちたい」ときに「まだ?まだキー来ない?」となんども呼び出すのは効率が悪い。自分だけがMacを専有していたときはそれでもよかったのだが、複数のアプリが強調するなら、ここは効率を重視したい。

 

というわけで、MultiFInder導入と同時に、GetNextEvent()の上位版API。WaitNextEvent()が登場する。これはGetNextEvent()とほぼ同じだが、待ち時間の引数が追加されて指定した時間の間待つから帰ってこなくていいよ。というものだ。つまりあるアプリケーションが「100msの間もどってこなくていいよ」といえば他のタスクにその分CPUを割り当てることが可能になる。

 

なお、これもやはり待ち時間をどうするかなどめんどくさいところが多かったし、プリエンプティブ・マルチタスクMacOSXが導入されるとプリエンプティブなタスク切り替えとコオペラティブなそれが競合してしまってはなおさら無意味になる、結局昔のMacの互換APIであるCarbonは、Carbon Event Modelという、イベントハンドラをOSが適切に呼び出す形式のAPIを追加することになった。まあ、そんなすったもんだのあげくに互換性を保ちながらモダンになりかけてたCarbonもあっさり64bit非対応で廃止されちゃったんですけどね。