[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