отсрочка записи реестра
wine дважды в минуту замораживает все процессы бутылки примерно на четверть секунды, чтобы переписать реестр. этот фикс придерживает запись, пока идёт игра.
симптом
каждые 30 с игра подтормаживает. на сетевом графике 15–17 тиков приходят одной пачкой. то же самое происходит на loopback, так что wi-fi ни при чём.
причина
wineserver однопоточный. каждые 30 с он в этом единственном потоке целиком переписывает каждую ветку реестра, изменившуюся с прошлого сохранения, одним текстовым файлом. в бутылке со steam system.reg весит около 12 мб (36.5 тыс. ключей), поэтому одна запись блокирует все обращения к серверу и все события сокетов примерно на 240 мс.
ветка не остаётся чистой надолго: steam раз в минуту трогает HKLM\Software\Wow6432Node\Valve\SteamService.
замеры
udp-эхо на 64 гц по loopback. приёмник работает внутри игровой бутылки так же, как сетевой код valve (WSAEventSelect + recvfrom). запущен deadlock, 110–120 с на прогон.
| приёмник | p99 | максимум | опоздали > 5 мс |
|---|---|---|---|
| нативный | 1.2 мс | 8 мс | 2 / 7040 |
| wine, без фикса | 6.1 мс | 264 мс | 80 / 7040 |
| wine, с фиксом | 3.1 мс | 10 мс | 18 / 7680 |
четыре фриза без фикса длились 232–264 мс, шли ровно через 30 с и заканчивались в пределах 7 мс от момента перезаписи system.reg.
фикс
в wineserver внедряется маленькая библиотека, которая перехватывает open(). когда начинается периодическая запись ветки больше 1 мб, а сервер занят (его доля cpu выше 5 %), открытие временного файла сразу завершается ошибкой. ветка остаётся изменённой, wineserver повторяет попытку через 30 с, и запись проходит, когда бутылка затихает. запись при выходе и запись маленьких веток вроде user.reg не затрагиваются.
if (branch[0] && periodic) {
double share = cpu_share();
if (busy_cpu > 0 && share > busy_cpu) { // идёт игра
errno = EACCES; // save_branch() отступает, ветка остаётся изменённой
return -1;
}
}
return open(path, flags, mode);что меняется на диске
bin/wineserverстановится скриптом-обёрткой из 5 строкbin/wineserver.orig— нетронутый оригиналbin/wineserver.real— неподписанная копия: у подписанного файла включён hardened runtime, и он игнорируетDYLD_INSERT_LIBRARIESbin/wineserver_fix.dylib— внедряемая библиотека
проверка
поиграйте несколько минут и откройте журнал. во время игры каждые 30 с должна появляться строка defer system.reg.
tail -f ~/Library/Logs/mgf/regsave.logограничения
- лёгкая игра может не дотянуть до порога 5 %, и фризы останутся. уменьшите
порог занятостив настройках и сверьтесь с журналом. - если wineserver убит, а не завершился сам, изменения реестра за время игры теряются. при обычном выходе они сохраняются.
- обновление crossover или повторный патч cxpatcher удаляют обёртку. приложение применит её снова, если включено «применять фиксы заново».