言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

インチキプログレスバーの怪

Squeak でプロジェクトを保存したり読み込むときに、不思議なプログレスバーが現れる。普通、プログレスバーは現在コンピュータがどれくらい仕事を終えたのかを表示し、固まらずちゃんと動いている事をユーザに知らせる為にあるのだが、どうもこの保存のときのバーは無茶苦茶な動きをして全然参考にならないのだ。そこで、テストコードを書いて動きを試してみた。

ComplexProgressIndicator new 
	targetMorph: nil;
	historyCategory: 'hello';
	withProgressDo: [
		1 to: 10 do: [:i |
			ProgressNotification signal: i asString, ':TEST proceed'.
			(Delay forMilliseconds: 1000) wait.]
]

ここで、1から10の数字をプログレスバーに知らせて1秒待つという事をしているのだが、実はこのコードは驚くべき挙動を示す。初め、表示されるプログレスバーは滅茶苦茶な動きをする。しかし、なんと回数を重ねるうちに、正しく動き始めるのだ。ちなみに、プログレスバーに表示される数字は、残り秒数である。

プログレスバーは historyCategory の値を参考に、過去に行った仕事にかかった時間を覚えていて、その平均経過時間を元にバーを進ませる。凄い!賢すぎ!しかし、実はこのような挙動は Squeak プロジェクトにとっては全く良いところ無しなはずだ。

  1. プロジェクトは大抵作品によって大きく保存時間が違う。その為過去の記録は役に立たない。
  2. 典型的なプロジェクト保存の使い方としては、Squeak を起動してプロジェクトを保存すると、Squeakの過去の記録自体は保存せず終了してしまう。つまり、過去の記録はそもそも保存されない。

果たして、何でこんな滅茶苦茶なプログレスバーSqueak etoys の標準なのか理解に苦しむ。もう少し注意深くコードを読む必要があるとは思うが、ProgressInitiationException のようにまともに動作するプログレスバーがあるのにわざわざこういった物を作ったからには何らかの理由があるに違いない。

Squeak プロジェクト・コードリーディング

微妙に現れる不具合やパフォーマンスの低下を何とかしようと、ここの所SuperSwiki の日本語化問題に取り組んでいる。これは長くて苦しい茨の道である。しかしなんとなく道筋が分かってきたので書いておく。プロジェクトの保存は、Project>>storeOnServerShowProgressOn:forgetURL: から処理が始まる。しかしあわててここからデバッグしたりベンチマークを取ろうとするとドツボにはまる。プロジェクト保存時にモーダルダイアログを表示するため、相当トリッキーな処理をしているため、全くデバッグを寄せ付けない。そういうややこしい所を抜かして本質的なコードから入らなくてはならない。様々な準備を含め、デバッグに参考になるコードは以下のようになる。

"プロジェクトの準備、保存サーバやプロジェクト名を設定しておく"
Project current storeNewPrimaryURL: 'http://languagegame.no-ip.com/super/SuperSwikiProj/'.
projectName _ '実験です'.
Project current renameTo: projectName.
World setProperty: #ProjectDetails toValue: (Dictionary new add: 'projectname' -> projectName; yourself).

CurrentProjectRefactoring xxxCurrent flapsSuppressed: true.			"フラップが邪魔"
Project current storeOnServerWithProgressInfo.			"実際の保存処理"
CurrentProjectRefactoring xxxCurrent flapsSuppressed: false.			"フラップを戻す"

実際にはプロジェクト保存のメインルーチンとでも言える箇所は Project >> storeOnServerInnards になる。ここで、以下のような順序で保存/アップロード処理が行われる。

  1. バージョン番号の決定
  2. ローカルへの保存 (Project >> exportSegmentFileName:directory:)
  3. サーバにアップロード (Project >> writeFileNamed:fromDirectory:toServer:)

なんでこんな長いメソッドを書くのだ。。。

SuperSwiki プロジェクト・コードリーディング・解決編

何の面白みも無い日記で申し訳ありませんが、解決しました。業務連絡モードです。

問題: SuperSwiki を日本語で使うためのパッチを蜂須賀さんと阿部さんから頂いたのだが、どうやら上手く動かなかったり凄く遅い時があるらしい。

原因: 阿部さんのパッチでは、クライアントからサーバに送る文字列の一部に MultiString が混ざる。蜂須賀さんのコードでも再現すると思ったのは単なる勘違い。

String と MultiString を連結すると、答えもまた MultiString になるという動作から発生したバグ。

SqueakNihongo で内部的に利用される MultiString をソケットから出力したとき、なんか良くわからんエンコーディングで出力される。MutiString は 8 ビット境界の情報じゃないので、エラーになっても良さそうな物だが通ってしまう(そもそもサイズを必ず間違う)。サーバの側では勿論わけが分からずその情報を無視する。クライアントはサーバが何らかの返事をくれるのを期待し、タイムアウトまでじっと我慢する。

クライアントも適当な物で、タイムアウトで止まってもエラーとして処理せず何も無かったかのように動作し続けるので、遅いだけで一見動いているように見えてしまうが、実はこの時点ですでに問題が発生している(この問題というのが、単に gif のアップに失敗するなど、致命的で無い場合も多い)

というわけで、蜂須賀さんのコードをそのまま使えば良いんじゃないかと思います。もし問題が起これば sendCommand: あたりにデバッグコードを仕込んで、Project current storeOnServerWithProgressInfo を実行すれば良い(フラップから保存すると、技に溺れたコードのせいで上手く Transcript が使えません)。