| /* | |
| * Speech API (SAPI) async helper implementation. | |
| * | |
| * Copyright 2023 Shaun Ren for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "objbase.h" | |
| #include "wine/list.h" | |
| #include "wine/debug.h" | |
| #include "sapi_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(sapi); | |
| static struct async_task *async_dequeue_task(struct async_queue *queue) | |
| { | |
| struct async_task *task = NULL; | |
| struct list *head; | |
| EnterCriticalSection(&queue->cs); | |
| if ((head = list_head(&queue->tasks))) | |
| { | |
| task = LIST_ENTRY(head, struct async_task, entry); | |
| list_remove(head); | |
| } | |
| LeaveCriticalSection(&queue->cs); | |
| return task; | |
| } | |
| void async_empty_queue(struct async_queue *queue) | |
| { | |
| struct async_task *task, *next; | |
| if (!queue->init) return; | |
| EnterCriticalSection(&queue->cs); | |
| LIST_FOR_EACH_ENTRY_SAFE(task, next, &queue->tasks, struct async_task, entry) | |
| { | |
| list_remove(&task->entry); | |
| free(task); | |
| } | |
| LeaveCriticalSection(&queue->cs); | |
| SetEvent(queue->empty); | |
| } | |
| static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx) | |
| { | |
| struct async_queue *queue = ctx; | |
| HANDLE handles[2] = { queue->cancel, queue->wait }; | |
| DWORD ret; | |
| CoInitializeEx(NULL, COINIT_MULTITHREADED); | |
| SetEvent(queue->ready); | |
| for (;;) | |
| { | |
| ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE); | |
| if (ret == WAIT_OBJECT_0) | |
| goto cancel; | |
| else if (ret == WAIT_OBJECT_0 + 1) | |
| { | |
| struct async_task *task; | |
| while ((task = async_dequeue_task(queue))) | |
| { | |
| ResetEvent(queue->empty); | |
| task->proc(task); | |
| free(task); | |
| if (WaitForSingleObject(queue->cancel, 0) == WAIT_OBJECT_0) | |
| goto cancel; | |
| } | |
| SetEvent(queue->empty); | |
| } | |
| else | |
| ERR("WaitForMultipleObjects failed: %#lx.\n", ret); | |
| } | |
| cancel: | |
| async_empty_queue(queue); | |
| CoUninitialize(); | |
| SetEvent(queue->ready); | |
| } | |
| HRESULT async_start_queue(struct async_queue *queue) | |
| { | |
| HRESULT hr; | |
| if (queue->init) | |
| return S_OK; | |
| InitializeCriticalSection(&queue->cs); | |
| list_init(&queue->tasks); | |
| if (!(queue->wait = CreateEventW(NULL, FALSE, FALSE, NULL)) || | |
| !(queue->ready = CreateEventW(NULL, FALSE, FALSE, NULL)) || | |
| !(queue->cancel = CreateEventW(NULL, FALSE, FALSE, NULL)) || | |
| !(queue->empty = CreateEventW(NULL, TRUE, TRUE, NULL))) | |
| goto fail; | |
| queue->init = TRUE; | |
| if (!TrySubmitThreadpoolCallback(async_worker, queue, NULL)) | |
| goto fail; | |
| WaitForSingleObject(queue->ready, INFINITE); | |
| return S_OK; | |
| fail: | |
| hr = HRESULT_FROM_WIN32(GetLastError()); | |
| DeleteCriticalSection(&queue->cs); | |
| if (queue->wait) CloseHandle(queue->wait); | |
| if (queue->ready) CloseHandle(queue->ready); | |
| if (queue->cancel) CloseHandle(queue->cancel); | |
| if (queue->empty) CloseHandle(queue->empty); | |
| memset(queue, 0, sizeof(*queue)); | |
| return hr; | |
| } | |
| void async_cancel_queue(struct async_queue *queue) | |
| { | |
| if (!queue->init) return; | |
| SetEvent(queue->cancel); | |
| WaitForSingleObject(queue->ready, INFINITE); | |
| DeleteCriticalSection(&queue->cs); | |
| CloseHandle(queue->wait); | |
| CloseHandle(queue->ready); | |
| CloseHandle(queue->cancel); | |
| CloseHandle(queue->empty); | |
| memset(queue, 0, sizeof(*queue)); | |
| } | |
| HRESULT async_queue_task(struct async_queue *queue, struct async_task *task) | |
| { | |
| HRESULT hr; | |
| if (FAILED(hr = async_start_queue(queue))) | |
| return hr; | |
| EnterCriticalSection(&queue->cs); | |
| list_add_tail(&queue->tasks, &task->entry); | |
| LeaveCriticalSection(&queue->cs); | |
| ResetEvent(queue->empty); | |
| SetEvent(queue->wait); | |
| return S_OK; | |
| } | |
| HRESULT async_wait_queue_empty(struct async_queue *queue, DWORD timeout) | |
| { | |
| if (!queue->init) return WAIT_OBJECT_0; | |
| return WaitForSingleObject(queue->empty, timeout); | |
| } |