Skip to content

Instantly share code, notes, and snippets.

@xyzzy-17-638
Created March 15, 2012 23:09
Show Gist options
  • Save xyzzy-17-638/2047525 to your computer and use it in GitHub Desktop.
Save xyzzy-17-638/2047525 to your computer and use it in GitHub Desktop.
$XYZZY/xyzzy.ini が読めないときがある問題について

xyzzy 終了時の ini ファイル処理

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 ファイルが存在するが、アクセス権が確保できない場合は待つ、などの処理を追加する
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment