[PATCH v3 2/2] winmm: Buffer any WOM_DONE/WIM_DATA events triggered from within a waveOut/waveIn callback.
Liam Murphy
liampm32 at gmail.com
Thu Jan 20 22:37:27 CST 2022
The events are then fired after the callback completes.
Signed-off-by: Liam Murphy <liampm32 at gmail.com>
---
v3: Use block comments instead of line comments
---
dlls/winmm/tests/wave.c | 1 -
dlls/winmm/waveform.c | 165 ++++++++++++++++++++++++++++++----------
2 files changed, 124 insertions(+), 42 deletions(-)
diff --git a/dlls/winmm/tests/wave.c b/dlls/winmm/tests/wave.c
index 619c4f9339e..61c8cae72a5 100644
--- a/dlls/winmm/tests/wave.c
+++ b/dlls/winmm/tests/wave.c
@@ -881,7 +881,6 @@ static void CALLBACK reentrancy_callback_func(HWAVEOUT hwo, UINT uMsg,
data->call_num += 1;
- todo_wine_if(data->call_num == 3)
ok(data->running_thread != GetCurrentThreadId(), "winmm callback called reentrantly, with message %u\n", uMsg);
if (data->running_thread) {
if (data->running_thread != GetCurrentThreadId()) trace("Callback running on two threads simultaneously\n");
diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c
index 1159b48b483..32104d5abbb 100644
--- a/dlls/winmm/waveform.c
+++ b/dlls/winmm/waveform.c
@@ -67,12 +67,20 @@ WINE_DEFAULT_DEBUG_CHANNEL(winmm);
#define AC_BUFLEN (10 * 100000)
#define MAX_DEVICES 256
#define MAPPER_INDEX 0x3F
+#define CB_RUNNING 0x10
typedef struct _WINMM_CBInfo {
DWORD_PTR callback;
DWORD_PTR user;
+ /* Layout: 0b00000000_000r0ttt
+ * where t: callback type (`DCB_*` constants)
+ * r: Whether the callback is currently running, to avoid reentrancy.
+ * The 0 in the middle is `DCB_NOSWITCH`. */
DWORD flags;
HWAVE hwave;
+ /* Used to buffer data from `WIM_DATA` or `WOM_DONE` if the callback is still running,
+ * depending on whether this is an input or output device respectively. */
+ WAVEHDR *first, *last;
} WINMM_CBInfo;
struct _WINMM_MMDevice;
@@ -169,6 +177,12 @@ typedef struct _WINMM_OpenInfo {
WAVEFORMATEX *format;
DWORD_PTR callback;
DWORD_PTR cb_user;
+ /* The high half of the bits are the same as `WINMM_CBInfo.flags`,
+ * and the low half have the layout 0b00000000_0000dmaq,
+ * where d: `WAVE_FORMAT_DIRECT`,
+ * m: `WAVE_MAPPED`,
+ * a: `WAVE_ALLOWSYNC`,
+ * q: `WAVE_FORMAT_QUERY`. */
DWORD flags;
BOOL reset;
} WINMM_OpenInfo;
@@ -370,13 +384,103 @@ static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
return device;
}
+/* Note: the lock on `device` must already have been acquired before calling this function. */
+static inline void WINMM_SendBufferedMessages(WINMM_Device *device) {
+ WINMM_CBInfo* info = &device->cb_info;
+ /* Make a copy of this to pass to the callback while within the critical section,
+ * since it can be mutated once we leave the critical section. */
+ DWORD cb_type = info->flags;
+
+ while (info->first) {
+ WAVEHDR* hdr;
+ WORD new_msg;
+
+ if (device->render) {
+ new_msg = WOM_DONE;
+ } else {
+ new_msg = WIM_DATA;
+ }
+
+ hdr = info->first;
+ info->first = hdr->lpNext;
+ hdr->lpNext = NULL;
+
+ LeaveCriticalSection(&device->lock);
+
+ DriverCallback(info->callback, cb_type, (HDRVR)info->hwave,
+ new_msg, info->user, (DWORD_PTR)hdr, 0);
+
+ EnterCriticalSection(&device->lock);
+ }
+}
+
/* Note: NotifyClient should never be called while holding the device lock
- * since the client may call wave* functions from within the callback. */
-static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
- DWORD_PTR param2)
+ * since the client may call wave* functions from within the callback.
+ *
+ * Any `WAVEHDR`s passed to this function must no longer be referenced by any other `WAVEHDR`s,
+ * so that their linked list can be reused to buffer them when this is called from within the callback. */
+static inline void WINMM_NotifyClient(WINMM_Device *device, WORD msg, WAVEHDR* param1,
+ WAVEHDR* param2)
{
- DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
- msg, info->user, param1, param2);
+ WINMM_CBInfo *info;
+ DWORD cb_type;
+
+ EnterCriticalSection(&device->lock);
+ info = &device->cb_info;
+ /* Make a copy of this to pass to the callback while within the critical section,
+ * since it can be mutated once we leave the critical section. */
+ cb_type = info->flags;
+
+ if (info->flags & CB_RUNNING) {
+ switch (msg) {
+ case WIM_DATA:
+ case WOM_DONE:
+ param1->lpNext = NULL;
+ if (!info->first) {
+ info->first = info->last = param1;
+ } else {
+ info->last->lpNext = param1;
+ info->last = param1;
+ }
+ break;
+ case WIM_CLOSE:
+ case WOM_CLOSE:
+ /* If this device is closing, it might get reopened as another kind of device
+ * with all the buffered messages wiped, causing them to never get sent.
+ * So we need to send them all now.
+ *
+ * On Windows, calling `waveOutClose` from within `DriverCallback` hangs anyway,
+ * so this causing reentrancy isn't really a problem. */
+ WINMM_SendBufferedMessages(device);
+
+ LeaveCriticalSection(&device->lock);
+
+ DriverCallback(info->callback, cb_type, (HDRVR)info->hwave,
+ msg, info->user, (DWORD_PTR)param1, (DWORD_PTR)param2);
+
+ EnterCriticalSection(&device->lock);
+ break;
+ }
+ } else {
+ if ((info->flags & DCB_TYPEMASK) == DCB_FUNCTION) {
+ info->flags |= CB_RUNNING;
+ }
+
+ LeaveCriticalSection(&device->lock);
+
+ DriverCallback(info->callback, cb_type, (HDRVR)info->hwave,
+ msg, info->user, (DWORD_PTR)param1, (DWORD_PTR)param2);
+
+ EnterCriticalSection(&device->lock);
+
+ if ((info->flags & DCB_TYPEMASK) == DCB_FUNCTION) {
+ WINMM_SendBufferedMessages(device);
+
+ info->flags &= ~CB_RUNNING;
+ }
+ }
+
+ LeaveCriticalSection(&device->lock);
}
static MMRESULT hr2mmr(HRESULT hr)
@@ -1202,6 +1306,8 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
device->cb_info.callback = info->callback;
device->cb_info.user = info->cb_user;
device->cb_info.hwave = device->handle;
+ device->cb_info.first = NULL;
+ device->cb_info.last = NULL;
info->handle = device->handle;
@@ -1623,7 +1729,6 @@ static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
static void WOD_PushData(WINMM_Device *device)
{
- WINMM_CBInfo cb_info;
HRESULT hr;
UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
UINT32 queue_bytes, nloops;
@@ -1752,15 +1857,13 @@ static void WOD_PushData(WINMM_Device *device)
device->played_frames += avail_frames;
exit:
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
while(first){
WAVEHDR *next = first->lpNext;
first->dwFlags &= ~WHDR_INQUEUE;
first->dwFlags |= WHDR_DONE;
- WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WOM_DONE, first, 0);
first = next;
}
}
@@ -1860,7 +1963,6 @@ static void WID_PullACMData(WINMM_Device *device)
static void WID_PullData(WINMM_Device *device)
{
- WINMM_CBInfo cb_info;
WAVEHDR *queue, *first = NULL, *last = NULL;
HRESULT hr;
@@ -1926,8 +2028,6 @@ static void WID_PullData(WINMM_Device *device)
}
exit:
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
if(last){
@@ -1936,7 +2036,7 @@ exit:
WAVEHDR *next = first->lpNext;
first->dwFlags &= ~WHDR_INQUEUE;
first->dwFlags |= WHDR_DONE;
- WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WIM_DATA, first, 0);
first = next;
}
}
@@ -1985,7 +2085,6 @@ static LRESULT WINMM_Pause(WINMM_Device *device)
static LRESULT WINMM_Reset(HWAVE hwave)
{
- WINMM_CBInfo cb_info;
WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
BOOL is_out;
WAVEHDR *first;
@@ -2012,7 +2111,6 @@ static LRESULT WINMM_Reset(HWAVE hwave)
device->last_clock_pos = 0;
IAudioClient_Reset(device->client);
- cb_info = device->cb_info;
is_out = device->render != NULL;
LeaveCriticalSection(&device->lock);
@@ -2022,9 +2120,9 @@ static LRESULT WINMM_Reset(HWAVE hwave)
first->dwFlags &= ~WHDR_INQUEUE;
first->dwFlags |= WHDR_DONE;
if(is_out)
- WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WOM_DONE, first, 0);
else
- WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
+ WINMM_NotifyClient(device, WIM_DATA, first, 0);
first = next;
}
@@ -2732,7 +2830,7 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
{
LRESULT res;
WINMM_OpenInfo info;
- WINMM_CBInfo cb_info;
+ WINMM_Device* device;
TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
dwCallback, dwInstance, dwFlags);
@@ -2763,12 +2861,9 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
if(lphWaveOut)
*lphWaveOut = (HWAVEOUT)info.handle;
- cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
- cb_info.callback = dwCallback;
- cb_info.user = dwInstance;
- cb_info.hwave = info.handle;
+ device = WINMM_GetDeviceFromHWAVE(info.handle);
- WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
+ WINMM_NotifyClient(device, WOM_OPEN, 0, 0);
return res;
}
@@ -2780,7 +2875,6 @@ UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
{
UINT res;
WINMM_Device *device;
- WINMM_CBInfo cb_info;
TRACE("(%p)\n", hWaveOut);
@@ -2789,14 +2883,12 @@ UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
if(!WINMM_ValidateAndLock(device))
return MMSYSERR_INVALHANDLE;
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
if(res == MMSYSERR_NOERROR)
- WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
+ WINMM_NotifyClient(device, WOM_CLOSE, 0, 0);
return res;
}
@@ -3388,7 +3480,7 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
{
LRESULT res;
WINMM_OpenInfo info;
- WINMM_CBInfo cb_info;
+ WINMM_Device* device;
TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
dwCallback, dwInstance, dwFlags);
@@ -3419,12 +3511,9 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
if(lphWaveIn)
*lphWaveIn = (HWAVEIN)info.handle;
- cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
- cb_info.callback = dwCallback;
- cb_info.user = dwInstance;
- cb_info.hwave = info.handle;
+ device = WINMM_GetDeviceFromHWAVE(info.handle);
- WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
+ WINMM_NotifyClient(device, WIM_OPEN, 0, 0);
return res;
}
@@ -3435,7 +3524,6 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
UINT WINAPI waveInClose(HWAVEIN hWaveIn)
{
WINMM_Device *device;
- WINMM_CBInfo cb_info;
UINT res;
TRACE("(%p)\n", hWaveIn);
@@ -3445,14 +3533,12 @@ UINT WINAPI waveInClose(HWAVEIN hWaveIn)
if(!WINMM_ValidateAndLock(device))
return MMSYSERR_INVALHANDLE;
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
if(res == MMSYSERR_NOERROR)
- WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
+ WINMM_NotifyClient(device, WIM_CLOSE, 0, 0);
return res;
}
@@ -3568,7 +3654,6 @@ UINT WINAPI waveInStart(HWAVEIN hWaveIn)
*/
UINT WINAPI waveInStop(HWAVEIN hWaveIn)
{
- WINMM_CBInfo cb_info;
WINMM_Device *device;
WAVEHDR *buf;
HRESULT hr;
@@ -3593,14 +3678,12 @@ UINT WINAPI waveInStop(HWAVEIN hWaveIn)
}else
buf = NULL;
- cb_info = device->cb_info;
-
LeaveCriticalSection(&device->lock);
if(buf){
buf->dwFlags &= ~WHDR_INQUEUE;
buf->dwFlags |= WHDR_DONE;
- WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
+ WINMM_NotifyClient(device, WIM_DATA, buf, 0);
}
return MMSYSERR_NOERROR;
--
2.34.1
More information about the wine-devel
mailing list