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 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, ¶m)) { + while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m, &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; }