this patch fixes another bunch of synchronisation issues in playsound function family this should help the evolution of the function to add some unimplemented features (like several playsound in //, differenciation of SND_NOWAIT and SND_NOSTOP flags) PS1: thanks to Chris Rankin for the good bug reports and the testing of the various solutions PS2: this should also take care, in a different way, of the issue reported today by Michael Karcher A+
Name: winmm_ps ChangeLog: some other synchronisation issues License: X11 GenDate: 2002/05/28 19:27:34 UTC ModifiedFiles: dlls/winmm/mmsystem.c dlls/winmm/winemm.h AddedFiles: =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/winmm/mmsystem.c,v retrieving revision 1.54 diff -u -u -r1.54 mmsystem.c --- dlls/winmm/mmsystem.c 22 May 2002 01:52:31 -0000 1.54 +++ dlls/winmm/mmsystem.c 26 May 2002 19:30:03 -0000 @@ -93,6 +93,9 @@ iData->lpNextIData = lpFirstIData; lpFirstIData = iData; InitializeCriticalSection(&iData->cs); + iData->cs.DebugInfo = (void*)__FILE__ ": WinMM"; + iData->psStopEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + iData->psLastEvent = CreateEventA(NULL, TRUE, FALSE, NULL); TRACE("Created IData (%p) for pid %08lx\n", iData, iData->dwThisProcess); return TRUE; } @@ -116,6 +119,9 @@ } /* FIXME: should also free content and resources allocated * inside iData */ + CloseHandle(iData->psStopEvent); + CloseHandle(iData->psLastEvent); + DeleteCriticalSection(&iData->cs); HeapFree(GetProcessHeap(), 0, iData); } } @@ -245,17 +251,12 @@ static WCHAR wszSounds[] = {'S','o','u','n','d','s',0}; static WCHAR wszDefault[] = {'D','e','f','a','u','l','t',0}; - static WCHAR wszKey[] = {'A','p','p','E','v','e','n','t','s','\\','\\', - 'S','c','h','e','m','e','s','\\','\\', + static WCHAR wszKey[] = {'A','p','p','E','v','e','n','t','s','\\', + 'S','c','h','e','m','e','s','\\', 'A','p','p','s',0}; static WCHAR wszDotDefault[] = {'.','D','e','f','a','u','l','t',0}; static WCHAR wszNull[] = {0}; - /* FIXME: we should also look up the registry under - * HKCU\AppEvents\Schemes\Apps\.Default - * HKCU\AppEvents\Schemes\Apps\<AppName> - */ - TRACE("searching in SystemSound list for %s\n", debugstr_w(lpszName)); GetProfileStringW(wszSounds, (LPWSTR)lpszName, wszNull, str, sizeof(str)/sizeof(str[0])); if (lstrlenW(str) == 0) @@ -269,14 +270,16 @@ hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); if (hmmio != 0) return hmmio; next: + /* we look up the registry under + * HKCU\AppEvents\Schemes\Apps\.Default + * HKCU\AppEvents\Schemes\Apps\<AppName> + */ if (RegOpenKeyW(HKEY_CURRENT_USER, wszKey, &hRegSnd) != 0) goto none; if (uFlags & SND_APPLICATION) { - err = 1; + err = 1; /* error */ if (GetModuleFileNameW(0, str, sizeof(str)/sizeof(str[0]))) { - LPWSTR ptr; - for (ptr = str + lstrlenW(str) - 1; ptr >= str; ptr--) { if (*ptr == '.') *ptr = 0; @@ -303,7 +306,7 @@ count = sizeof(str)/sizeof(str[0]); err = RegQueryValueExW(hSnd, NULL, 0, &type, (LPBYTE)str, &count); RegCloseKey(hSnd); - if (err != 0) goto none; + if (err != 0 || !*str) goto none; hmmio = mmioOpenW(str, NULL, MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); if (hmmio) return hmmio; none: @@ -348,6 +351,77 @@ } } +static BOOL PlaySound_IsString(DWORD fdwSound, const void* psz) +{ + /* SND_RESOURCE is 0x40004 while + * SND_MEMORY is 0x00004 + */ + switch (fdwSound & (SND_RESOURCE|SND_ALIAS|SND_FILENAME)) + { + case SND_RESOURCE: return HIWORD(psz) != 0; /* by name or by ID ? */ + case SND_MEMORY: return FALSE; + case SND_ALIAS: /* what about ALIAS_ID ??? */ + case SND_FILENAME: + case 0: return TRUE; + default: FIXME("WTF\n"); return FALSE; + } +} + +static void PlaySound_Free(WINE_PLAYSOUND* wps) +{ + LPWINE_MM_IDATA iData = MULTIMEDIA_GetIData(); + WINE_PLAYSOUND** p; + + EnterCriticalSection(&iData->cs); + for (p = &iData->lpPlaySound; *p && *p != wps; p = &((*p)->lpNext)); + if (*p) *p = (*p)->lpNext; + if (iData->lpPlaySound == NULL) SetEvent(iData->psLastEvent); + LeaveCriticalSection(&iData->cs); + if (wps->bAlloc) HeapFree(GetProcessHeap(), 0, (void*)wps->pszSound); + HeapFree(GetProcessHeap(), 0, wps); +} + +static WINE_PLAYSOUND* PlaySound_Alloc(const void* pszSound, HMODULE hmod, + DWORD fdwSound, BOOL bUnicode) +{ + WINE_PLAYSOUND* wps; + + wps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wps)); + if (!wps) return NULL; + + wps->hMod = hmod; + wps->fdwSound = fdwSound; + if (PlaySound_IsString(fdwSound, pszSound)) + { + if (bUnicode) + { + if (fdwSound & SND_ASYNC) + { + wps->pszSound = HeapAlloc(GetProcessHeap(), 0, + (lstrlenW(pszSound)+1) * sizeof(WCHAR)); + if (!wps->pszSound) goto oom_error; + lstrcpyW((LPWSTR)wps->pszSound, pszSound); + wps->bAlloc = TRUE; + } + else + wps->pszSound = pszSound; + } + else + { + wps->pszSound = HEAP_strdupAtoW(GetProcessHeap(), 0, pszSound); + if (!wps->pszSound) goto oom_error; + wps->bAlloc = TRUE; + } + } + else + wps->pszSound = pszSound; + + return wps; + oom_error: + PlaySound_Free(wps); + return NULL; +} + static DWORD WINAPI proc_PlaySound(LPVOID arg) { WINE_PLAYSOUND* wps = (WINE_PLAYSOUND*)arg; @@ -385,7 +459,6 @@ data = (void*)wps->pszSound; /* construct an MMIO stream (either in memory, or from a file */ -/* hmmio = 0; */ /* to catch errors */ if (wps->fdwSound & SND_MEMORY) { /* NOTE: SND_RESOURCE has the SND_MEMORY bit set */ MMIOINFO mminfo; @@ -482,9 +555,11 @@ s.dwEventCount = 1L; /* for first buffer */ mmioSeek(hmmio, mmckInfo.dwDataOffset, SEEK_SET); - while (left) { - if (wps->bStop) { - wps->bStop = wps->bLoop = FALSE; + while (left) + { + if (WaitForSingleObject(iData->psStopEvent, 0) == WAIT_OBJECT_0) + { + wps->bLoop = FALSE; break; } count = mmioRead(hmmio, waveHdr[index].lpData, min(bufsize, left)); @@ -515,38 +590,15 @@ if (hWave) while (waveOutClose(hWave) == WAVERR_STILLPLAYING) Sleep(100); if (hmmio) mmioClose(hmmio, 0); - SetEvent(wps->hReadyEvent); - iData->lpPlaySound = NULL; - - if (wps->bAlloc) HeapFree(GetProcessHeap(), 0, (void*)wps->pszSound); - CloseHandle(wps->hReadyEvent); - HeapFree(GetProcessHeap(), 0, wps); + PlaySound_Free(wps); return bRet; } -static BOOL MULTIMEDIA_IsString(DWORD fdwSound, const void* psz) -{ - /* SND_RESOURCE is 0x40004 while - * SND_MEMORY is 0x00004 - */ - switch (fdwSound & (SND_RESOURCE|SND_ALIAS|SND_FILENAME)) - { - case SND_RESOURCE: return HIWORD(psz) != 0; /* by name or by ID ? */ - case SND_MEMORY: return FALSE; - case SND_ALIAS: /* what about ALIAS_ID ??? */ - case SND_FILENAME: - case 0: return TRUE; - default: FIXME("WTF\n"); return FALSE; - } -} - static BOOL MULTIMEDIA_PlaySound(const void* pszSound, HMODULE hmod, DWORD fdwSound, BOOL bUnicode) { WINE_PLAYSOUND* wps = NULL; - DWORD id; LPWINE_MM_IDATA iData = MULTIMEDIA_GetIData(); - BOOL bRet = FALSE; TRACE("pszSound='%p' hmod=%04X fdwSound=%08lX\n", pszSound, hmod, fdwSound); @@ -554,74 +606,52 @@ /* FIXME? I see no difference between SND_NOWAIT and SND_NOSTOP ! * there could be one if several sounds can be played at once... */ - if ((fdwSound & (SND_NOWAIT | SND_NOSTOP)) && iData->lpPlaySound) + if ((fdwSound & (SND_NOWAIT | SND_NOSTOP)) && iData->lpPlaySound != NULL) return FALSE; + + /* alloc internal structure, if we need to play something */ + if (pszSound && !(fdwSound & SND_PURGE)) + { + if (!(wps = PlaySound_Alloc(pszSound, hmod, fdwSound, bUnicode))) + return FALSE; + } - do { - HANDLE hEvt = 0; + EnterCriticalSection(&iData->cs); + /* since several threads can enter PlaySound in parallel, we're not + * sure, at this point, that another thread didn't start a new playsound + */ + while (iData->lpPlaySound != NULL) + { + ResetEvent(iData->psLastEvent); + /* FIXME: doc says we have to stop all instances of pszSound if it's non + * NULL... as of today, we stop all playing instances */ + SetEvent(iData->psStopEvent); - /* Trying to stop if playing */ - EnterCriticalSection(&iData->cs); - if (iData->lpPlaySound) { - LPWINE_PLAYSOUND ps2stop = iData->lpPlaySound; - - hEvt = ps2stop->hReadyEvent; - ps2stop->bStop = TRUE; - } LeaveCriticalSection(&iData->cs); - /* Waiting playing thread to get ready. I think 10 secs is ok & if not then leave - * FIXME: race here (if hEvt is destroyed and reallocated - as a handle - to - * another object)... unlikely but possible - */ - if (hEvt) WaitForSingleObject(hEvt, 1000*10); + WaitForSingleObject(iData->psLastEvent, INFINITE); + EnterCriticalSection(&iData->cs); - if (!pszSound || (fdwSound & SND_PURGE)) - return TRUE; /* We stopped playing so leaving */ + ResetEvent(iData->psStopEvent); + } - if (wps == NULL) - { - wps = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wps)); - if (!wps) return FALSE; - - wps->hMod = hmod; - wps->fdwSound = fdwSound; - wps->bAlloc = FALSE; - if (MULTIMEDIA_IsString(fdwSound, pszSound)) - { - if (bUnicode) - { - if (fdwSound & SND_ASYNC) - { - wps->pszSound = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(pszSound)+1) * sizeof(WCHAR)); - lstrcpyW((LPWSTR)wps->pszSound, pszSound); - wps->bAlloc = TRUE; - } - else - wps->pszSound = pszSound; - } - else - { - wps->pszSound = HEAP_strdupAtoW(GetProcessHeap(), 0, pszSound); - wps->bAlloc = TRUE; - } - } - else - wps->pszSound = pszSound; - if ((wps->hReadyEvent = CreateEventA(NULL, TRUE, FALSE, NULL)) == 0) - goto cleanup; - } - } while (InterlockedCompareExchangePointer((void**)&iData->lpPlaySound, wps, NULL) != NULL); + wps->lpNext = iData->lpPlaySound; + iData->lpPlaySound = wps; + LeaveCriticalSection(&iData->cs); + + if (!pszSound || (fdwSound & SND_PURGE)) return TRUE; if (fdwSound & SND_ASYNC) { - wps->bLoop = fdwSound & SND_LOOP; - /* FIXME: memory leak in case of error & cs is still locked */ - return ((wps->hThread = CreateThread(NULL, 0, proc_PlaySound, wps, 0, &id)) != 0); - } - - bRet = proc_PlaySound(wps); - cleanup: - return bRet; + DWORD id; + wps->bLoop = (fdwSound & SND_LOOP) ? TRUE : FALSE; + if (CreateThread(NULL, 0, proc_PlaySound, wps, 0, &id) != 0) + return TRUE; + } + else return proc_PlaySound(wps); + + /* error cases */ + PlaySound_Free(wps); + return FALSE; } /************************************************************************** Index: dlls/winmm/winemm.h =================================================================== RCS file: /home/cvs/cvsroot/wine/wine/dlls/winmm/winemm.h,v retrieving revision 1.12 diff -u -u -r1.12 winemm.h --- dlls/winmm/winemm.h 22 May 2002 01:52:31 -0000 1.12 +++ dlls/winmm/winemm.h 26 May 2002 17:24:37 -0000 @@ -156,14 +156,12 @@ } WINE_MMIO, *LPWINE_MMIO; typedef struct tagWINE_PLAYSOUND { - HANDLE hThread; - HANDLE hReadyEvent; - BOOL bStop; - LPCWSTR pszSound; - HMODULE hMod; - DWORD fdwSound; - BOOL bLoop; - BOOL bAlloc; + volatile BOOL bLoop : 1, + bAlloc : 1; + LPCWSTR pszSound; + HMODULE hMod; + DWORD fdwSound; + struct tagWINE_PLAYSOUND* lpNext; } WINE_PLAYSOUND, *LPWINE_PLAYSOUND; typedef struct tagWINE_MM_IDATA { @@ -190,7 +188,9 @@ /* mmio part */ LPWINE_MMIO lpMMIO; /* playsound and sndPlaySound */ - LPWINE_PLAYSOUND lpPlaySound; + WINE_PLAYSOUND* lpPlaySound; + HANDLE psLastEvent; + HANDLE psStopEvent; } WINE_MM_IDATA, *LPWINE_MM_IDATA; /* function prototypes */