xyzzy 終了時には、以下のように conf.cc:write_conf()
群が呼び出されている
write_conf(Misc, 1920x1080, WINDOWPLACEMENT:(69,69)-(1221,679),1)
write_conf(Misc, saveWindowSize, 1, 0)
write_conf(Misc, saveWindowPosition, 1, 0)
write_conf(Misc, restoreWindowSize, 1, 0)
write_conf(Misc, restoreWindowPosition, 1, 0)
write_conf(Misc, windowFlags, 1383, 1)
write_conf(Misc, fnkeyLabels, 0, 0)
write_conf(Misc, foldMode, -1, 0)
write_conf(Misc, foldLinenumMode, 1, 0)
flush_conf()
終了時の ini ファイルへの書き込みが遅いのが原因ではないか、という予測のもとで、
conf.cc:void write_conf (const char *section, const char *name, const WINDOWPLACEMENT &w)
の動作を遅らせてみることにする。
関数を以下のように書き換えることで、 同様の現象が起きるようになる。
void
write_conf (const char *section, const char *name, const WINDOWPLACEMENT &w)
{
+ {
+ HANDLE h = CreateFileA(g_app.ini_file_path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(h != INVALID_HANDLE_VALUE)
+ {
+ Sleep(1000);
+ CloseHandle(h);
+ }
+ }
char buf[128];
sprintf (buf, "(%d,%d)-(%d,%d),%d",
w.rcNormalPosition.left,
w.rcNormalPosition.top,
w.rcNormalPosition.right,
w.rcNormalPosition.bottom,
w.showCmd);
WritePrivateProfileString (section, name, buf, g_app.ini_file_path);
}
なぜ $XYZZY/xyzzy.ini
が読めない場合、$XYZZY/usr/$USERNAME/wxp/xyzzy.ini
を読みに行くのか?
この場合の xyzzy.ini
の選択処理は init.cc:try_load_inifile_of_modulepath()
の中にあり、
$XYZZY/xyzzy.ini
への init.cc:FileExists()
が失敗した場合、期待と違う動作をする。
FileExists()
では、CreateFile()
をアクセス種別 GENERIC_READ
、ファイル共有モード 0
(共有無し) で呼び出しているため、
システム上の別の場所でファイルを開いている場合、必ず失敗することになる。
以下のどちらかで解消できる
FileExists()
のCreateFile()
の第2引数をGENERIC_READ
ではなく 0 (問い合わせ) にする。GetFileAttributes()
を使う (Win32的にはこちらのほうが素直かも?)
まず、動作を確認すると以下のようなことが分かる
- 起動時に ini ファイルへの読み書きをいろいろ行っている
- 終了時に ini ファイルへ書き込みを行っている
- 書き込み後の ini ファイルのフラッシュ (
conf.cc:flush_conf()
) は呼ばれたり、呼ばれなかったりする (意図は不明)
同一ファイルに対して、複数のプロセス (xyzzy.exe) から上記のようなファイルアクセスを行った場合、 ファイルが壊れる場合があるかもしれない(特に遅いメディアに対してフラッシュしなかった時は危険な予感がする)。
# ただこれは ini ファイルのそもそも持つ問題とも言える
そこで、以下のような変更を行う
write_conf()
等は廃止する- プロセスをまたいで共有するセマフォを作り、ini ファイルへのアクセスの排他制御を行う
- セマフォ取得、ini ファイルアクセス、フラッシュ、セマフォ開放を行うクラスを作る
- ini ファイルが存在するが、アクセス権が確保できない場合は待つ、などの処理を追加する