Фокусы с cfg, разбор полетов

December 13th, 2009 Begemot Posted in Oбщее

Продолжая тему с потерей конфига. Кратко напомню суть проблемы – периодически при загрузки компа конфиг файлы программы – “плохие”. Либа выдает ошибку конвертации в юникод и данных нет. Как показали эксперименты, файл есть, примерно нужного размера но заполнен нулевыми символами (???). Причем конфига у меня два, первый открыт все время работы программы и закрывается при выходе\получении сообщения о выключении компа. Второй открывается локально при выходе\завершении и тут же закрывается. Проблема с обоими.

Я тут повозился полдня, подебажил сорцы, по перезагружался много раз – результаты такие.

Версия Сергея:

Теоретически, при любом сбое файл настроек должен ведь сохраниться. Я вижу пока только одну возможность – в момент flush когда он уже пометил старый файл пустым и не успел записать данные.

Неправильная, там внутрях используется wxTempFile, который обрабатывает это дело умно – пишет данные в временный файл, убивает оригинал, переименовывает временный в оригинал.

 

Eсли просто выключать комп все ок.

Если инициировать процесс выключения (в обработчике я пишу в конфиг и удаляю его), и сразу же нажать рессет. То в 60% случаев – проблема проявляется. Причем устойчивое появление проблемы мне удалось наблюдать только если программа стоит в автозагрузке, если же нет – то вроде все ок (это мне вообще мозг ломает…).

Сорцы смотрел, дебажил, эксперементировал. Почему такое может быть понять не могу – в мою картину мира не вписывается, то что трабла происходит только при ресете  – ну что может там такого происходить???  Судя по сорцам – все просто и правильно. А то что мне не удается это поймать когда запускаю руками программу, а не из автозагрузки – вообще путает все карты.

Уже не знаю что и думать. Наверное буду писать систему дублирования 🙁

Related:

Posted in Oбщее | Tags:

19 Responses to “Фокусы с cfg, разбор полетов”

  1. > используется wxTempFile, который обрабатывает это дело умно
    Использовал в программе аналогичный самописный механизм.
    Сначала создается файл name.ext.tmp, потом переименовывается в name.ext с заменой старого. В результате и нулевые, и заполненные нулями файлы точно так же случаются. Ни разу не было “куска” (незаконченного) файла при том, что размер – сотни килобайт. MFC, запись файла std::ofstream.

  2. ZeroDivisi0n Says:

    > трабла происходит только при ресете

    Вероятно все таки ОС не успевает сбрасывать файл на диск. Flush это программный механизм, который сообщает ОС что файл необходимо в ближайшее время записать на диск. А вот насколько это время будет “ближайшим” решает ядро ОС. Но даже это не дает полных гарантий записи файла. Следующий “этап” это контроллер жесткого диска. Сначала данные попадают к нему в кэш и в дело вступает планировщик запросов жесткого диска (со своими хитрыми алгоритмами).
    Это я все к тому что успешный вызов Flush не означает что данные записаны на диск, а гарантированного способа записи данных не существует.

  3. ну flush flushу рознь:) Конкретно здесь flush для настроек означает следующее
    1. записать данные из памяти в временный файл
    2. удалить оригинал
    3. переименовать временный в оригинал

    по симптомам он пишет временный заполняя его 0x00, и потом проходит оставшиеся 2 шага – удаление\переименование.

    Может ли тут быть задержка ОС?

  4. ZeroDivisi0n Says:

    Только что глянул реализацию файлового ввода-вывода в wxWidgets – она реализована с помощью стандартной библиотеки C, не нативными функциями WinAPI. У нее еще один буфер есть. ))) А про Flush вообще написано:

    Note that wxFile::Flush is not implemented on some Windows compilers due to a missing fsync function, which reduces the usefulness of this function (it can still be called but it will do nothing on unsupported compilers).

    Может стоит попробовать сделать этот модуль программы платформеннозависимым и написать на чистом WinAPI?

  5. Еще раз повторюсь, wxFileConfig::Flush != wxFile::Flush

  6. ZeroDivisi0n Says:

    Ну как бы там ни было, ошибка то возникает при записи в файл. А она реализована в wxFile. Так что я считаю копать надо в эту сторону.

    Если хотите, я могу попробовать переписать wxFile с использованием функций WinAPI, пришлю Вам реализацию, а Вы пропатчите wxWidgets и посмотрим что получится. 🙂

  7. Поздно, я уже написал систему бекапа файла настроек:) Хотя ради академического интереса можно наверное, но есть ли смысл?

    Но там по алгоритму файл явно *закрывается*, потом переименовывается – разве это не гарантирует отсутствие проблем с отложенным кешем?

  8. ZeroDivisi0n Says:

    Смысл, честно говоря, очень сомнительный. Только лишь для нас убедиться в правильности/не правильности идеи. 🙂

    Я еще внутрях откопал интересную штуку.
    Временный файл в wxTempFile создается с помощью функции wxFileName::CreateTempFileName(), которая довольно запутанная.
    В ней фигурирует функция с интересным названием: wxOpenWithDeleteOnClose (которая, вообще говоря, реализована только для Windows). Тык вот не возможен ли такой случай что программа, получив сигнал завершения, в какой-то момент удаляет этот файл, не закончив сохранять в него конфигурацию или переименовывать?
    Из вариантов решения – попробовать отказаться от временных файлов.

  9. Не думаю нет, я тестировал как ведет себя либа при получении сообщения о закрытии сессии, вроде бы ничего лишнего не делает, к тому же обработка все таки последовательная по идее пока я обрабатываю сообщение, либа ждет…

  10. ZeroDivisi0n Says:

    Дело в том что в функции wxOpenWithDeleteOnClose вызывается API-функция Windows CreateFile с флагом FILE_FLAG_DELETE_ON_CLOSE. Т.е. вся ответственность за удаление файла при выходе ложится на ОС и либа с этим никак не работает. А в какой именно момент Windows решит его удалить – неизвестно.

    Хотя… Если порассуждать логически, то удаление возможно после закрытия файла и до его переименования (т.е. файл закрыт и событие DELETE_ON_CLOSE также выполняется).
    Это хорошо состыкуется с логикой функции wxTempFile::Commit().

  11. эээ как это состыкуется? если бы винда удаляет файл до переименования, что тогда переименовывает вх? тогда бы терялись все настройки…

  12. ZeroDivisi0n Says:

    > тогда бы терялись все настройки…
    Ну в общем-то они и теряются. )))

    Винда может начать удалять В МОМЕНТ переименовывания – среда же многозадачная, никаких критических секций в коде WX нету. А как побочный эффект такого вмешательства – заполнение нулями. По коду в wx не могу сказать чтобы он где-то сам нули в файл писал.

    В общем это все догадки, они имеют место быть, но все их надо проверять.

  13. Может производить контрольное чтение после записи файла)? Типа:
    do
    {
    save(…);
    } while( check(…) );
    Или опять кэш(а их несколько…) будет мешать?

    Ещё как вариант: аппаратная зависимость от фирмвари ЖД.

  14. эээ в тот момент когда юзер нажал ресет производить или немного подождать сперва? 🙂

  15. Ну с ресетом понятно – нечего держать открытми файлы 🙂 . Я говорю про сохранение во время выключения. Вообще, судя про прочитанному выше про “умную” реализацию wxTempFile, я бы остерегался её использовать. Лучше самому написать нечто подобное. Один из простых вариантов: “крутить” несколько файлов. Тоесть сохранять в лоб под новым именем(лишний файл с “головы” удалять). Загрузку осуществлять по последнему валидному.
    Мне вот интересно как БД работают 🙂 – ничего же не пропадает. Где-то слышал про такие матерные слова как транзакции, журналирование…

  16. ZeroDivisi0n Says:

    Реализовывать журналирование для сохранения конфигурации – это слишком сурово я считаю. )))

  17. baralgin, возможно я выше неправильно что-то написал о работе wxTempFile, оригинал тут – http://docs.wxwidgets.org/stable/wx_wxtempfile.html

    И я не могу понять что вам там кажется “умным”, как по мне так совершенно логичная схема, безопасной записи, в которой я не вижу изъянов.

    p.s. и файлы открытыми никто не держит.

    и да журналирование и стек из нескольких файлов для конфигурации это конечно реально жесть 🙂 хотя с другой стороны я сделал резервное копирование\востановление, в принципе это тот же стек из двух файлов 🙂

  18. Попробовал. В итоге получается три вызова: конструктор, Write и Commit. Между какими жать ресет для повторения? 🙂 … может ещё есть какие особенности для надёжного повторения?

    Кстати, если не вызывать Commit или Discard временный файл живёт, он после перезагрузки то хоть удалиться или так останется мусором?

  19. >Между какими жать ресет для повторения?
    Если я правильно понимаю в коммите между записью и переименованием:)

    >может ещё есть какие особенности для надёжного повторения?
    У меня более менее стабильно повторялось если прога в атозагрузке, нажать выключение и сразу же ресет.

    >Кстати, если не вызывать Commit или Discard временный файл живёт, он после перезагрузки то хоть удалиться или так останется мусором?
    Сорцы либы не правил, ничего не могу сказать.