Avoid blocking the default callback queue with file I/O.
-- v2: mfplat: Use MFASYNC_CALLBACK_QUEUE_IO for byte stream async requests. mfplat/tests: Test that BeginRead doesn't use MFASYNC_CALLBACK_QUEUE_STANDARD. mfplat/tests: Create the test_callback event in the constructor.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 39fdb665bbf..0f4ae026228 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -521,7 +521,10 @@ static ULONG WINAPI testcallback_Release(IMFAsyncCallback *iface) ULONG refcount = InterlockedDecrement(&callback->refcount);
if (!refcount) + { + CloseHandle(callback->event); free(callback); + }
return refcount; } @@ -804,6 +807,7 @@ static struct test_callback * create_test_callback(const IMFAsyncCallbackVtbl *v
callback->IMFAsyncCallback_iface.lpVtbl = vtbl ? vtbl : &testcallbackvtbl; callback->refcount = 1; + callback->event = CreateEventA(NULL, FALSE, FALSE, NULL);
return callback; } @@ -816,7 +820,6 @@ static BOOL get_event(IMFMediaEventGenerator *generator, MediaEventType expected HRESULT hr;
callback = create_test_callback(&events_callback_vtbl); - callback->event = CreateEventA(NULL, FALSE, FALSE, NULL);
for (;;) { @@ -847,7 +850,6 @@ static BOOL get_event(IMFMediaEventGenerator *generator, MediaEventType expected } }
- CloseHandle(callback->event); if (callback->media_event) IMFMediaEvent_Release(callback->media_event); IMFAsyncCallback_Release(&callback->IMFAsyncCallback_iface); @@ -937,7 +939,6 @@ static void test_source_resolver(void) IMFByteStream_Release(stream);
/* Create from URL. */ - callback->event = CreateEventA(NULL, FALSE, FALSE, NULL);
hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"nonexisting.mp4", MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, (IUnknown **)&stream); @@ -1234,7 +1235,6 @@ static void test_source_resolver(void) (void **)&scheme_handler); ok(hr == S_OK, "Failed to create handler object, hr %#lx.\n", hr);
- callback2->event = callback->event; cancel_cookie = NULL; hr = IMFSchemeHandler_BeginCreateObject(scheme_handler, pathW, MF_RESOLUTION_MEDIASOURCE, NULL, &cancel_cookie, &callback2->IMFAsyncCallback_iface, (IUnknown *)scheme_handler); @@ -1249,8 +1249,6 @@ static void test_source_resolver(void) if (do_uninit) CoUninitialize();
- CloseHandle(callback->event); - IMFSourceResolver_Release(resolver);
hr = MFShutdown(); @@ -3524,16 +3522,12 @@ static void test_event_queue(void) hr = IMFMediaEventQueue_BeginGetEvent(queue, &callback2->IMFAsyncCallback_iface, (IUnknown *)&callback->IMFAsyncCallback_iface); ok(hr == MF_E_MULTIPLE_SUBSCRIBERS, "Unexpected hr %#lx.\n", hr);
- callback->event = CreateEventA(NULL, FALSE, FALSE, NULL); - hr = IMFMediaEventQueue_QueueEvent(queue, event); ok(hr == S_OK, "Failed to queue event, hr %#lx.\n", hr);
ret = WaitForSingleObject(callback->event, 500); ok(ret == WAIT_OBJECT_0, "Unexpected return value %#lx.\n", ret);
- CloseHandle(callback->event); - IMFMediaEvent_Release(event);
hr = MFCreateAsyncResult(NULL, &callback->IMFAsyncCallback_iface, NULL, &result); @@ -4957,8 +4951,6 @@ static void test_async_create_file(void) hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Fail to start up, hr %#lx.\n", hr);
- callback->event = CreateEventA(NULL, FALSE, FALSE, NULL); - GetTempPathW(ARRAY_SIZE(pathW), pathW); GetTempFileNameW(pathW, NULL, 0, fileW);
@@ -4971,8 +4963,6 @@ static void test_async_create_file(void)
IUnknown_Release(cancel_cookie);
- CloseHandle(callback->event); - hr = MFShutdown(); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr);
@@ -5856,7 +5846,6 @@ static void test_queue_com_state(const char *name) HRESULT hr;
callback = create_test_callback(&test_queue_com_state_callback_vtbl); - callback->event = CreateEventA(NULL, FALSE, FALSE, NULL);
hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); @@ -5888,8 +5877,6 @@ static void test_queue_com_state(const char *name) } }
- CloseHandle(callback->event); - hr = MFShutdown(); ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfplat/tests/mfplat.c | 254 +++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 0f4ae026228..9c73f71ca7e 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -488,6 +488,7 @@ struct test_callback HANDLE event; DWORD param; IMFMediaEvent *media_event; + IMFAsyncResult *result; };
static struct test_callback *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) @@ -535,6 +536,37 @@ static HRESULT WINAPI testcallback_GetParameters(IMFAsyncCallback *iface, DWORD return E_NOTIMPL; }
+static HRESULT WINAPI test_async_callback_result_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + + callback->result = result; + IMFAsyncResult_AddRef(callback->result); + SetEvent(callback->event); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl test_async_callback_result_vtbl = +{ + testcallback_QueryInterface, + testcallback_AddRef, + testcallback_Release, + testcallback_GetParameters, + test_async_callback_result_Invoke, +}; + +static DWORD wait_async_callback_result(IMFAsyncCallback *iface, DWORD timeout, IMFAsyncResult **result) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + DWORD res = WaitForSingleObject(callback->event, timeout); + + *result = callback->result; + callback->result = NULL; + + return res; +} + static BOOL check_clsid(CLSID *clsids, UINT32 count) { int i; @@ -2129,15 +2161,191 @@ static void test_attributes(void) IMFAttributes_Release(attributes1); }
+struct test_stream +{ + IStream IStream_iface; + LONG refcount; + + HANDLE read_event; + HANDLE done_event; +}; + +static struct test_stream *impl_from_IStream(IStream *iface) +{ + return CONTAINING_RECORD(iface, struct test_stream, IStream_iface); +} + +static HRESULT WINAPI test_stream_QueryInterface(IStream *iface, REFIID iid, void **out) +{ + if (IsEqualIID(iid, &IID_IUnknown) + || IsEqualIID(iid, &IID_IStream)) + { + *out = iface; + IStream_AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_stream_AddRef(IStream *iface) +{ + struct test_stream *stream = impl_from_IStream(iface); + return InterlockedIncrement(&stream->refcount); +} + +static ULONG WINAPI test_stream_Release(IStream *iface) +{ + struct test_stream *stream = impl_from_IStream(iface); + ULONG ref = InterlockedDecrement(&stream->refcount); + + if (!ref) + { + CloseHandle(stream->read_event); + CloseHandle(stream->done_event); + free(stream); + } + + return ref; +} + +static HRESULT WINAPI test_stream_Read(IStream *iface, void *data, ULONG size, ULONG *ret_size) +{ + struct test_stream *stream = impl_from_IStream(iface); + DWORD res; + + SetEvent(stream->read_event); + res = WaitForSingleObject(stream->done_event, 1000); + ok(res == 0, "got %#lx\n", res); + + *ret_size = size; + return S_OK; +} + +static HRESULT WINAPI test_stream_Write(IStream *iface, const void *data, ULONG size, ULONG *ret_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD method, ULARGE_INTEGER *ret_offset) +{ + return S_OK; +} + +static HRESULT WINAPI test_stream_SetSize(IStream *iface, ULARGE_INTEGER size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, + ULARGE_INTEGER *read_size, ULARGE_INTEGER *write_size) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_Commit(IStream *iface, DWORD flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_Revert(IStream *iface) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI test_stream_Stat(IStream *iface, STATSTG *stat, DWORD flags) +{ + memset(stat, 0, sizeof(STATSTG)); + stat->pwcsName = NULL; + stat->type = STGTY_STREAM; + stat->cbSize.QuadPart = 16; + return S_OK; +} + +static HRESULT WINAPI test_stream_Clone(IStream *iface, IStream **out) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IStreamVtbl test_stream_vtbl = +{ + test_stream_QueryInterface, + test_stream_AddRef, + test_stream_Release, + test_stream_Read, + test_stream_Write, + test_stream_Seek, + test_stream_SetSize, + test_stream_CopyTo, + test_stream_Commit, + test_stream_Revert, + test_stream_LockRegion, + test_stream_UnlockRegion, + test_stream_Stat, + test_stream_Clone, +}; + +static HRESULT test_stream_create(IStream **out) +{ + struct test_stream *stream; + + if (!(stream = calloc(1, sizeof(*stream)))) + return E_OUTOFMEMORY; + + stream->IStream_iface.lpVtbl = &test_stream_vtbl; + stream->refcount = 1; + + stream->read_event = CreateEventW(NULL, FALSE, FALSE, NULL); + stream->done_event = CreateEventW(NULL, FALSE, FALSE, NULL); + + *out = &stream->IStream_iface; + return S_OK; +} + +static DWORD test_stream_wait_read(IStream *iface, DWORD timeout) +{ + struct test_stream *stream = impl_from_IStream(iface); + return WaitForSingleObject(stream->read_event, timeout); +} + +static void test_stream_complete_read(IStream *iface) +{ + struct test_stream *stream = impl_from_IStream(iface); + SetEvent(stream->done_event); +} + static void test_MFCreateMFByteStreamOnStream(void) { + struct test_callback *read_callback, *test_callback; IMFByteStream *bytestream; IMFByteStream *bytestream2; IStream *stream; IMFAttributes *attributes = NULL; + IMFAsyncResult *result; DWORD caps, written; IUnknown *unknown; + BYTE buffer[16]; ULONG ref, size; + DWORD res, len; HRESULT hr; UINT count; QWORD to; @@ -2266,6 +2474,52 @@ static void test_MFCreateMFByteStreamOnStream(void) IMFAttributes_Release(attributes); IMFByteStream_Release(bytestream); IStream_Release(stream); + + + /* test that BeginRead doesn't use MFASYNC_CALLBACK_QUEUE_STANDARD */ + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + read_callback = create_test_callback(&test_async_callback_result_vtbl); + test_callback = create_test_callback(&test_async_callback_result_vtbl); + + hr = test_stream_create(&stream); + ok(hr == S_OK, "got %#lx\n", hr); + hr = pMFCreateMFByteStreamOnStream(stream, &bytestream); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IMFByteStream_BeginRead(bytestream, buffer, 1, &read_callback->IMFAsyncCallback_iface, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + + res = test_stream_wait_read(stream, 1000); + ok(res == 0, "got %#lx\n", res); + res = wait_async_callback_result(&read_callback->IMFAsyncCallback_iface, 10, &result); + ok(res == WAIT_TIMEOUT, "got %#lx\n", res); + + /* MFASYNC_CALLBACK_QUEUE_STANDARD is not blocked */ + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &test_callback->IMFAsyncCallback_iface, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + res = wait_async_callback_result(&test_callback->IMFAsyncCallback_iface, 100, &result); + todo_wine ok(res == 0, "got %#lx\n", res); + if (res == 0) IMFAsyncResult_Release(result); + + test_stream_complete_read(stream); + res = wait_async_callback_result(&read_callback->IMFAsyncCallback_iface, 1000, &result); + ok(res == 0, "got %#lx\n", res); + hr = IMFByteStream_EndRead(bytestream, result, &len); + ok(hr == S_OK, "got %#lx\n", hr); + ok(len == 1, "got %#lx\n", len); + IMFAsyncResult_Release(result); + + IMFByteStream_Release(bytestream); + IStream_Release(stream); + + IMFAsyncCallback_Release(&read_callback->IMFAsyncCallback_iface); + IMFAsyncCallback_Release(&test_callback->IMFAsyncCallback_iface); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); }
static void test_file_stream(void)
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/mfplat/main.c | 2 +- dlls/mfplat/tests/mfplat.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 8aaf9f93cdc..96ec23fba9b 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -3811,7 +3811,7 @@ static HRESULT bytestream_create_io_request(struct bytestream *stream, enum asyn &stream->write_callback, NULL, &request))) goto failed;
- RtwqPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, 0, request); + RtwqPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, 0, request); IRtwqAsyncResult_Release(request);
failed: diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 9c73f71ca7e..f7ae614a109 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -2501,8 +2501,8 @@ static void test_MFCreateMFByteStreamOnStream(void) hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &test_callback->IMFAsyncCallback_iface, NULL); ok(hr == S_OK, "got %#lx\n", hr); res = wait_async_callback_result(&test_callback->IMFAsyncCallback_iface, 100, &result); - todo_wine ok(res == 0, "got %#lx\n", res); - if (res == 0) IMFAsyncResult_Release(result); + ok(res == 0, "got %#lx\n", res); + IMFAsyncResult_Release(result);
test_stream_complete_read(stream); res = wait_async_callback_result(&read_callback->IMFAsyncCallback_iface, 1000, &result);
This merge request was approved by Nikolay Sivov.