mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 11:43:31 +00:00
179 lines
4.6 KiB
C
179 lines
4.6 KiB
C
/*
|
|
* 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();
|
|
TRACE("cancelled.\n");
|
|
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);
|
|
}
|