このページは福井県立大学の田中求之が2006年1月まで運用していた Mac のサーバ運用に関する会議室 「Web Scripter's Meeting」の記録です。情報が古くなっている可能性がありますのでご注意ください。

AppleScript のアプレットの処理の順番

発言者:田中求之
( Date Tuesday, September 10, 1996 18:55:36 )


AppleScript のアプレットにメッセージ(イベント)が大量に送られたとき
の動作を確認するために、以下のような簡単な実験を行ってみました。する
と面白い現象に気がつきました。

testServer というアプレットを作っておいて、スクリプトエディタで以下の
ようなスクリプトを実行し、連続して10個の AppleEvent を送りつけま
す。

ignoring application responses
  repeat with x from 1 to 10
    tell application "testServer"
      ヌevent XXXXYYYYネ (x as string)
    end tell
  end repeat
end ignoring

testServer は、送られてきたパラメーターをそのままファイルに追記してい
くという処理を行わせました。

まず testServer のスクリプトを、以下のような単純なものにした場合
(メッセージが送られると、ほぼ瞬時に処理が完了すると思われる)は、

on ヌevent XXXXYYYYネ thisP
  appendToFile (thisP & "-> ") to file "Plateaux2:Tempo:testServerData"
end ヌevent XXXXYYYYネ

ファイルには、以下のような当たり前の結果が記録されていました。

1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9-> 10-> 


ところが、testServer のスクリプトを、以下のように、かならず2秒間の処
理中断を行うように変更したところ、

on ヌevent XXXXYYYYネ thisP
  set dx to (current date) + 2
  repeat until (current date) > dx
  end repeat
  
  appendToFile (thisP & "-> ") to file "Plateaux2:Tempo:testServerData"
end ヌevent XXXXYYYYネ

処理結果は

10-> 9-> 8-> 7-> 6-> 5-> 4-> 3-> 2-> 1-> 

となって、最後に送られたイベントから逆順に処理されていました。


サーバー側(呼び出しを行ったスクリプト)で ignoring application 
responses を使っていますので、ACGI に対するサーバーからの多重呼び出し
とは動作が異なるのですが、それでも、ACGI 側での処理の順番が、サーバー
でメッセージが発せられた順番とは異なる可能性があるということは分かり
ます。

Web サーバーで CGI を実装する際に、この点を考慮していないとトラブルの
元になりますね。 WebCenter は、もしかしてこの点に問題があるのかな?

中島としひで さんからのコメント
( Tuesday, September 10, 1996 19:44:22 )

やっぱり、そうですか。昨日WebCenterの件で2台のクライアントからアクセスを
するテストをしてたんです。
同時にアクセスをした場合は、わからなかったんですが、片方がアクセスをはじめて
一息ついてから、もう1台でアクセスをするとなぜか、後からアクセスしたほうが先
にサーバーからのレスポンスがあったんです。

ん〜、やっぱりこの現象は私の気のせいではなかったんですねぇ。

石津@RJC さんからのコメント
( Tuesday, September 10, 1996 23:39:28 )

この現象(アップルスクリプトで組んだCGIが動作している最中に後から
リクエストが来た場合、後からのリクエストを先に処理する)については
WebStarでも同様です。

以前私が近似検索のスクリプトを田中さんに教えて(ほとんど作成)して
いただいた際に、この現象を確認しています。
一番目のリクエストの検索中に2番目のリクエストを行うと2番目が先に
答えが返ってきます。なんでかわからなかったけど、まぁいいやとほって
おきました。:-)

田中求之 さんからのコメント
( Wednesday, October 09, 1996 01:54:10 )

Applet にしたときの処理の順番について、いろいろと調べていたのですが、ようやく
わかりました( Scripter 2 のマニュアルに書いてあった (^_^;;  )

ある処理を行っている最中に、新しいメッセージが届いたときには、先に実行していた
のを1時中断して、後から来たメッセージの処理を行うというのが、AppleScript の
Applet の場合は仕様になっているようです。 re-entrance behavior という
のだそうで、要は、メッセージの処理の順番が first-in, last-out になるという
ことです。

このため、他のメッセージからの割り込みによって中断が入ると、グローバル変数など
の値は、実行中に変わってしまう可能性があります。これが原因で、間違った会議室に
投稿がポストされたり、あるいは要求したのとは異なるページ(会議室)が表示されて
しまうという現象が起こることになります。

特に、EasyBBS DX や STAR では、会議室のフォルダーや、メッセージを書き込む
フォルダーのパスをグローバル変数によって持つようになっていますので、これが
Re-entrancy によって影響を受けることになります(あと、AppleScript の
Text item delimiter も引っかかる可能性があるなぁ)。

Scripter 2 には、この re-entrant call の状況でスクリプトをデバッグする
ことができるようなので、これを使って、DX や STAR が re-enrant call でも
うまく動くように改良してみるつもりです。

たぶん、グローバル変数に依存するのをやめて、すべてローカル変数とパラメーター
で処理するようにすれば、なんとかなると思いますので、その方向でトライして
みます。

やれやれ。  …こんなことは AppleScript Language Guide には書いていなかった
ように思うぞ。

前薗 健一 さんからのコメント
( Wednesday, October 09, 1996 02:57:00 )

MacOS の問題ですね。
早くプリエンプティブなマルチタスクになって、くれないなか〜。

OS レベルで Thread, Semapho をサポートしてくれると楽なんですが。
今の ThreadMgr ってノンプリエンプティブですからね。

前薗 健一 さんからのコメント
( Wednesday, October 09, 1996 03:19:54 )

ついでと言っては失礼なんですが、マルチタスクのプログラムでの、
これまでの私の経験から ( OS/2 での経験です )

1.グローバル変数はできるだけ使わない。
2.グローバル変数を使う時は、排他制御を行う。(今の MacOS では無理)
3.C のプログラムでも、できるだけオブジェクト化し、外部からの影響を
  少なくする。(グローバル変数は static 宣言する)

ということを心がけています。

MacOS 8 になったら、オブジェクト指向の AppleScript ができたりする
かもしれませんね。

中島としひで さんからのコメント
( Wednesday, October 09, 1996 18:23:58 )

>>このため、他のメッセージからの割り込みによって中断が入ると、グローバル変数など
>>の値は、実行中に変わってしまう可能性があります。これが原因で、間違った会議室に
>>投稿がポストされたり、あるいは要求したのとは異なるページ(会議室)が表示されて
>>しまうという現象が起こることになります。
なるほど〜。これじゃあ、私の作ったスクリプトが正しく動作しないのもあた
り前ですね。でも、これ以外の理由は考えられないとは思ってましたけど。
とにかく、グローバル変数をなるべく使わないようにスクリプトを変更した
ら、正しく動作するようになりました。

この件って、スクリプトを作成する上では要注意事項ですね(^_^;)

田中求之 さんからのコメント
( Wednesday, October 09, 1996 19:25:22 )

>この件って、スクリプトを作成する上では要注意事項ですね(^_^;)

そうですね。CGI のように、同時にいくつものメッセージが重なって届く可能性が
ある Applet では、気を付けないといけないですよね。

私自身、普段のデバッグでは、こうした多重呼び出しのテストを行っていないため
気が付きませんでした。first-in, first-out だと信じ込んでました (^_^;;


というわけで、CGI のスクリプトを書くときの注意事項として

1:グローバル変数は使わない

2:AppleScript's text item delimiter を操作するときには、操作の前の
  値を保存しておき、作業後に元に戻すという処理を必ず行うこと

という2点があげられるということになりますね。


EasyBBS の方は、グローバル変数にしていた情報をレコードにまとめて、パラメーター
で渡すように書き換えようと思っています。