понедельник, 28 января 2013 г.

Борюсь с дедлоком в своем проекте эмулятора. Вещь неприятная - возникает рандомно при включении звука в HOMM3. Оказалась причина дедлока - библиотека Miles Sound System. Для синхронизации работы с основным потоком они не используют критические секции и подобные примитивы, а нашли более "оригинальный метод".
Как написано на их сайте, "John Miles first released MSS in 1991 in the early days of PC gaming". Во времена ДОС для синхронизации было популярно использовать команды CLI (запретить прерывания), сделать свои дела и потом вызвать STI (разрешить прерывания). Видать стояла перед Женькой задача "быстро портировать под windows", вот он взял и заменил CLI на SuspendThread, а STI на ResumeThread для основного потока программы. А о том, что основной поток может быть не единственным, или то, что этот поток может "держать" критическую секцию, которая может понадобится самому MSS, да и то, что использовать данные функции не оптимально хотя бы с точки зрения производительности - думать не надо. И работало все до тех пор, пока не пришел я со своим эмулятором. И засуспендил MSS основной поток в тот момент, когда эмулятор держал критическую секцию, и попробовал MSS начать дальше выполняться - а фиг вам, для этого надо взять ту же секцию, а остановленный поток ее держит и не отдает. И пришлось мне делать свою реализацию SuspendThread, учитывающую подобные "особенности" криворуких программистов, не умеющих пользоваться примитивами синхронизации и изобретающих свой велосипед. "Miles is super robust. Since we ship in so many games, Miles just gets better and better - it just doesn't crash", да, он не крашится. Он просто наглухо вешает вашу программу. Хотя я сужу по библиотеке более чем 10-летней давности, может быть в последних версиях, наконец, действуют более корректно.

И еще один перл от того же автора. На этот раз в библиотеке binkw32.dll:
          loc_100014C2:                          
                push    0              
                call    Sleep
                mov     eax, [esi+4Ch]
                test    eax, eax
                jnz     short loc_100014C2
Зачем нам евенты - есть же поллинг, так хорошо зарекомендовавший себя под ДОС! Будем опрашивать переменную до посинения обнуления. Хотя бы Sleep(0) догадался добавить, чтобы квант времени потока отдать. Только не сильно оно помогло, у меня в эмуляторе этот цикл превратился в вечный. Но это уже баг на моей стороне, "переоптимизировал".

mamaich, 28.01.2013

2 комментария:

  1. А может не стоит делать критические секции вокруг вызовов? Там ещё много чего подобного SuspendThread может быть

    ОтветитьУдалить
  2. Критические секции делаю не я, а OS. Как пример - поток который пытаются засуспендить может находиться в это время внутри функции HeapAlloc. При этом ядро вынды держит крит.секцию, защищающую данную кучу. Если другой поток в это же время попробует вызвать любую функцию по работе с кучей - он будет остановлен. А если это будет поток, вызвавший SuspendThread - имеем дедлок. HeapAlloc в момем случае вызывается динамическим рекомпилятором от ДосБокса для генерации нового блока кода. То есть, как только я избавлюсь от движка добсокса - проблема сразу уйдет.
    Свой собственный движок x86 эмулятора я буду изначально разрабатывать максимально свободным от локов, в пользу атомарных операций.

    ОтветитьУдалить