Signed-off-by: Bernhard Kölbl besentv@gmail.com --- v2: Make IAsyncOperation truely asynchronous using a threadpool worker. --- dlls/windows.media.speech/async.c | 118 ++++++++++++++++++++--- dlls/windows.media.speech/private.h | 4 +- dlls/windows.media.speech/recognizer.c | 10 +- dlls/windows.media.speech/tests/speech.c | 73 +++++++------- 4 files changed, 156 insertions(+), 49 deletions(-)
diff --git a/dlls/windows.media.speech/async.c b/dlls/windows.media.speech/async.c index 94d0b35d9c1..243fe6869a1 100644 --- a/dlls/windows.media.speech/async.c +++ b/dlls/windows.media.speech/async.c @@ -35,6 +35,18 @@ struct async_operation IAsyncInfo IAsyncInfo_iface; const GUID *iid; LONG ref; + + IAsyncOperationCompletedHandler_IInspectable *handler; + IInspectable *result; + + async_operation_worker worker; + TP_WORK *async_run_work; + void *data; + + CRITICAL_SECTION cs; + + AsyncStatus status; + HRESULT hr; };
static inline struct async_operation *impl_from_IAsyncOperation_IInspectable(IAsyncOperation_IInspectable *iface) @@ -84,7 +96,11 @@ static ULONG WINAPI async_operation_Release( IAsyncOperation_IInspectable *iface TRACE("iface %p, ref %lu.\n", iface, ref);
if (!ref) + { + if (impl->result) IInspectable_Release(impl->result); + DeleteCriticalSection(&impl->cs); free(impl); + }
return ref; } @@ -110,21 +126,58 @@ static HRESULT WINAPI async_operation_GetTrustLevel( IAsyncOperation_IInspectabl static HRESULT WINAPI async_operation_put_Completed( IAsyncOperation_IInspectable *iface, IAsyncOperationCompletedHandler_IInspectable *handler ) { - FIXME("iface %p, handler %p stub!\n", iface, handler); - return E_NOTIMPL; + struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + HRESULT hr; + + TRACE("iface %p, handler %p.\n", iface, handler); + + EnterCriticalSection(&impl->cs); + + if (impl->handler) + hr = E_ILLEGAL_DELEGATE_ASSIGNMENT; + else + { + impl->handler = handler; + if (impl->status != Started && impl->handler) + IAsyncOperationCompletedHandler_IInspectable_Invoke(impl->handler, &impl->IAsyncOperation_IInspectable_iface, impl->status); + hr = S_OK; + } + + LeaveCriticalSection(&impl->cs); + + return hr; }
static HRESULT WINAPI async_operation_get_Completed( IAsyncOperation_IInspectable *iface, IAsyncOperationCompletedHandler_IInspectable **handler ) { - FIXME("iface %p, handler %p stub!\n", iface, handler); - return E_NOTIMPL; + FIXME("iface %p, handler %p semi stub!\n", iface, handler); + *handler = NULL; + return S_OK; }
static HRESULT WINAPI async_operation_GetResults( IAsyncOperation_IInspectable *iface, IInspectable **results ) { - FIXME("iface %p, results %p stub!\n", iface, results); - return E_NOTIMPL; + /* NOTE: Despite the name, this function only returns one result! */ + struct async_operation *impl = impl_from_IAsyncOperation_IInspectable(iface); + HRESULT hr; + + TRACE("iface %p, results %p.\n", iface, results); + + EnterCriticalSection(&impl->cs); + + if (!impl->result) + hr = E_UNEXPECTED; + else + { + *results = impl->result; + impl->result = NULL; /* NOTE: AsyncOperation gives up it's reference to result here! */ + hr = S_OK; + } + + LeaveCriticalSection(&impl->cs); + + return hr; }
static const struct IAsyncOperation_IInspectableVtbl async_operation_vtbl = @@ -159,14 +212,26 @@ static HRESULT WINAPI async_operation_info_get_Id( IAsyncInfo *iface, UINT32 *id
static HRESULT WINAPI async_operation_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) { - FIXME("iface %p, status %p stub!\n", iface, status); - return E_NOTIMPL; + struct async_operation *impl = impl_from_IAsyncInfo(iface); + TRACE("iface %p, status %p.\n", iface, status); + + EnterCriticalSection(&impl->cs); + *status = impl->status; + LeaveCriticalSection(&impl->cs); + + return S_OK; }
static HRESULT WINAPI async_operation_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) { - FIXME("iface %p, error_code %p stub!\n", iface, error_code); - return E_NOTIMPL; + struct async_operation *impl = impl_from_IAsyncInfo(iface); + TRACE("iface %p, error_code %p.\n", iface, error_code); + + EnterCriticalSection(&impl->cs); + *error_code = impl->hr; + LeaveCriticalSection(&impl->cs); + + return S_OK; }
static HRESULT WINAPI async_operation_info_Cancel( IAsyncInfo *iface ) @@ -199,7 +264,28 @@ static const struct IAsyncInfoVtbl async_operation_info_vtbl = async_operation_info_Close };
-HRESULT async_operation_create( const GUID *iid, IAsyncOperation_IInspectable **out ) +static void CALLBACK async_run_cb(TP_CALLBACK_INSTANCE *instance, void *context, TP_WORK *work) +{ + struct async_operation *impl = context; + IInspectable *result; + HRESULT hr; + + hr = impl->worker(impl->data, &result); + impl->result = result; + + EnterCriticalSection(&impl->cs); + if (FAILED(hr)) impl->status = Error; + else impl->status = Completed; + + if (impl->handler) IAsyncOperationCompletedHandler_IInspectable_Invoke( impl->handler, + &impl->IAsyncOperation_IInspectable_iface, + impl->status ); + + impl->hr = hr; + LeaveCriticalSection(&impl->cs); +} + +HRESULT async_operation_create( const GUID *iid, void *data, async_operation_worker worker, IAsyncOperation_IInspectable **out ) { struct async_operation *impl;
@@ -214,6 +300,16 @@ HRESULT async_operation_create( const GUID *iid, IAsyncOperation_IInspectable ** impl->iid = iid; impl->ref = 1;
+ impl->worker = worker; + impl->data = data; + impl->async_run_work = CreateThreadpoolWork(async_run_cb, impl, NULL); + impl->status = Started; + + InitializeCriticalSection(&impl->cs); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": async_operation.cs"); + + SubmitThreadpoolWork(impl->async_run_work); + *out = &impl->IAsyncOperation_IInspectable_iface; TRACE("created %p\n", *out); return S_OK; diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 1cf61c51f1e..ed11cad7966 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -69,7 +69,9 @@ struct vector_iids const GUID *view; };
-HRESULT async_operation_create( const GUID *iid, IAsyncOperation_IInspectable **out ); +typedef HRESULT (WINAPI *async_operation_worker)( void *data, IInspectable **result ); + +HRESULT async_operation_create( const GUID *iid, void *data, async_operation_worker worker, IAsyncOperation_IInspectable **out );
HRESULT typed_event_handlers_append( struct list *list, ITypedEventHandler_IInspectable_IInspectable *handler, EventRegistrationToken *token ); HRESULT typed_event_handlers_remove( struct list *list, EventRegistrationToken *token ); diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index b16ecb24641..fa36b7937b0 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -348,12 +348,20 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; }
+static HRESULT WINAPI compile_worker( void *data, IInspectable **result ) +{ + return S_OK; +} + static HRESULT WINAPI recognizer_CompileConstraintsAsync( ISpeechRecognizer *iface, IAsyncOperation_SpeechRecognitionCompilationResult **operation ) { IAsyncOperation_IInspectable **value = (IAsyncOperation_IInspectable **)operation; + struct recognizer *impl = impl_from_ISpeechRecognizer(iface); + FIXME("iface %p, operation %p semi-stub!\n", iface, operation); - return async_operation_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, value); + + return async_operation_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, impl, compile_worker, value); }
static HRESULT WINAPI recognizer_RecognizeAsync( ISpeechRecognizer *iface, diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index a3866683016..1b36f6c4f44 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -953,55 +953,57 @@ static void test_SpeechRecognizer(void)
handler = (void*)0xdeadbeef; hr = IAsyncOperation_SpeechRecognitionCompilationResult_get_Completed(operation, &handler); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(handler == NULL, "Handler had value %p.\n", handler); - - if (FAILED(hr)) goto skip_operation; + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(handler == NULL, "Handler had value %p.\n", handler);
compilation_result = (void*)0xdeadbeef; hr = IAsyncOperation_SpeechRecognitionCompilationResult_GetResults(operation, &compilation_result); todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(compilation_result == (void*)0xdeadbeef, "Compilation result had value %p.\n", compilation_result); + ok(compilation_result == (void*)0xdeadbeef, "Compilation result had value %p.\n", compilation_result);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, &compilation_handler.IAsyncHandler_Compilation_iface); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine check_refcount(&compilation_handler.IAsyncHandler_Compilation_iface, 1); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + check_refcount(&compilation_handler.IAsyncHandler_Compilation_iface, 1);
WaitForSingleObject(compilation_handler.event_finished, INFINITE); CloseHandle(compilation_handler.event_finished);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, NULL); - todo_wine ok(hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "Got unexpected hr %#lx.\n", hr); + ok(hr == E_ILLEGAL_DELEGATE_ASSIGNMENT, "Got unexpected hr %#lx.\n", hr);
handler = (void*)0xdeadbeef; hr = IAsyncOperation_SpeechRecognitionCompilationResult_get_Completed(operation, &handler); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(handler == NULL, "Handler had value %p.\n", handler); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(handler == NULL, "Handler had value %p.\n", handler);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_GetResults(operation, &compilation_result); todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); - todo_wine check_interface(compilation_result, &IID_IAgileObject, TRUE);
- hr = ISpeechRecognitionCompilationResult_get_Status(compilation_result, &result_status); - todo_wine ok(hr == S_OK, "ISpeechRecognitionCompilationResult_get_Status failed, hr %#lx.\n", hr); - todo_wine ok(result_status == SpeechRecognitionResultStatus_Success, "Got unexpected status %x.\n", result_status); + if (SUCCEEDED(hr)) + { + todo_wine check_interface(compilation_result, &IID_IAgileObject, TRUE); + + hr = ISpeechRecognitionCompilationResult_get_Status(compilation_result, &result_status); + todo_wine ok(hr == S_OK, "ISpeechRecognitionCompilationResult_get_Status failed, hr %#lx.\n", hr); + todo_wine ok(result_status == SpeechRecognitionResultStatus_Success, "Got unexpected status %x.\n", result_status);
- ref = ISpeechRecognitionCompilationResult_Release(compilation_result); - todo_wine ok(!ref , "Got unexpected ref %lu.\n", ref); + ref = ISpeechRecognitionCompilationResult_Release(compilation_result); + todo_wine ok(!ref , "Got unexpected ref %lu.\n", ref); + }
hr = IAsyncOperation_SpeechRecognitionCompilationResult_GetResults(operation, &compilation_result); - todo_wine ok(hr == E_UNEXPECTED, "Got unexpected hr %#lx.\n", hr); + ok(hr == E_UNEXPECTED, "Got unexpected hr %#lx.\n", hr);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_QueryInterface(operation, &IID_IAsyncInfo, (void **)&info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
/* Check if AsyncInfo and AsyncOperation share the same refcount. */ IAsyncOperation_SpeechRecognitionCompilationResult_AddRef(operation); - todo_wine check_refcount(operation, 3); - todo_wine check_refcount(info, 3); + check_refcount(operation, 3); + check_refcount(info, 3);
IAsyncOperation_SpeechRecognitionCompilationResult_Release(operation); - todo_wine check_refcount(info, 2); + check_refcount(info, 2);
id = 0xdeadbeef; hr = IAsyncInfo_get_Id(info, &id); @@ -1010,16 +1012,16 @@ static void test_SpeechRecognizer(void)
async_status = 0xdeadbeef; hr = IAsyncInfo_get_Status(info, &async_status); - todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); - todo_wine ok(async_status == Completed, "Status was %x.\n", async_status); + ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + ok(async_status == Completed, "Status was %x.\n", async_status);
hr = IAsyncInfo_Cancel(info); todo_wine ok(hr == S_OK, "IAsyncInfo_Cancel failed, hr %#lx.\n", hr);
async_status = 0xdeadbeef; hr = IAsyncInfo_get_Status(info, &async_status); - todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); - todo_wine ok(async_status == Completed, "Status was %x.\n", async_status); + ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + ok(async_status == Completed, "Status was %x.\n", async_status);
hr = IAsyncInfo_Close(info); todo_wine ok(hr == S_OK, "IAsyncInfo_Close failed, hr %#lx.\n", hr); @@ -1032,7 +1034,7 @@ static void test_SpeechRecognizer(void) todo_wine ok(async_status == AsyncStatus_Closed, "Status was %x.\n", async_status);
ref = IAsyncInfo_Release(info); - todo_wine ok(ref == 1, "Got unexpected ref %lu.\n", ref); + ok(ref == 1, "Got unexpected ref %lu.\n", ref);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, NULL); todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "Got unexpected hr %#lx.\n", hr); @@ -1051,36 +1053,35 @@ static void test_SpeechRecognizer(void) ok(compilation_handler2.event_finished != NULL, "Finished event wasn't created.\n");
hr = ISpeechRecognizer_CompileConstraintsAsync(recognizer, &operation); - todo_wine ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr); - todo_wine check_interface(operation, &IID_IAgileObject, TRUE); + ok(hr == S_OK, "ISpeechRecognizer_CompileConstraintsAsync failed, hr %#lx.\n", hr); + check_interface(operation, &IID_IAgileObject, TRUE);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_QueryInterface(operation, &IID_IAsyncInfo, (void **)&info); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
compilation_result = (void*)0xdeadbeef; hr = IAsyncOperation_SpeechRecognitionCompilationResult_GetResults(operation, &compilation_result); todo_wine ok(hr == E_ILLEGAL_METHOD_CALL, "Got unexpected hr %#lx.\n", hr); - todo_wine ok(compilation_result == (void*)0xdeadbeef, "Compilation result had value %p.\n", compilation_result); + ok(compilation_result == (void*)0xdeadbeef, "Compilation result had value %p.\n", compilation_result);
async_status = 0xdeadbeef; hr = IAsyncInfo_get_Status(info, &async_status); - todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); - todo_wine ok(async_status == Started || async_status == Completed, "Status was %x.\n", async_status); + ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + ok(async_status == Started || async_status == Completed, "Status was %x.\n", async_status);
IAsyncInfo_Release(info);
hr = IAsyncOperation_SpeechRecognitionCompilationResult_put_Completed(operation, &compilation_handler2.IAsyncHandler_Compilation_iface); - todo_wine ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
WaitForSingleObject(compilation_handler2.event_finished, INFINITE); CloseHandle(compilation_handler2.event_finished);
async_status = 0xdeadbeef; hr = IAsyncInfo_get_Status(info, &async_status); - todo_wine ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); - todo_wine ok(async_status == Completed, "Status was %x.\n", async_status); + ok(hr == S_OK, "IAsyncInfo_get_Status failed, hr %#lx.\n", hr); + ok(async_status == Completed, "Status was %x.\n", async_status);
-skip_operation: ref = IAsyncOperation_SpeechRecognitionCompilationResult_Release(operation); ok(!ref, "Got unexpected ref %lu.\n", ref);