On Thu, Dec 01, 2016 at 12:27:43PM -0200, Bruno Jesus wrote:
On Thu, Dec 1, 2016 at 12:11 PM, Andrew Eikum aeikum@codeweavers.com wrote:
This seems like something you should be able to write a test for. I'm also curious how this manages to work on Windows >= XP
The test is at [1], if you run in Win9x it will fail (Win9x uses different thread), while running at >= XP it will succeed (>=XP uses same thread). This works in XP due to a shim that blindly ignores the SuspendThread/ResumeThread calls when inside a WinMM callback.
[1] http://source.winehq.org/source/dlls/winmm/tests/wave.c#0570
See, I told you we could write a test for it. I already did ;)
I'm still not sure exactly how this shim works. Is that something we can replicate in Wine instead of this threaded solution? Requiring users to switch the Windows version isn't ideal. Does this game require you to set 9x compatibility mode on Windows?
You need to be more careful about thread shutdown. See WINMM_StartDevicesThread and WINMM_DevicesThreadProc. I think you could use the same logic, including WINMM_DevicesThreadDone, to determine when to quit your new thread.
I tried understand what you say but I'm not sure yet. WINMM_DeleteWaveform is only called on process detach, can the DLL live after that?
The issue is that the callback thread may still be running after you exit DllMain and the DLL is unloaded on a different thread, which will cause a crash.
Because you can't use the Wait functions during DllMain, you need to ensure no winmm code is being run on any thread *before* PROCESS_DETACH is called. This means your thread needs to hold a reference to the module to ensure it isn't unloaded. However, some apps require winmm to be able to unload, so the thread can't just hold the reference forever. Since your callback thread will only do useful work if devices are opened (right?), you can use WINMM_DevicesThreadDone to determine when to quit without depending on Wait in DllMain. Finally, FreeLibraryAndExitThread allows you to exit winmm code permanently in order to free the reference safely, in case your thread is the last reference holder.
Note that this all means you'll need to start the callback thread dynamically instead of just once. Since your thread and the existing devices thread have the same lifespans (right?), I think you can just piggyback on WINMM_StartDevicesThread and start both threads in there.
Also, double-check that g_devthread_token is incremented in all instances where your callback thread may be used while no devices exist. This situation might never occur, but double-check to make sure.
I thought we had a bug about this, but I can't find it. See commit 2d76befbddc798f02812fe48a12ab2b80d25181b.
Hope that makes sense, it took a while to wrap my head around it when I wrote that commit...
Andrew