Since 2.0.9 game freezes every few minutes on Win7

On Win7 x64, but with 32bit dhewm3 and SDL2 2.0.9 the game freezes for about 5 seconds after a few minutes. Then it continues working again and freezes again a few minutes later. While the freezes seem to always take around 5sec on my machine, the time before the first freezes and between subsequent freezes seems more random - usually 5-10min before the first freeze?
This does not happen with 2.0.8 and I couldn’t reproduce it on Win10 either.

So I debugged it a bit by manually breaking in the VS debugger when the game is frozen (luckily 5 seconds is long enough to do this…). I did it about 3 times, and each time the backtrace was like the following:

ntdll.dll!7765f931()    Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
ntdll.dll!7765f931()    Unknown
KernelBase.dll!76cfd2bf()       Unknown
kernel32.dll!75483217() Unknown
hid.dll!5d442874()      Unknown
SDL2.dll!hid_enumerate(unsigned short vendor_id, unsigned short product_id) Line 481    C
SDL2.dll!HIDAPI_UpdateDeviceList() Line 872     C
SDL2.dll!HIDAPI_JoystickDetect() Line 927       C
SDL2.dll!SDL_JoystickUpdate_REAL() Line 1076    C
SDL2.dll!SDL_PumpEvents_REAL() Line 664 C
SDL2.dll!SDL_PumpEvents() Line 147      C
dhewm3.exe!Sys_GenerateEvents() Line 701        C++
dhewm3.exe!idCommonLocal::Frame() Line 2396     C++
dhewm3.exe!SDL_main(int argc, char * * argv) Line 795   C++
dhewm3.exe!main(int argc, char * * argv) Line 317       C
dhewm3.exe!WinMain(...)

The function that’s being called there by SDL is HidD_GetProductString() .

Apparently this HIDAPI code has been added between 2.0.8 and 2.0.9, so that’s consistent with the observation that it doesn’t happen with 2.0.8 and older.

However it seems like (at least on Win7…) HIDAPI_JoystickDetect() is called each frame, and it seems to call HIDAPI_UpdateDeviceList() each frame - is that really necessary?
Even though it doesn’t take 5 seconds every time, after skimming the code it seems to do lots of calls to Windows functions (possibly causing syscalls) and several memory allocations, so it looks kinda “expensive”?

It should only be calling that if SDL_HIDAPI_discovery.m_bHaveDevicesChanged is true, and then resetting it to false once it has run.

It would be useful to understand why this value continues to be true, as it should only change on startup, or if a USB device was plugged in or disconnected. Can you maybe set a watchpoint on that variable to have the debugger break when it changes?

(there are two ways Windows should set this: in a window event handler that only fires on USB device changes, or in a manual detection every few seconds, which is only done if that handler failed to install for some arbitrary and unlikely win32 API error condition. The variable SDL_HIDAPI_discovery.m_bCanGetNotifications will tell you if it fell back to the manual detection method.)

1 Like

Ok, you’re right, it doesn’t happen every frame, but every 3 seconds or so (when stopping and continuing in the debugger 3 seconds are over fast) - though it feels like when it happens I have to continue multiple times before not happening for 3 seconds - it’s so fun that VS opens some dialog window when a data breakpoint is hit :-/

Anyway, it’s called a lot, and every once in a while it takes super long

But what is causing SDL_HIDAPI_discovery.m_bHaveDevicesChanged to set? And is SDL_HIDAPI_discovery.m_bCanGetNotifications set or not?

SDL_HIDAPI_discovery.m_bHaveDevicesChanged is set by HIDAPI_UpdateDiscovery(), because 3000ms have passed.
SDL_HIDAPI_discovery.m_bCanGetNotifications is false

This is the thing to fix. Can you step into HIDAPI_InitializeDiscovery() and see why this fails in the __WIN32__ section? If this starts working, you won’t get those stalls any more.

It’s not called - possibly because I don’t even enable joysticks (SDL_INIT_JOYSTICK) in dhewm3?
(I could have realized that this is all joystick code and I don’t even enable that before… :-/)

So a possible fix is to modify SDL_PumpEvents() to only call SDL_JoystickUpdate() if SDL_WasInit(SDL_INIT_JOYSTICK) (in addition to the other conditions)?

UPDATE: Seems to work (and so does adding SDL_INIT_JOYSTICK to my SDL_Init() call).
BTW, I wonder if the equivalent should be done for the SDL_SensorUpdate() call in SDL_PumpEvents() as well

Definitely put this in Bugzilla and I’ll follow up.

Thanks!

1 Like

Is this https://bugzilla.libsdl.org/show_bug.cgi?id=4389 ?

1 Like

sounds very similar at least

I posted the suggested fix (check SDL_WasInit(SDL_INIT_JOYSTICK) before calling SDL_JoystickUpdate()) in that bugzilla post.

Thanks for helping me debug this, @icculus! :slight_smile:

Ok, turns out that https://bugzilla.libsdl.org/show_bug.cgi?id=4389 is a different problem (there the game locks up permanently and the machine needs to be rebooted for it to even work again and also it’s Win10 while here it’s Win7).

I created https://bugzilla.libsdl.org/show_bug.cgi?id=4391 for my problem.

1 Like