通信機構の方式

2006/11/28

現行の主筆の構造だと、各プロセス間の通信はサーバを経由して行うようになっている。それはつまり、サーバプロセスが通信基盤として働いていると言うことではないだろうか。

サーバプロセスの主たる業務は、同じファイルを同時に編集して、編集内容が失われることがないように調整することである。だが、今まで俺は、各プロセス間の通信を行う専門のモジュールを用意することは考えてこなかった。そのため、試行錯誤した結果としてサーバプロセスが通信基盤としての役割も担うことになってしまった。

だが、通信を司る役目と、主筆間の排他制御とには、本質的な関連性は何もない。であれば、この二つは分割するのが筋であるはずだ。つまり、独立した通信基盤を用意するのが良いのではないか、サーバはその上で動く一つのプロセスにしてしまうのが良いのではないか、ということに思い至った。

まぁ、気が付くのが遅いと言われれば返す言葉はないのだが。

では、独立した通信基盤というものを作るとした場合、それは一体どういう作りにするのが良いのだろうか? つまり、インターフェースをどうするべきなのか?

この通信基盤に乗っかって相互に会話を行うプロセスは、現状では三種類ある。クライアントとサーバと主筆本体だ。この三種類のプロセスは、それぞれまったく違ったパターンで通信を行う。

クライアントは、ユーザから入力された指示をパケットにしてサーバに送りつける。そしてサーバからの応答を受け取る。つまり、一問一答の形式で通信を行い、そしてそのままプロセスを終了してしまう。

サーバはクライアントとはまったく異なり、起動したら常に受信可能な状態で待機しており、誰かから何らかのパケットを受信したら、それに応じた処理を行い結果を返している。また、サーバは明示的な終了の指示が来るまでずっと存在し続ける。

主筆本体はサーバと似ているが、求めるインターフェースが大分異なる。パケットを送信する部分は他の二者と同様なのだが、データの受信はディスクリプタからの読み込みでなければならない。これはXはMotifの仕様によるものであり、曲げるわけにはいかない。

だから、主筆で使用する通信基盤は、それら三種類のプロセスが期待する通信形態を全て満足するような機能を提供しなければならない。

また、プロセスの起動を誰がやるのかという問題もある。サーバや主筆本体の起動は、通信基盤で責任を持ってやるのだろうか。あるいはクライアントやサーバプロセスで行うのだろうか。そもそも、通信基盤のプロセスは誰が起動するのだろうか? クライアントだろうか?

さらに、通信基盤のインスタンスはシステム全体に一つだけ用意するのか、各ユーザごと用意するのか?

通信基盤の構造はどうなるのか? 通信関係を司るプロセスを用意するのか、独立したプロセスは用意せず、各プロセスにリンクされるライブラリとプロトコルだけにするのか?

単純に通信基盤だけ独立させると言っても、決めなければならないことは多数ある。だが、ここを独立させることが出来れば、大分構造を明瞭にすることができるのはないだろうか。一考の価値はあるようだ。

通信機構

2006/11/25

主筆」は、クライアント・サーバ・エディタ本体の三つの部分に分かれている。ユーザはクライアントのコマンドを起動し、クライアントはサーバに指示を出し、サーバがエディタ本体を制御する機構となっている。

クライアント-サーバ間、エディタ-サーバ間の通信には、Solaris特有のdoorを用いている。サーバプロセスがdoorのサーバとなり、クライアント及びエディタ本体はdoorの手続きを呼ぶようにしている。また、サーバからエディタへの通信には名前付きpipeを使用している。

doorはSolaris特有の機能だ。そのため、現状では「主筆」をそのほかのOSに移植することはできない。しかし、市場のパイの大きさを考えれば、SolarisだけではなくLinux等の他のOSでも動作できるようにした方が、「主筆」の利用者数増加のためには良いと思われる。

他のOSに移植するには、door以外の他の通信手段を使用するよう変更しなければならない。何にするのが一番良いのだろうか? 前も似たような検討をしたことがあるような気もするが、暇だからもう一度考えてみよう。

やや古いが、Solaris インターナルという本がある。それによれば、Solarisで利用可能なIPC機構には下記のものがあるらしい。

  1. 共有メモリ

  2. メッセージキュー

  3. door

  4. ソケット

  5. シグナル


また、同期オブジェクトとしてセマフォやnutexを使用することができるようだ。

とりあえず、door以外の共有メモリ・メッセージキュー・ソケット・シグナルについて、利点・欠点について考えてみたいと思う。だがその前に、まずはIPC機構に求められる要件を明らかにする。

  1. 、「主筆」の通信はローカルマシン内に閉じている。NFSやsamba等の存在を考えれば、複数マシン間での排他制御を実現することには意義があるかも知れないが、しかしそれには非常に難しいものがあると思われる。とりあえずはローカルマシン内に限定して考えたい。そうすると、セキュリティ上の観点から、他のホストからは主筆サーバにアクセスできないようにする仕掛けが必要になる。自分で他ホストからのアクセスを禁止する仕掛けを作り込んでも良いが、そもそもの初めからIPC機構に他ホストからの通信を受け付ける機能が備わっていなければ、余計な心配をする必要が無くなる。

  2. 通信の速度はほとんど気にする必要はないようだ。通信に使用されるデータ量は余り多くなく、通信回数も、ユーザの操作に応じて数回発生するという程度なので、よほど遅いとか、あるいは極端にレイテンシが低いとか言うのでなければ問題はない。

  3. サーバは複数プロセスからのデータを受信する。そのため、各プロセスからのデータをひとかたまりで受信できるようになっていると良い。単一のデータストリームとして受信する形式になっていると、各プロセスからのデータを自力で分割するロジックを作り込む必要がある。

  4. サーバへは、複数のプロセスが非同期にデータを送信してくる。だが、サーバの処理はシリアルに行う必要がある。必ずしもシリアル化する必要があるというわけではないのだが、内部で状態管理を行っている都合上、いくつかの処理では排他制御を行う必要がある。そのため、ロジックを単純化するために、全面的に処理要求をシリアライズして実行するようにしている。非同期にデータを受信するIPC機構を用いた場合、サーバ側での排他制御処理を自前で作る必要があって面倒だ。

  5. サーバで処理を行ったら、サーバの呼び出し側(クライアントやエディタ本体)に対して、処理結果を返す必要がある。一方通行のIPC機構を用いた場合、逆方向のコネクションを自前で構築しなければならない。

  6. 当然だが、サーバにとってはイベントの受信は例外的事象ではない。むしろ、主たる業務なのである。そのため、受信用のハンドラ内で行うことのできる処理に制約があるような機構は使用することができない。


以上の要件を基に、各IPC機構について評価を行ってみたいと思う。

項番IPC機構評価/td>
1共有メモリ共有メモリで今回求められる機能を実現するのは難しそうだ。セマフォなどを駆使してうまく排他制御をかければ実現できなくもないだろうが、余りメリットはなさそうだ。そもそも、共有メモリは大量のデータを複数プロセス間で共有する際に真価を発揮するようだが、残念ながら今回はそれ程大量のデータをやりとりする必要性はない。
2メッセージキューメッセージキューを使えば自前で排他制御を行う必要はなくなるようだ。また、データはメッセージという単位で送受信されるため、各プロセスからのデータを自前で切り分ける必要性や、複数プロセスからのデータが入り交じる心配などはないようだ。だが、クライアントやエディタ本体がサーバからのレスポンスと受け取る方式については、別途考える必要性がありそうだ。メッセージキューの機構をそのまま使うと、サーバからのレスポンスを受け取るために待機しているクライアントなどに対して、他のプロセスが不正なメッセージを送りつけることが可能となってしまうのではないだろうか? まぁ、そこまで考える必要はないといえばそれまでなのだが。
3door現在使っているのが、このdoorである。サーバ側で排他制御処理を自前でやらなければならないということ以外には特に問題がないため、今回の目的にもっとも適合していると思い採用した。だが、Solarisに依存することになるのが最大の難点である。
4ソケットここで挙げた五つの中では、おそらく一番自由性が高いのではないかと思われる。少なくとも、今回の目的において必要とされる機能は全て実現することができる。だが、強いて難を挙げるとすれば、他ホストからの通信をブロックする処理を自前で作らなければならないことや、通信処理を記述するのが面倒そうだということがある。
5シグナルシグナルはハンドラ内で実行できる処理に限りがある。また、ハンドラ内でシグナルのマスクだの何だのという処理を自前でやらなくてはならず、さらにはマスクしている間に送信されてきたシグナルは無視されるというのだから、今回のような目的には使えた代物ではないと思われる。そもそも、俺はシグナルは好きではない。


こう考えると、候補としてあげられるのはソケットかメッセージキューであるといえる。中でもメッセージキューが一番目的に適合しているのではないだろうか? サーバ内での排他制御処理を実装するのは、実は結構面倒だったりもするからだ。

あるいはSolaris依存であることを受け入れてdoorを使い続けるか。

まぁ、Linux上でのdoorの実装というのも無いことはないようだし、どうしてもdoorを捨てなければならないというわけではないのだが、しかし、移植の容易性や市場のパイの大きさを考えれば、通信機構を作り直すのも悪くはないと思われる。

今後、気が向いたら手を入れてみることにしよう。

中断機能の拡張

2006/11/24

昨日の考察を基に、エンコード変換処理のプロセスを停止する機能を実装しておいた。難しいのではないかと思ったが、存外困難もなく終わった。

何か処理をやらせたら応答が返ってこなくなり、にっちもさっちもいかなくなって最終的にはプロセスを殺さざるを得なくなる。そういう経験は誰しもあるだろうが、「主筆」ではそんなことは起こらないようにしたい。せっかく自分で作るのだから、細かいところにもこだわって作りたいと思う。

そういうことで、処理がかかるであろうと思われる部分、テキストの検索・置換処理やファイル入出力の処理は、途中でキャンセルできるようにしてある。また、処理にほとんど時間がかからなかった場合、処理中ダイアログが一瞬だけ表示されて、表示された直後に消えるということが起こってしまうが、「主筆」ではそれを防ぐために、処理が開始されてから処理中ダイアログが表示されるまでにある程度の遅延時間を持たせてある。そうすることで、ダイアログの無意味な明滅を防ぐことができる。

だが、それ程思い入れのある中断機能も、現在は検索・置換とファイル入出力にしか実装されていない。一般的にはこの二つが重くなりがちな機能ではあるのだが、これ以外にもコピーや貼り付けも重くなる要因を含んでいる。長大なテキストを選択してクリップボードにコピーしようとしたり、逆に長いテキストを貼り付けようとした場合には、マシンのスペックにもよるが、かなりの時間がかかる可能性がある。

また、プラグインの処理も時間がかかる可能性がある。プラグインの本来の目的は、第三者作成のプログラムを組み込む機能を提供することにある。そのため、プラグインは自分以外の人間により作成されるものと想定しておかなければならない。つまり、プラグインの処理にどれぐらい時間がかかるか、あらかじめ決めてかかるわけにはいかないと言うことだ。(実態としては、全てのプラグインは自分で作っているのだが。)

だから、プラグインを呼び出してしばらくの間応答がなかった場合には、処理中ダイアログを表示するようにしておいた方が良いと思われる。

だが、この機能を闇雲に実装するのもまた考え物である。

まず、昨日書いたとおり、この昨日の実装のためには、UIのスレッドと実処理のスレッドとを分けなければならない。今まで単純安直にシングルスレッドで実装していたものをマルチスレッドにしなければならないのだから、面倒な事この上ない。

スレッド分割には、面倒だという以外にも嫌な問題が腐るほどある。特に嫌なのが、テキストの描画(UIスレッドで行う)と、テキストの編集(実作業を行うスレッドで行う)とが競合することだ。同じものを複数のスレッドで同時に編集しようとすれば、当然の事ながらやばいことになる。

だから「主筆」、スレッドを分割するときに空のドキュメントを作成して、UIのスレッドには生成した空のドキュメントを参照させるようにしている。そうすることで、処理中には画面が真っ白になってしまうという問題はあるが、宇宙の果てまで飛んでいく様なことにはならずに済む。
だが、この処理には時間がかかるという問題がある。

通常であれば、編集対象のドキュメントに対して排他制御を行うということになるのだろうが、ドキュメントクラスが持つメソッドの個数や、その他諸々の状況を考えると、排他制御をかけるのは困難だと判断した。そのため、空のドキュメントの作成という力業に頼るしかなかったのだ。

作業工数やロジックの複雑性の増大、処理速度の低下などを考えると、中断機能を連発するのも危険なのだ。だから、当面は現状のままおいておくものとして、拡張については追々考えていくようにしよう。

子プロセスの停止

2006/11/23

以前、「処理中」ダイアログが表示される待ち時間のうちには、ファイル入出力時に実行されるエンコード変換処理は含まれていないと書いたが、よく見直してみるとそうではなかった。一応、エンコード変換処理に時間がかかった場合にも「処理中」ダイアログは表示されるようになっていた。

しかし、「処理中」ダイアログにある停止ボタンを押下しても、エンコード変換処理が途中で停止されることはない。それは、エンコード変換処理が外部プロセスとして実装されており、主筆は単純にエンコード変換処理が終了するのを待ち合わせるだけだからだ。だから、中断を指示されても、そのことを検知して処理を中断するような作りにはなっていない。

だが、それでは「処理中」ダイアログの意味がない。中断できない中断ボタンならない方がいい。そういうことで、エンコード変換処理を途中で中断させる方法について考えてみたいと思う。

上でも書いたが、エンコード変換処理は別プロセスとして実装されている。また、「主筆」内の入出力処理は別スレッドで処理されるようになっており、「中断」ボタンが押下されたときには、変数「InterruptFlg」に真を設定することで中断指示を伝えるようにしている。つまり、入出力処理を行っているスレッドでは、変数「InterruptFlg」を随時チェックして、真が設定されていたらその時点で処理を中断するようにしているのだ。

ここで、エンコード変換処理のプロセスを停止することを考える。外部プロセスを停止する方法はいろいろあるだろうが、汎用的な方法としてはシグナルを使う方法がある。エンコード変換処理を行うプログラムの仕様を考えると、おそらくシグナルを使うのが最も簡単だろう。

変数「InterruptFlg」に真が設定されたタイミングで、エンコード変換処理プロセスにシグナルを送るためには、「主筆」側に変数の値を監視して然るべき時にシグナルを送信するスレッドが必要となる。

では、どのスレッドで監視処理を行うのか。

今の仕様では、入出力処理中に動いているスレッドは二つ。「処理中」ダイアログを表示しているスレッドと、入出力処理を行っているスレッドだ。

「処理中」ダイアログを表示しているスレッドは、「中断」ボタンが押下されたタイミングで変数「InterruptFlg」に真を設定するのだから、その延長でエンコード変換処理プロセスにシグナルを送出することもできる。だが、せっかくUI用のスレッドと実処理を行うスレッドとを分割したのに、その設計に逆らってUI用スレッドで中断処理をやらせるのは気分が悪い。また、「中断」ボタンはエンコード変換処理中のみならず、その他の処理、たとえばテンポラリファイルへの出力処理やエンコード変換後のテキストを実際に読み込む処理などでも押下される可能性がある。そのため、「中断」ボタンが押下された時にシグナルを送出するようにするのは、何かと難しいのではないかと思う。

だったら、入出力処理を行っているスレッドでシグナルを送出するようにすればいいのか。だが、それも難しいような気がする。現在の実装では、エンコード変換処理中には入出力スレッドはプロセスの処理待ちで停止するようにしている。そのため、変数「InterruptFlg」に真が設定されたか否かの監視を行うことができない。当然、シグナルを送出することもできない。

入出力処理を行っているスレッドでシグナルを送出するためには、単純にプロセスの終了を待ち合わせるのではなく、スピンウェイトを行い、その中で子プロセスと変数「InterruptFlg」の監視を行うようにしなければならない。

無論、スレッドは全力でスピンウェイトしなければならない理由はないので、おそらく100ms程度の待ちを入れることになるのだろうが、それでもスピンウェイトに対しては抵抗を感じざるを得ない。まぁ、入出力スレッドで監視を行う場合には、スピンウェイトする以外に選択肢はないし、おそらく実用上も問題はないのだろうが。

それと、もう一つ方法がある。監視用のスレッドを別に一つ作るという方法だ。プロセス生成時に監視用のスレッドを作り、そのスレッドでスピンウェイトを行い変数「InterruptFlg」の監視とシグナルの送出を行うのだ。

だが、この方法でも結局スピンウェイトからは逃れられないし、おまけに新たにスレッドを作らなければならないため、処理が複雑になるというデメリットがある。入出力用スレッドでの監視に比べてメリットが少ない。

そういうことで、エンコード変換処理プロセスを中断させるには、入出力処理用スレッドで監視を行うのが一番いいのではないかと思う。

残る問題は、エンコード変換処理プロセスの実装だ。シグナルを受信したら、何か処理を行わなければならないのだろうか?

プロセスが終了すれば開いたファイルや確保したメモリの後始末はOSが全部やってくれるし、ファイルの出力が完了せず中途半端な状態になるという問題はあるが、これはそもそも中断するのだから当然のことだし、特に何か必要なことがあるとも思えない。

強いて問題としてあげるのであれば、OSの機能でプロセスを殺すことを通常処理の一部に組み入れることに対する抵抗感だけだ。

この気持ち悪さに目をつぶれば、少なくともユーザから見た仕様を本来あるべき姿にする事ができるだろう。

次の機能

2006/11/19

主筆」のHTML対応を終えた。ついでに、構文強調表示に使用するキーワードを外部のファイルから読み込むようにも変更しておいた。

次の何の機能に手を着けるのがいいだろうか?

行折り返しやマネージャー機能など、いろいろと思いつきはするが、どれも大変そうだ。第16版を公開してからだいぶ経つから、そろそろ第17版として公開しても悪くはないのではないだろうか。そんな気がしてきた。

いつ新バージョンを公開するとか、どれぐらいの規模の改修が行われたらバージョンをあげるとか、そういった決め事は一切ないのだし、自分の好きなときに公開すればいいだけなのだが、それだから逆にいつ公開するのかが迷う。

第16版から比較して、あまり機能が向上しているとも思えないが、まぁいいか。そろそろ第17版を公開する事にしよう。

高速化

2006/11/17

何日ぶりかに「主筆」の開発に戻ることにした。

とりあえずは、途中で放置していたHTML対応を終了させておいた。構文強調表示のロジックは作ってあったのだが、強調表示を行うキーワードの一覧の作成が滞っていたのだ。だから、MSDNからHTMLのキーワードとおぼしきものをリストアップして、使っておくことにした。

その作業が終了して動作を確認している時に気が付いたのだが、なぜか処理速度が遅い。それ程大した量でもないファイルを読み込んだだけで、構文強調表示処理に結構時間がかかる。

なぜだろうかと思って間tなんいしあべてみると、すぐに原因が分かった。

キーワードのリストは、今後の検索に備えて並び替える処理を行っているのだが、その並び替えの処理を、キーワードを検索するたびに行っていたのだ。

この負荷の重い処理をやりたくないがためだけに、構文強調表示処理を行うクラスのインスタンスを使い回すようにしたり、メンバをstaticにしたりだのといろいろと小賢しいことをやっているのに、最後の最後に検索を行う関数で、キーワードのリストを保持するクラスのインスタンスを生成して検索処理をやっている。

我ながらあきれるほど馬鹿だとしか言いようがない。しかも、よく見てみれば、同じ事をCOBOLの構文強調表示でもやっていた。

とりあえずそれを修正したら、かなり高速化された。

ディスプレイ

2006/11/13

今朝起きてPCの電源を入れたら、ディスプレイが写らなかった。

ディスプレイの設定やケーブルの接続、PCの操作(電源ボタンを押すだけだが)など、いろいろと思い当たる節をいじってみたが、どうにも写らない。

ディスプレイ関係で何か壊れる原因となるようなことをやったのだろうかと自問してみると、どうにも思い当たる節は一つしかない。

おとといリリースした糞ゲームJsvzのテストをやるために、いろいろとディスプレイの設定を変えて動作させてみたから、壊れたとしたらそれしかないだろう。

ということは、おそらく故障の原因はソフト的なものだろうから、その設定をいじってやれば元に戻る可能性が高い。だが、どうやっていじるのだろうか?

現在のPCの設定では、外部からアクセスして操作できるような方図は用意していない。telnetも有効にはしていない。正直、お手上げ状態だ。

そこまで考えたときに気がついた。

よく見てみると、BIOSの画面すら表示されていない。ソフト的な故障であれば、BIOSの画面は表示されてしかるべきだ。そうすれば、せめてセーフモードで起動することぐらいはできるだろうし、セーフモードでも起動すればディスプレイの設定ぐらいなら何とでもなる。

だが、BIOSの画面すら表示されないのであれば、故障の原因はハード的なものに他ならないのだろう。では何が原因か。

最近あった影響を与えそうなイベントといえば、やはりグラフィックボードの設定を変更したことだ。だから、それが原因でグラフィックボードがいかれたのだろうか?

そう考えると納得がいく。何せ今使っているPCは6年前に買ったものであり、グラフィックボードは5年前のものだ。さすがにそろそろ壊れてもおかしくない。

もうPCの買い換え時だろうか。仕方がない、とりあえず今日のところはBlade100のやつで用を足しておくことにしようか。

そう思って、配線をつなぎ変えてBlade100の電源を入れてみたが、それでもディスプレイが写らなかった。

どうやら、犯人はPCのグラフィックボードではなかったようだ。これは単純にPCが壊れただけのようだ。

だが、それは最悪の結論ともいえる。半年ほど前にもディスプレイ損壊の危機が訪れたが、そのとき以来状況は何も変化していないから、ディスプレイに故障されると俺の生活に非常に多大な影響を及ぼすことになる。

今使用しているディスプレイは、俺の日常生活においてはクリティカルな機器の一つなのだ。

マジでどうしようか。どうやら、写らないのはPCの映像だけで、ビデオなど映像はちゃんと映っているから、どうにかすればPCの映像をテレビ入力に接続することもできなくはないだろうが、そんな運用では長期間の使用には耐えそうにもない。

どうにかして写るようにすることはできないだろうか。そう思っていろいろといじっていたら、ふとあることに思い至った。

昨日、電源系統をいくつかつなぎ変えたが、そのときディスプレイ切り替え機の電源をつないだだろうか?

とりあえず、切り替え機の本体に目をやると、スイッチの状態を表示するランプはちゃんと点灯している。だが、なんだかいつもより暗いような気がするが、しかし電源はきているようだ。

とりあえずだめ元で電源を調べてみると、コードはちぇんとテーブルタップにつながってはいるのだが、そのテーブルタップについているスイッチが入っていなかった。

結局、これが原因だった。

電源が入っていなくてもスイッチのランプが点灯していた原因はよくわからないが、おそらくPCからの映像出力の信号から電源を得ていたのだろう。それで薄暗く光っていたのだろう。というか、それしか考えられない。紛らわしいことだ。

とりあえず、グラフィックボードとディスプレイは冤罪だった。まぁ、よかったといえばそれまでなのだが、しかし、朝から心臓に悪い出来事だった。

Vectorに投稿

2006/11/11

ここ一月ばかりかけて作った糞ゲームをVectorに投稿しておいた。これで多少はアクセス数が増えるだろうか?

一応、曲がりなりにも撒き餌を撒いたのだから、その受け皿となるWebページの方も用意してやらなければならないだろう。

まったく、面倒なことだ。

微調整

2006/11/04

工数をつぎ込んだお陰で、とりあえずゲームとして成立するだけの機能はそろった。

ステージがあって、その中をプレイヤが移動することができて、敵がいて、敵はプレイヤに向かって攻撃してきて、プレイヤは敵に攻撃してそれを倒すことができる。また、敵や敵の攻撃に触れるとゲームが終了し、あるいは、穴に落ちた場合もゲームが終了する。ステージの最後にはボスキャラがいて、そいつを倒すとゲームが終了する。

一般的に、ゲームオーバーになった場合は素っ気ない終わり方で、ゲームをクリアした場合には豪勢なエンディングを見せるのだろうが、実装するのは面倒だから、とりあえずクリアしたと言うことが分ける程度のエンディングにしておいた。というか、単に画面が暗転するだけだが。

残作業としては、音を出す、細かい微調整、ゲームそのものを出す、配布できるようにする、等が残っている。今週中には公開できるようにするつもりだったが、おそらく無理だろう。