audio.c converted from WaitForSingleObject to poll

eric pouech eric.pouech at wanadoo.fr
Thu Nov 1 04:28:59 CST 2001


> What I've done instead is reversed my poll patch and
> added a patch to mmsystem.c which causes application
> (and possibly some non-application) callbacks to be
> called from a different thread so that
> wodPlayer_Notify doesn't ever halt waiting for the
> application to return (which may in turn be waiting on
> wodReset,wodRestart, wodClose, wodPause etc).  I'll
> post this to wine-patches in just a moment.
well, I think it's a bit overkill. furthermore, this won't
let synchronize that the notification has been done before
returning from the call. this stops waiting in notify, but
doesn't synchronize at the end (except if you wrap every
entry point in winmm/mmsys which might generate a 
notification with a synchro with this extra thread)

I think we should not use a general event for the synchro 
in audio.c, but rather create a new event for each time we 
need to wait (wodReset...) and pass it to the WWO_MSG struct, 
hence removing the race condition. does the attached patch
solves your issue ?

> With the audio.c poll patch I posted, wodPlayer_Notify
>  doesn't update the total bytes played until after
> sufficient time has elapsed for the dsp to have
> finished playing it.  The absence of more data to send
> to the dsp may stop the polling loop before all sounds
> have been played, meaning that the total bytes for a
> the last sample written may not be updated until the
> next wave is queued to be written to the dsp.
> It appears that HL loops checking wodGetPosition until
> the last sound has been played before writing the next
> sound.  This never happens with my poll patch because
> of the issue in the previous para.  Hence: no sound.
> I verified this by doing another evil hack which just
> notified and updated total bytes as soon as they were
> written.  The sound returned.
ok (I didn't get it was with the poll patch applied)
but OTOH, the poll patch can have nasty effects:
1/ let's assume an app uses only two wavehdr:s which cumuled
   size if smaller than the OSS play ring
2/ the app will send the two wavehdr:s and they will be queued
   and written to /dev/dsp
3/ if we use the modification to not have the busy wait, then
   no notification will be ever made (IIRC some Flash apps are
   doing this). They normally wait for notification to occur
   in order to resubmit the same wavehdr:s will different 
   values

so, I think the poll patch (even with some modifications) 
introduces some more evil parts than it fixes (and your deadlock
issues don't come from the playback loop)

A+
-- 
---------------
Eric Pouech (http://perso.wanadoo.fr/eric.pouech/)
"The future will be better tomorrow", Vice President Dan Quayle
-------------- next part --------------
Index: dlls/winmm/wineoss/audio.c
===================================================================
RCS file: /usr/share/cvs/cvsroot/wine/wine/dlls/winmm/wineoss/audio.c,v
retrieving revision 1.44
diff -u -r1.44 audio.c
--- dlls/winmm/wineoss/audio.c	2001/08/18 16:09:41	1.44
+++ dlls/winmm/wineoss/audio.c	2001/11/01 10:19:52
@@ -87,6 +87,7 @@
 typedef struct {
     int msg;
     DWORD param;
+    HANDLE			hEvent;
 } WWO_MSG;
 
 typedef struct {
@@ -109,9 +110,9 @@
     DWORD			dwRemain;		/* number of bytes to write to end the current fragment  */
 
     /* synchronization stuff */
+    HANDLE			hStartUpEvent;
     HANDLE			hThread;
     DWORD			dwThreadID;
-    HANDLE			hEvent;
 #define WWO_RING_BUFFER_SIZE	30
     WWO_MSG			messages[WWO_RING_BUFFER_SIZE];
     int				msg_tosave;
@@ -522,29 +523,40 @@
 }
 
 
-int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param)
+static int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param, BOOL wait)
 {
+    HANDLE	hEvent;
+
     EnterCriticalSection(&wwo->msg_crst);
     if ((wwo->msg_tosave == wwo->msg_toget) /* buffer overflow ? */
-    &&  (wwo->messages[wwo->msg_toget].msg))
+	&& (wwo->messages[wwo->msg_toget].msg))
     {
 	ERR("buffer overflow !?\n");
         LeaveCriticalSection(&wwo->msg_crst);
 	return 0;
     }
 
+    hEvent = wait ? CreateEventA(NULL, FALSE, FALSE, NULL) : INVALID_HANDLE_VALUE;
+
     wwo->messages[wwo->msg_tosave].msg = msg;
     wwo->messages[wwo->msg_tosave].param = param;
+    wwo->messages[wwo->msg_tosave].hEvent = hEvent;
+
     wwo->msg_tosave++;
     if (wwo->msg_tosave > WWO_RING_BUFFER_SIZE-1)
 	wwo->msg_tosave = 0;
     LeaveCriticalSection(&wwo->msg_crst);
     /* signal a new message */
     SetEvent(wwo->msg_event);
+    if (wait)
+    {
+	    WaitForSingleObject(hEvent, INFINITE);
+	    CloseHandle(hEvent);
+    }
     return 1;
 }
 
-int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param)
+static int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param, HANDLE *hEvent)
 {
     EnterCriticalSection(&wwo->msg_crst);
 
@@ -557,6 +569,7 @@
     *msg = wwo->messages[wwo->msg_toget].msg;
     wwo->messages[wwo->msg_toget].msg = 0;
     *param = wwo->messages[wwo->msg_toget].param;
+    *hEvent = wwo->messages[wwo->msg_toget].hEvent;
     wwo->msg_toget++;
     if (wwo->msg_toget > WWO_RING_BUFFER_SIZE-1)
 	wwo->msg_toget = 0;
@@ -642,6 +655,7 @@
     DWORD		param;
     DWORD		tc;
     BOOL                had_msg;
+    HANDLE		ev;
 
     wwo->state = WINE_WS_STOPPED;
 
@@ -652,7 +666,7 @@
     wwo->dwPlayedTotal = 0;
 
     TRACE("imhere[0]\n");
-    SetEvent(wwo->hEvent);
+    SetEvent(wwo->hStartUpEvent);
 
     for (;;) {
 	/* wait for dwSleepTime or an event in thread's queue
@@ -684,17 +698,17 @@
 	TRACE("imhere[2] (q=%p p=%p) tc = %08lx\n", wwo->lpQueuePtr,
 	      wwo->lpPlayPtr, GetTickCount());
 	had_msg = FALSE;
-	while (wodPlayer_RetrieveMessage(wwo, &msg, &param)) {
+	while (wodPlayer_RetrieveMessage(wwo, &msg, &param, &ev)) {
 	    had_msg = TRUE;
 	    switch (msg) {
 	    case WINE_WM_PAUSING:
 		wodPlayer_Reset(wwo, uDevID, FALSE);
 		wwo->state = WINE_WS_PAUSED;
-		SetEvent(wwo->hEvent);
+		SetEvent(ev);
 		break;
 	    case WINE_WM_RESTARTING:
 		wwo->state = WINE_WS_PLAYING;
-		SetEvent(wwo->hEvent);
+		SetEvent(ev);
 		break;
 	    case WINE_WM_HEADER:
 		lpWaveHdr = (LPWAVEHDR)param;
@@ -711,14 +725,14 @@
 		break;
 	    case WINE_WM_RESETTING:
 		wodPlayer_Reset(wwo, uDevID, TRUE);
-		SetEvent(wwo->hEvent);
+		SetEvent(ev);
 		break;
 	    case WINE_WM_CLOSING:
 		/* sanity check: this should not happen since the device must have been reset before */
 		if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
 		wwo->hThread = 0;
 		wwo->state = WINE_WS_CLOSED;
-		SetEvent(wwo->hEvent);
+		SetEvent(ev);
 		ExitThread(0);
 		/* shouldn't go here */
 	    default:
@@ -895,14 +909,15 @@
     InitializeCriticalSection(&wwo->msg_crst);
 
     if (!(dwFlags & WAVE_DIRECTSOUND)) {
-	wwo->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+	wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
 	wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
-	WaitForSingleObject(wwo->hEvent, INFINITE);
+	WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
+	CloseHandle(wwo->hStartUpEvent);
     } else {
-	wwo->hEvent = INVALID_HANDLE_VALUE;
 	wwo->hThread = INVALID_HANDLE_VALUE;
 	wwo->dwThreadID = 0;
     }
+    wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
 
     TRACE("fd=%d fragmentSize=%ld\n", 
 	  wwo->unixdev, wwo->dwFragmentSize);
@@ -942,10 +957,8 @@
 	ret = WAVERR_STILLPLAYING;
     } else {
 	TRACE("imhere[3-close]\n");
-	if (wwo->hEvent != INVALID_HANDLE_VALUE) {
-	    wodPlayer_Message(wwo, WINE_WM_CLOSING, 0);
-	    WaitForSingleObject(wwo->hEvent, INFINITE);
-	    CloseHandle(wwo->hEvent);
+	if (wwo->hThread != INVALID_HANDLE_VALUE) {
+	    wodPlayer_Message(wwo, WINE_WM_CLOSING, 0, TRUE);
 	}
 	if (wwo->mapping) {
 	    munmap(wwo->mapping, wwo->maplen);
@@ -988,7 +1001,7 @@
     lpWaveHdr->lpNext = 0;
 
     TRACE("imhere[3-HEADER]\n");
-    wodPlayer_Message(&WOutDev[wDevID], WINE_WM_HEADER, (DWORD)lpWaveHdr);
+    wodPlayer_Message(&WOutDev[wDevID], WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
 
     return MMSYSERR_NOERROR;
 }
@@ -1047,8 +1060,7 @@
     }
     
     TRACE("imhere[3-PAUSING]\n");
-    wodPlayer_Message(&WOutDev[wDevID], WINE_WM_PAUSING, 0);
-    WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
+    wodPlayer_Message(&WOutDev[wDevID], WINE_WM_PAUSING, 0, TRUE);
     
     return MMSYSERR_NOERROR;
 }
@@ -1067,8 +1079,7 @@
     
     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
 	TRACE("imhere[3-RESTARTING]\n");
-	wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESTARTING, 0);
-	WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
+	wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESTARTING, 0, TRUE);
     }
     
     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
@@ -1095,8 +1106,7 @@
     }
     
     TRACE("imhere[3-RESET]\n");
-    wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESETTING, 0);
-    WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
+    wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESETTING, 0, TRUE);
     
     return MMSYSERR_NOERROR;
 }


More information about the wine-devel mailing list