[PATCH 2/2] winmm: Shutdown the devices thread when all devices are closed

Andrew Eikum aeikum at codeweavers.com
Mon Mar 25 14:46:41 CDT 2013


---
 dlls/winmm/waveform.c | 190 +++++++++++++++++++++++++++++---------------------
 1 file changed, 111 insertions(+), 79 deletions(-)

diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c
index 594d82c..de081e9 100644
--- a/dlls/winmm/waveform.c
+++ b/dlls/winmm/waveform.c
@@ -150,8 +150,6 @@ static WINMM_Device *g_in_mapper_devices[MAX_DEVICES];
 
 static IMMDeviceEnumerator *g_devenum;
 
-#define WINMM_WM_QUIT WM_USER
-
 static CRITICAL_SECTION g_devthread_lock;
 static CRITICAL_SECTION_DEBUG g_devthread_lock_debug =
 {
@@ -160,8 +158,10 @@ static CRITICAL_SECTION_DEBUG g_devthread_lock_debug =
       0, 0, { (DWORD_PTR)(__FILE__ ": g_devthread_lock") }
 };
 static CRITICAL_SECTION g_devthread_lock = { &g_devthread_lock_debug, -1, 0, 0, 0, 0 };
+static LONG g_devthread_token;
 static HANDLE g_devices_thread;
 static HWND g_devices_hwnd;
+static HMODULE g_devthread_module;
 
 static UINT g_devhandle_count;
 static HANDLE *g_device_handles;
@@ -200,64 +200,47 @@ void WINMM_DeleteWaveform(void)
 {
     UINT i, j;
 
-    if(g_devices_hwnd){
-        for(i = 0; i < g_outmmdevices_count; ++i){
-            WINMM_MMDevice *mmdevice = &g_out_mmdevices[i];
-            for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
-                WINMM_Device *device = mmdevice->devices[j];
-                SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)device->handle, 0);
-            }
-        }
-
-        for(i = 0; i < g_inmmdevices_count; ++i){
-            WINMM_MMDevice *mmdevice = &g_in_mmdevices[i];
-            for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
-                WINMM_Device *device = mmdevice->devices[j];
-                SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)device->handle, 0);
-            }
-        }
-
-        SendMessageW(g_devices_hwnd, WINMM_WM_QUIT, 0, 0);
-
-        for(i = 0; i < g_outmmdevices_count; ++i){
-            WINMM_MMDevice *mmdevice = &g_out_mmdevices[i];
+    if(g_devices_thread)
+        CloseHandle(g_devices_thread);
 
-            for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
-                WINMM_Device *device = mmdevice->devices[j];
-                if(device->handle)
-                    CloseHandle(device->handle);
-                DeleteCriticalSection(&device->lock);
-            }
+    for(i = 0; i < g_outmmdevices_count; ++i){
+        WINMM_MMDevice *mmdevice = &g_out_mmdevices[i];
 
-            if(mmdevice->volume)
-                ISimpleAudioVolume_Release(mmdevice->volume);
-            CoTaskMemFree(mmdevice->dev_id);
-            DeleteCriticalSection(&mmdevice->lock);
+        for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
+            WINMM_Device *device = mmdevice->devices[j];
+            if(device->handle)
+                CloseHandle(device->handle);
+            DeleteCriticalSection(&device->lock);
         }
 
-        for(i = 0; i < g_inmmdevices_count; ++i){
-            WINMM_MMDevice *mmdevice = &g_in_mmdevices[i];
+        if(mmdevice->volume)
+            ISimpleAudioVolume_Release(mmdevice->volume);
+        CoTaskMemFree(mmdevice->dev_id);
+        DeleteCriticalSection(&mmdevice->lock);
+    }
 
-            for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
-                WINMM_Device *device = mmdevice->devices[j];
-                if(device->handle)
-                    CloseHandle(device->handle);
-                DeleteCriticalSection(&device->lock);
-            }
+    for(i = 0; i < g_inmmdevices_count; ++i){
+        WINMM_MMDevice *mmdevice = &g_in_mmdevices[i];
 
-            if(mmdevice->volume)
-                ISimpleAudioVolume_Release(mmdevice->volume);
-            CoTaskMemFree(mmdevice->dev_id);
-            DeleteCriticalSection(&mmdevice->lock);
+        for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
+            WINMM_Device *device = mmdevice->devices[j];
+            if(device->handle)
+                CloseHandle(device->handle);
+            DeleteCriticalSection(&device->lock);
         }
 
-        HeapFree(GetProcessHeap(), 0, g_out_mmdevices);
-        HeapFree(GetProcessHeap(), 0, g_in_mmdevices);
-
-        HeapFree(GetProcessHeap(), 0, g_device_handles);
-        HeapFree(GetProcessHeap(), 0, g_handle_devices);
+        if(mmdevice->volume)
+            ISimpleAudioVolume_Release(mmdevice->volume);
+        CoTaskMemFree(mmdevice->dev_id);
+        DeleteCriticalSection(&mmdevice->lock);
     }
 
+    HeapFree(GetProcessHeap(), 0, g_out_mmdevices);
+    HeapFree(GetProcessHeap(), 0, g_in_mmdevices);
+
+    HeapFree(GetProcessHeap(), 0, g_device_handles);
+    HeapFree(GetProcessHeap(), 0, g_handle_devices);
+
     DeleteCriticalSection(&g_devthread_lock);
 }
 
@@ -2424,17 +2407,39 @@ static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
     case DRV_QUERYDEVICEINTERFACESIZE:
     case DRV_QUERYDEVICEINTERFACE:
         return DRV_QueryDeviceInterface((WINMM_QueryInterfaceInfo*)wparam);
-    case WINMM_WM_QUIT:
-        TRACE("QUIT message received\n");
-        DestroyWindow(g_devices_hwnd);
-        g_devices_hwnd = NULL;
-        IMMDeviceEnumerator_Release(g_devenum);
-        CoUninitialize();
-        return 0;
     }
     return DefWindowProcW(hwnd, msg, wparam, lparam);
 }
 
+static BOOL WINMM_DevicesThreadDone(void)
+{
+    UINT i;
+
+    EnterCriticalSection(&g_devthread_lock);
+
+    if(g_devthread_token > 0){
+        LeaveCriticalSection(&g_devthread_lock);
+        return FALSE;
+    }
+
+    for(i = 0; i < g_devhandle_count; ++i){
+        if(g_handle_devices[i]->open){
+            LeaveCriticalSection(&g_devthread_lock);
+            return FALSE;
+        }
+    }
+
+    DestroyWindow(g_devices_hwnd);
+    g_devices_hwnd = NULL;
+    IMMDeviceEnumerator_Release(g_devenum);
+    g_devenum = NULL;
+    CoUninitialize();
+
+    LeaveCriticalSection(&g_devthread_lock);
+
+    return TRUE;
+}
+
 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
 {
     HANDLE evt = arg;
@@ -2444,13 +2449,13 @@ static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
     if(FAILED(hr)){
         WARN("CoInitializeEx failed: %08x\n", hr);
-        return 1;
+        FreeLibraryAndExitThread(g_devthread_module, 1);
     }
 
     hr = WINMM_InitMMDevices();
     if(FAILED(hr)){
         CoUninitialize();
-        return 1;
+        FreeLibraryAndExitThread(g_devthread_module, 1);
     }
 
     hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
@@ -2458,16 +2463,15 @@ static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
     if(FAILED(hr)){
         WARN("CoCreateInstance failed: %08x\n", hr);
         CoUninitialize();
-        return 1;
+        FreeLibraryAndExitThread(g_devthread_module, 1);
     }
 
     g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
             HWND_MESSAGE, NULL, NULL, NULL);
     if(!g_devices_hwnd){
         WARN("CreateWindow failed: %d\n", GetLastError());
-        IMMDeviceEnumerator_Release(g_devenum);
         CoUninitialize();
-        return 1;
+        FreeLibraryAndExitThread(g_devthread_module, 1);
     }
 
     SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
@@ -2496,11 +2500,17 @@ static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
         }else
             WARN("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
                     GetLastError());
+        if(WINMM_DevicesThreadDone()){
+            TRACE("Quitting devices thread\n");
+            FreeLibraryAndExitThread(g_devthread_module, 0);
+        }
     }
 
-    return 0;
+    FreeLibraryAndExitThread(g_devthread_module, 0);
 }
 
+/* on success, increments g_devthread_token to prevent
+ * device thread shutdown. caller must decrement. */
 static BOOL WINMM_StartDevicesThread(void)
 {
     HANDLE events[2];
@@ -2508,25 +2518,36 @@ static BOOL WINMM_StartDevicesThread(void)
 
     EnterCriticalSection(&g_devthread_lock);
 
-    if(g_devices_thread){
-        DWORD wait;
-
+    if(g_devices_hwnd){
         wait = WaitForSingleObject(g_devices_thread, 0);
         if(wait == WAIT_TIMEOUT){
+            /* thread still running */
+            InterlockedIncrement(&g_devthread_token);
             LeaveCriticalSection(&g_devthread_lock);
             return TRUE;
         }
         if(wait != WAIT_OBJECT_0){
+            /* error */
             LeaveCriticalSection(&g_devthread_lock);
             return FALSE;
         }
-
-        g_devices_thread = NULL;
+        TRACE("Devices thread left dangling message window?\n");
         g_devices_hwnd = NULL;
+        CloseHandle(g_devices_thread);
+        g_devices_thread = NULL;
+    }else if(g_devices_thread){
+        WaitForSingleObject(g_devices_thread, INFINITE);
+        CloseHandle(g_devices_thread);
+        g_devices_thread = NULL;
     }
 
     TRACE("Starting up devices thread\n");
 
+    /* The devices thread holds a reference to the winmm module
+     * to prevent it from unloading while it's running. */
+    GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+            (const WCHAR *)&WINMM_StartDevicesThread, &g_devthread_module);
+
     events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
 
     g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
@@ -2534,6 +2555,7 @@ static BOOL WINMM_StartDevicesThread(void)
     if(!g_devices_thread){
         LeaveCriticalSection(&g_devthread_lock);
         CloseHandle(events[0]);
+        FreeLibrary(g_devthread_module);
         return FALSE;
     }
 
@@ -2550,6 +2572,8 @@ static BOOL WINMM_StartDevicesThread(void)
         return FALSE;
     }
 
+    InterlockedIncrement(&g_devthread_token);
+
     LeaveCriticalSection(&g_devthread_lock);
 
     return TRUE;
@@ -2708,9 +2732,6 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
     TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
             dwCallback, dwInstance, dwFlags);
 
-    if(!WINMM_StartDevicesThread())
-        return MMSYSERR_NODRIVER;
-
     if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
         return MMSYSERR_INVALPARAM;
 
@@ -2718,6 +2739,9 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
     if(res != MMSYSERR_NOERROR)
         return res;
 
+    if(!WINMM_StartDevicesThread())
+        return MMSYSERR_NODRIVER;
+
     info.handle = 0;
     info.format = (WAVEFORMATEX*)lpFormat;
     info.callback = dwCallback;
@@ -2727,6 +2751,7 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
     info.reset = TRUE;
 
     res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
+    InterlockedDecrement(&g_devthread_token);
     if(res != MMSYSERR_NOERROR || (dwFlags & WAVE_FORMAT_QUERY))
         return res;
 
@@ -3182,6 +3207,7 @@ static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
 static UINT get_device_interface(UINT msg, BOOL is_out, UINT index, WCHAR *out, ULONG *out_len)
 {
     WINMM_QueryInterfaceInfo info;
+    UINT ret;
 
     if(!WINMM_StartDevicesThread())
         return MMSYSERR_NODRIVER;
@@ -3191,7 +3217,9 @@ static UINT get_device_interface(UINT msg, BOOL is_out, UINT index, WCHAR *out,
     info.str = out;
     info.len_bytes = out_len;
 
-    return SendMessageW(g_devices_hwnd, msg, (DWORD_PTR)&info, 0);
+    ret = SendMessageW(g_devices_hwnd, msg, (DWORD_PTR)&info, 0);
+    InterlockedDecrement(&g_devthread_token);
+    return ret;
 }
 
 /**************************************************************************
@@ -3340,9 +3368,6 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
     TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
             dwCallback, dwInstance, dwFlags);
 
-    if(!WINMM_StartDevicesThread())
-        return MMSYSERR_NODRIVER;
-
     if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
         return MMSYSERR_INVALPARAM;
 
@@ -3350,6 +3375,9 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
     if(res != MMSYSERR_NOERROR)
         return res;
 
+    if(!WINMM_StartDevicesThread())
+        return MMSYSERR_NODRIVER;
+
     info.handle = 0;
     info.format = (WAVEFORMATEX*)lpFormat;
     info.callback = dwCallback;
@@ -3359,6 +3387,7 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
     info.reset = TRUE;
 
     res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
+    InterlockedDecrement(&g_devthread_token);
     if(res != MMSYSERR_NOERROR || (dwFlags & WAVE_FORMAT_QUERY))
         return res;
 
@@ -4341,12 +4370,10 @@ UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
 				   DWORD fdwDetails)
 {
     WINMM_ControlDetails details;
+    UINT ret;
 
     TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
 
-    if(!WINMM_StartDevicesThread())
-        return MMSYSERR_NODRIVER;
-
     if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
             MIXER_SETCONTROLDETAILSF_CUSTOM)
         return MMSYSERR_NOTSUPPORTED;
@@ -4354,14 +4381,19 @@ UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
     if(!lpmcd)
         return MMSYSERR_INVALPARAM;
 
+    if(!WINMM_StartDevicesThread())
+        return MMSYSERR_NODRIVER;
+
     TRACE("dwControlID: %u\n", lpmcd->dwControlID);
 
     details.hmix = hmix;
     details.details = lpmcd;
     details.flags = fdwDetails;
 
-    return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
+    ret = SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
             (DWORD_PTR)&details, 0);
+    InterlockedDecrement(&g_devthread_token);
+    return ret;
 }
 
 /**************************************************************************
-- 
1.8.2




More information about the wine-patches mailing list