[10/11] wineesd.drv: Hardcode dwSleepTime to 8ms as longer values cause problems with the Unix socket after a while.
Francois Gouget
fgouget at codeweavers.com
Sat Mar 28 06:15:28 CDT 2009
---
I'm not sure this patch should be applied, but it does help playback on
my SUSE 10.3 and Ubuntu 8.10 VMs. I don't have this problem on Debian on
bare hardware.
After a while (50 seconds to over 3 minutes), I'm seeing that
wodPlayer_WriteMaxFrags() cannot send data for extended periods of time.
What happens is that the write() call returns -1 + EAGAIN repeatedly,
so that by the time it works again we've had a buffer underrun :-(
Here's a normal portion of the log. Here we expect the socket buffer to
drain in 90ms, so the interval between two successful writes makes
sense:
trace:wave:wodPlayer_WriteMaxFrags Wrote 10800 bytes out of 10800, 76ms since last
trace:wave:wodPlayer_WriteMaxFrags write(58800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(58800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(58800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags Wrote 16000 bytes out of 58800, 72ms since last
trace:wave:wodPlayer_WriteMaxFrags write(42800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(42800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags Wrote 16000 bytes out of 42800, 108ms since last
trace:wave:wodPlayer_WriteMaxFrags write(26800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags Wrote 16000 bytes out of 26800, 74ms since last
But after 40 seconds where everything was fine, suddenly we see writes
succeed only once every 300ms, causing underruns:
trace:wave:wodPlayer_WriteMaxFrags Wrote 16000 bytes out of 42800, 440ms since last
trace:wave:wodPlayer_WriteMaxFrags write(26800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(26800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(26800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(26800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(26800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags Wrote 16000 bytes out of 26800, 233ms since last
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags write(10800) failed, errno=11
trace:wave:wodPlayer_WriteMaxFrags Wrote 10800 bytes out of 10800, 380ms since last
As you can see, it's not that we're sleeping for too long: we sometimes
try 8 times before anything goes through. I suspect it's a Linux kernel
optimisation that has to do with trying to minimise context switches or
some such (remember this is a Unix socket).
Trying to write once every 8ms (instead of once every ~30ms) cures this.
It may be because the ratio of failed to successful writes is about the
same, which means we get successful writes once every ~85ms instead of
once every ~350ms.
It would be nice to have an explanation for this behavior though,
especially as it may lead to a better fix. A lead may be SO_SNDLOWAT,
though I thought it was not supported.
Some notes on the test systems:
* The SUSE 10.3 VM has a real EsounD server and a 2.6.22 kernel.
* The Ubuntu 8.10 VM uses PulseAudio in EsounD emulation mode and a
2.6.27 kernel.
* The Debian system runs on real hardware with a real EsounD server and
has a 2.6.25 kernel.
* I don't see this problem on my Solaris 10u5 VM with a real EsounD
server.
dlls/wineesd.drv/audio.c | 9 ++++++---
1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/dlls/wineesd.drv/audio.c b/dlls/wineesd.drv/audio.c
index 283466e..b56dea2 100644
--- a/dlls/wineesd.drv/audio.c
+++ b/dlls/wineesd.drv/audio.c
@@ -1193,10 +1193,13 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
}
TRACE("dwLatency = %ums\n", wwo->dwLatency);
- /* ESD_BUF_SIZE is the socket buffer size in samples. Set dwSleepTime
- * to a fraction of that so it never get empty.
+ /* We could try to base dwSleepTime off ESD_BUF_SIZE. However it appears
+ * that on Linux if we try to write to a Unix socket every 9ms or less
+ * frequently we end up in a situation where it returns EAGAIN for
+ * extended periods of time, causing underruns. Setting dwSleepTime to
+ * 8ms works around that.
*/
- wwo->dwSleepTime = 1000 * ESD_BUF_SIZE / out_rate / 3;
+ wwo->dwSleepTime = 8;
/* Set the stream socket to O_NONBLOCK, so we can stop playing smoothly */
mode = fcntl(wwo->stream_fd, F_GETFL);
--
1.6.2
More information about the wine-patches
mailing list