Ken Thomases : winecoreaudio: Avoid a race between closing and opening the waveIn device.

Alexandre Julliard julliard at winehq.org
Tue Apr 14 15:59:59 CDT 2009


Module: wine
Branch: master
Commit: dff10db04435c651473615182708f13cbe8677fb
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=dff10db04435c651473615182708f13cbe8677fb

Author: Ken Thomases <ken at codeweavers.com>
Date:   Sat Apr 11 07:18:30 2009 -0500

winecoreaudio: Avoid a race between closing and opening the waveIn device.

---

 dlls/winecoreaudio.drv/audio.c |   70 +++++++++++++++++++++++++---------------
 1 files changed, 44 insertions(+), 26 deletions(-)

diff --git a/dlls/winecoreaudio.drv/audio.c b/dlls/winecoreaudio.drv/audio.c
index f3a63a4..10b66d0 100644
--- a/dlls/winecoreaudio.drv/audio.c
+++ b/dlls/winecoreaudio.drv/audio.c
@@ -1983,6 +1983,7 @@ static DWORD widClose(WORD wDevID)
 {
     DWORD           ret = MMSYSERR_NOERROR;
     WINE_WAVEIN*    wwi;
+    OSStatus        err;
 
     TRACE("(%u);\n", wDevID);
 
@@ -1994,7 +1995,7 @@ static DWORD widClose(WORD wDevID)
 
     wwi = &WInDev[wDevID];
     OSSpinLockLock(&wwi->lock);
-    if (wwi->state == WINE_WS_CLOSED)
+    if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
     {
         WARN("Device already closed.\n");
         ret = MMSYSERR_INVALHANDLE;
@@ -2006,36 +2007,50 @@ static DWORD widClose(WORD wDevID)
     }
     else
     {
-        wwi->state = WINE_WS_CLOSED;
+        wwi->state = WINE_WS_CLOSING;
     }
 
     OSSpinLockUnlock(&wwi->lock);
 
-    if (ret == MMSYSERR_NOERROR)
-    {
-        OSStatus err = AudioUnitUninitialize(wwi->audioUnit);
-        if (err)
-        {
-            ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
-                                                           (char) (err >> 16),
-                                                           (char) (err >> 8),
-                                                           (char) err);
-        }
+    if (ret != MMSYSERR_NOERROR)
+        return ret;
 
-        if (!AudioUnit_CloseAudioUnit(wwi->audioUnit))
-        {
-            ERR("Can't close AudioUnit\n");
-        }
 
-        /* Dellocate our audio buffers */
-        widHelper_DestroyAudioBufferList(wwi->bufferList);
-        wwi->bufferList = NULL;
-        HeapFree(GetProcessHeap(), 0, wwi->bufferListCopy);
-        wwi->bufferListCopy = NULL;
+    /* Clean up and close the audio unit.  This has to be done without
+     * wwi->lock being held to avoid deadlock.  AudioUnitUninitialize will
+     * grab an internal Core Audio lock while waiting for the device work
+     * thread to exit.  Meanwhile the device work thread may be holding
+     * that lock and trying to grab the wwi->lock in the callback. */
+    err = AudioUnitUninitialize(wwi->audioUnit);
+    if (err)
+    {
+        ERR("AudioUnitUninitialize return %c%c%c%c\n", (char) (err >> 24),
+                                                       (char) (err >> 16),
+                                                       (char) (err >> 8),
+                                                       (char) err);
+    }
 
-        ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
+    if (!AudioUnit_CloseAudioUnit(wwi->audioUnit))
+    {
+        ERR("Can't close AudioUnit\n");
     }
 
+
+    OSSpinLockLock(&wwi->lock);
+    assert(wwi->state == WINE_WS_CLOSING);
+
+    /* Dellocate our audio buffers */
+    widHelper_DestroyAudioBufferList(wwi->bufferList);
+    wwi->bufferList = NULL;
+    HeapFree(GetProcessHeap(), 0, wwi->bufferListCopy);
+    wwi->bufferListCopy = NULL;
+
+    wwi->audioUnit = NULL;
+    wwi->state = WINE_WS_CLOSED;
+    OSSpinLockUnlock(&wwi->lock);
+
+    ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
+
     return ret;
 }
 
@@ -2069,7 +2084,7 @@ static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
     wwi = &WInDev[wDevID];
     OSSpinLockLock(&wwi->lock);
 
-    if (wwi->state == WINE_WS_CLOSED)
+    if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
     {
         WARN("Trying to add buffer to closed device.\n");
         ret = MMSYSERR_INVALHANDLE;
@@ -2121,7 +2136,7 @@ static DWORD widStart(WORD wDevID)
     wwi = &WInDev[wDevID];
     OSSpinLockLock(&wwi->lock);
 
-    if (wwi->state == WINE_WS_CLOSED)
+    if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
     {
         WARN("Trying to start closed device.\n");
         ret = MMSYSERR_INVALHANDLE;
@@ -2180,7 +2195,7 @@ static DWORD widStop(WORD wDevID)
 
     OSSpinLockLock(&wwi->lock);
 
-    if (wwi->state == WINE_WS_CLOSED)
+    if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
     {
         WARN("Trying to stop closed device.\n");
         ret = MMSYSERR_INVALHANDLE;
@@ -2253,7 +2268,7 @@ static DWORD widReset(WORD wDevID)
     wwi = &WInDev[wDevID];
     OSSpinLockLock(&wwi->lock);
 
-    if (wwi->state == WINE_WS_CLOSED)
+    if (wwi->state == WINE_WS_CLOSED || wwi->state == WINE_WS_CLOSING)
     {
         WARN("Trying to reset a closed device.\n");
         ret = MMSYSERR_INVALHANDLE;
@@ -2441,6 +2456,9 @@ OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
 
     lpStorePtr = wwi->lpQueuePtr;
 
+    /* We might have been called while the waveIn device is being closed in
+     * widClose.  We have to do nothing in that case.  The check of wwi->state
+     * below ensures that. */
     while (dataToStore > 0 && wwi->state == WINE_WS_PLAYING && lpStorePtr)
     {
         unsigned int room = lpStorePtr->dwBufferLength - lpStorePtr->dwBytesRecorded;




More information about the wine-cvs mailing list