http://bugs.winehq.org/show_bug.cgi?id=28464
Summary: winmm causes DSOUND_callback Wave queue corrupted Product: Wine Version: 1.3.25 Platform: x86 OS/Version: All Status: UNCONFIRMED Severity: normal Priority: P2 Component: winmm&mci AssignedTo: wine-bugs@winehq.org ReportedBy: hoehle@users.sourceforge.net
WHDR_DONE should only be set immediately before invoking the DriverCallback, because some apps poll for that flag and don't depend on notifications or callbacks.
WOD_MarkDoneHeaders must find another way to isolate done headers.
Because the queue of done headers is not isolated properly, attachment #36494 to bug #28056 exhibits a log (around line 4250) where 2 threads, namely the app calling waveOutWrite and the winmm feeder thread both grab the same list and send the same notifications.
0034:trace:driver:DriverCallback (, 32bit function 0003,, 03BD,, 017D3A90, 00000000) 0036:trace:winmm:WINMM_BeginPlaying ... 0036:trace:oss:AudioRenderClient_ReleaseBuffer (0x17d34f0)->(512, 0) 0034:trace:driver:DriverCallback (, 32bit function 0003,, 03BD,, 017D3AB0, 00000000) 0034:trace:driver:DriverCallback (, 32bit function 0003,, 03BD,, 017D3AD0, 00000000) 0034:trace:winmm:WOD_PushData (0xc000) 0036:trace:driver:DriverCallback (, 32bit function 0003,, 03BD,, 017D3AB0, 00000000) 0036:trace:driver:DriverCallback (, 32bit function 0003,, 03BD,, 017D3AD0, 00000000)
What happens is that the first thread removes the first header from device>first. At the time the second thread kicks in, apparently more headers are marked done. But since they are all part of the same linked queue, the later ones trigger 2 notifications each.
err:dsound:DSOUND_callback Wave queue corrupted! is the result of getting notifications twice.
Nice race condition!
BTW, but that's another issue, the code says "NotifyClient should never be called while holding the device lock". However that's exactly what can happen via waveOutWrite -> WINMM_ValidateAndLock -> WINMM_BeginPlaying -> WOD_PushData -> NotifyClient. I suggest a per device slot updated via InterlockedExchangePointer holding the queue of pending notifications. The top-level wave* commands (and the winmm feeder thread too) would then drain that queue. (The feeder thread could even delegate that to another (timer?) thread.) Alternatively, Push/PullData return that queue.