Inside.

File-mapping Object(FMO)に関するプライマリなドキュメンテーションは、
伺か仕様書 メモリオブジェクト http://sakura.nanika.org/objects.html
SSP BUGTRAQ FMO関連の仮ドキュメント http://ssp.shillest.net/docs/fmo.html
にあります。(2003年4月現在)

独自FMO利用について

SSTP Bottle Clientでは、設定ダイアログの「詳細」タブより、読み取るFMOの識別名を自由に設定することが可能です。

"Sakura"以外の識別子を持ったFMOについても、メモリの中身の仕様は"Sakura"と同一です。
SSTP Bottle Clientでは、Sakuraを含め、すべてのFMOについて、64KB以外の任意のサイズを認めています。先頭4バイトに正しくサイズを指定してください。

当然、これら仕様外の識別名を持ったFMOを書き込むSSTPサーバは、正しく設定されたSSTP Bottle以外のSSTPクライアントからの利用はできなくなってしまいますので注意してください。

FMO排他制御について

総論(排他制御について詳しい人は読む必要がありません)

FMOに2つ以上のプロセス(正確にはスレッド)が同時にアクセスすると、FMOが破壊される危険性があります。
この問題は2通りに分類されます。

(パターンA) 本当に全く同時にメモリに書き込みする場合
FMOが書式的に破壊され、読み取り不可能になる可能性があります。
(パターンB) FMOのメモリアクセス自体は時間がずれている場合
プロセスAがFMOを書き換えるためにこれを読み込んだが、ほぼ同時にプロセスBもFMOを書き換えるためにこれを読み込んだ。プロセスAはFMOの内容をローカルで解析・変更し、結果をFMOに書き込んだ。その後プロセスBが少し遅れて、同様に結果をFMOに書き込んだ。この場合、プロセスAのFMOの書き換えの結果は、プロセスBによってなかったことにされてしまいます。

パターンAは、FMOの内容が書式的に破壊されることからFMOを利用するアプリケーションのハングアップの可能性すらあるので危険ですが、常識的な実装をしていれば、頻度は高くありません。
パターンBは、あるべきFMOのエントリが消える、消したはずのFMOが復活する、書き換えたデータが元に戻る、といった結果として表れます。書式的なFMOの破壊がない分、理解されにくく、気づくのも困難です。頻繁にFMOを書き換えるようなアプリケーションが複数長時間起動していれば、起こる可能性は(理論上)かなり高く、現実的に考慮しないといけないレベルです。1msかけて毎秒書き換えていれば、1000秒に1回はコンフリクトすることになりますので。

破壊されたFMOを修復するため、定期的にFMOにアクセスして破壊を検出し、壊れていたら直す、という、泥沼状態にもなっているソフトもあります。

これらの問題を解決するため、同時にFMOにアクセスするスレッドを1つに制限するための、いわゆる排他制御が必要になります。

ただし、たとえばロックファイルを作る、レジストリを利用する、グローバルメモリを利用する、といったフラグ立てでは、パターンBと全く同様の問題により、堅牢な排他制御とは成り得ません。(プロセスAがロックファイルを検索して見つからなかった→プロセスBがロックファイルを検索して見つからなかった→プロセスAがロックファイルを作成→プロセスBがロックファイルを上書きしつつ作成→プロセスAとBが同時にFMOにアクセス)

問題のスケールが非常に短いタイミング(msオーダー)の話なので、「ロックされていなかったらロックする(if~then~)」形式のあらゆるコーディングは、Win32のようなマルチタスクOSにおいて、原理的にすべて脆弱です。

解決

Mutexを利用します。Mutexは、同時にその所有権を保持できるプロセスがただ1つであることがオペレーティングシステムによって保証されている、同期のためのオブジェクトです。

FMOを利用するアプリケーションは、FMOを利用する間、「SakuraFMO」と呼ばれるMutexを所有します。
このMutexを所有している間のみ、"Sakura" FMOに対する完全アクセスが可能です。

FMOの読み込みだけをするプロセスは、FMOの読み込みの前に、SakuraFMO Mutexを所有し、FMO読み込み直後に解放します。所有時間を少しでも短くするため、FMO中身の文字列としての解析はMutex解放後に行ってください。こうすることで、読み取りの真っ最中にFMOが書き換えられる可能性を排除できます。

FMOの書き込みを行うプロセスの場合には、FMOの読み込み→解析→書き込みまでの一連のプロセス全体を、1回のセクションとして保護する必要があります。
重要な注意点として、読み込みの瞬間と書き込みの瞬間をそれぞれ2回に分けて保護するようなことはしてはいけません。それをやると、パターンAでの破壊は防げますが、パターンBのような機序で、他のプロセスのFMO書き換えを自分が破棄してしまうことになります。
Mutexによる保護セクションに入る以前に取得してあったFMOに関するすべての記憶は、すべて古く、ほかの誰かが書き換えている可能性があるものとして扱わなければいけません。

コーディング

Delphi Language における例

// var Mutex: THandle;
Mutex := CreateMutex(nil, false, 'SakuraFMO');
if Mutex = 0 then
  raise Exception.Create('Mutex Create Error');
if WaitForSingleObject(Mutex, 1000) = WAIT_TIMEOUT then
  raise Exception.Create('Mutex Synchronize Timeout');
try
  FMO読み書き
finally
  ReleaseMutex(Mutex);
  CloseHandle(Mutex);
end;

Cにおける例

HANDLE hMutex;
hMutex = CreateMutex(NULL, FALSE, "SakuraFMO");
if (hMutex == NULL) error();
if (WaitForSingleObject(hMutex, 1000) == WAIT_TIMEOUT) {
    error();
} else {
    FMO読み書き;
    ReleaseMutex(Mutex);
}
CloseHandle(Mutex);

限界

Mutexを利用しないでFMOを書き換えるアプリケーションが1つでも存在した場合、排他制御はその部分で破綻します。

現在、Mutex "SakuraFMO" を利用してFMOの書き換えを行っているアプリケーションは、SSP / CROW / SSSB / SSTP-Viewer / SSTP Bottle Client (それぞれ最新版)です。(2003年4月、記事作者把握分のみ)
また、FMOの書き換えを行っているがMutexの今後の実装の可能性が薄いアプリケーションは、materia / TalkShow となっています。

注意

所有したFMOは、例外が発生しても必ず解放されるようにコーディングしてください。VB6以前ならOn Error Gotoを、C++なら try {} catch {} を、DelphiやVB.netなら try ... finally ... end を利用します。所有を解放しないと他のプロセスが一切FMOにアクセスできません。

OpenMutex()関数は既存のMutexオブジェクトがある場合に限りMutexを開く、という動作を行う関数ですが、これを利用して「OpenMutexに失敗したらCreateMutexする」というようなロジックを組む必要はありません。むしろ「存在しなければ作る」という処理は再三述べた理由により、期待通りの動作はしません(今回の場合は結果的に悪いことにはなりませんが)
CreateMutexは、Mutexがすでに存在している場合には単純にそれへの参照を返しますので、最初からこれを1回呼び出してください。

上記のサンプルコーディングはそうなっていませんが、CreateMutexによってMutexのハンドルを保持しているだけでは、Mutexを参照しているだけで、「所有権を持つ」ことにはなりません。つまりFMOを書き換えるたびに毎回CreateMutexするのではなく、Mutexのハンドルはアプリケーションの起動と終了時にCreate/Closeするほうが効率がよいです。

独自拡張

また、SSTP Bottle Clientの独自拡張として、"Sakura"以外のFMOを読み取ることが可能ですが、この場合、それぞれのFMO名に"FMO"を付けたものを、排他制御用のMutexとして利用します。
"MySoftware"というFMOを読み取る場合、SSTP Bottle Clientは、そのFMOの排他制御用のMutexとして、"MySoftwareFMO"を利用します。