diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/pool | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/pool')
-rw-r--r-- | winpr/libwinpr/pool/CMakeLists.txt | 41 | ||||
-rw-r--r-- | winpr/libwinpr/pool/ModuleOptions.cmake | 9 | ||||
-rw-r--r-- | winpr/libwinpr/pool/callback.c | 54 | ||||
-rw-r--r-- | winpr/libwinpr/pool/callback_cleanup.c | 140 | ||||
-rw-r--r-- | winpr/libwinpr/pool/cleanup_group.c | 140 | ||||
-rw-r--r-- | winpr/libwinpr/pool/io.c | 49 | ||||
-rw-r--r-- | winpr/libwinpr/pool/pool.c | 251 | ||||
-rw-r--r-- | winpr/libwinpr/pool/pool.h | 122 | ||||
-rw-r--r-- | winpr/libwinpr/pool/synch.c | 44 | ||||
-rw-r--r-- | winpr/libwinpr/pool/test/CMakeLists.txt | 30 | ||||
-rw-r--r-- | winpr/libwinpr/pool/test/TestPoolIO.c | 8 | ||||
-rw-r--r-- | winpr/libwinpr/pool/test/TestPoolSynch.c | 8 | ||||
-rw-r--r-- | winpr/libwinpr/pool/test/TestPoolThread.c | 41 | ||||
-rw-r--r-- | winpr/libwinpr/pool/test/TestPoolTimer.c | 8 | ||||
-rw-r--r-- | winpr/libwinpr/pool/test/TestPoolWork.c | 136 | ||||
-rw-r--r-- | winpr/libwinpr/pool/timer.c | 50 | ||||
-rw-r--r-- | winpr/libwinpr/pool/work.c | 199 |
17 files changed, 1330 insertions, 0 deletions
diff --git a/winpr/libwinpr/pool/CMakeLists.txt b/winpr/libwinpr/pool/CMakeLists.txt new file mode 100644 index 0000000..2e25916 --- /dev/null +++ b/winpr/libwinpr/pool/CMakeLists.txt @@ -0,0 +1,41 @@ +# WinPR: Windows Portable Runtime +# libwinpr-pool cmake build script +# +# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +winpr_module_add( + synch.c + work.c + timer.c + io.c + cleanup_group.c + pool.c + pool.h + callback.c + callback_cleanup.c) + +winpr_library_add_private( + ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_DL_LIBS}) + +if(${CMAKE_SYSTEM_NAME} MATCHES SunOS) + winpr_library_add_private(rt) +endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() + + diff --git a/winpr/libwinpr/pool/ModuleOptions.cmake b/winpr/libwinpr/pool/ModuleOptions.cmake new file mode 100644 index 0000000..d5472ec --- /dev/null +++ b/winpr/libwinpr/pool/ModuleOptions.cmake @@ -0,0 +1,9 @@ + +set(MINWIN_LAYER "1") +set(MINWIN_GROUP "core") +set(MINWIN_MAJOR_VERSION "2") +set(MINWIN_MINOR_VERSION "1") +set(MINWIN_SHORT_NAME "threadpool") +set(MINWIN_LONG_NAME "Thread Pool API") +set(MODULE_LIBRARY_NAME "api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}") + diff --git a/winpr/libwinpr/pool/callback.c b/winpr/libwinpr/pool/callback.c new file mode 100644 index 0000000..1a4dde1 --- /dev/null +++ b/winpr/libwinpr/pool/callback.c @@ -0,0 +1,54 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Callback) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> +#include <winpr/library.h> + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static BOOL(WINAPI* pCallbackMayRunLong)(PTP_CALLBACK_INSTANCE pci); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { + pCallbackMayRunLong = (void*)GetProcAddress(kernel32, "CallbackMayRunLong"); + } + return TRUE; +} +#endif + +BOOL winpr_CallbackMayRunLong(PTP_CALLBACK_INSTANCE pci) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pCallbackMayRunLong) + return pCallbackMayRunLong(pci); +#endif + /* No default implementation */ + return FALSE; +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/callback_cleanup.c b/winpr/libwinpr/pool/callback_cleanup.c new file mode 100644 index 0000000..a4a24db --- /dev/null +++ b/winpr/libwinpr/pool/callback_cleanup.c @@ -0,0 +1,140 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Callback Clean-up) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> +#include <winpr/library.h> + +#include "pool.h" + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static VOID(WINAPI* pSetEventWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HANDLE evt); +static VOID(WINAPI* pReleaseSemaphoreWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HANDLE sem, + DWORD crel); +static VOID(WINAPI* pReleaseMutexWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HANDLE mut); +static VOID(WINAPI* pLeaveCriticalSectionWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, + PCRITICAL_SECTION pcs); +static VOID(WINAPI* pFreeLibraryWhenCallbackReturns)(PTP_CALLBACK_INSTANCE pci, HMODULE mod); +static VOID(WINAPI* pDisassociateCurrentThreadFromCallback)(PTP_CALLBACK_INSTANCE pci); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { + pSetEventWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "SetEventWhenCallbackReturns"); + pReleaseSemaphoreWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "ReleaseSemaphoreWhenCallbackReturns"); + pReleaseMutexWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "ReleaseMutexWhenCallbackReturns"); + pLeaveCriticalSectionWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "LeaveCriticalSectionWhenCallbackReturns"); + pFreeLibraryWhenCallbackReturns = + (void*)GetProcAddress(kernel32, "FreeLibraryWhenCallbackReturns"); + pDisassociateCurrentThreadFromCallback = + (void*)GetProcAddress(kernel32, "DisassociateCurrentThreadFromCallback"); + } + return TRUE; +} +#endif + +VOID SetEventWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE evt) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pSetEventWhenCallbackReturns) + { + pSetEventWhenCallbackReturns(pci, evt); + return; + } +#endif + /* No default implementation */ +} + +VOID ReleaseSemaphoreWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE sem, DWORD crel) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pReleaseSemaphoreWhenCallbackReturns) + { + pReleaseSemaphoreWhenCallbackReturns(pci, sem, crel); + return; + } +#endif + /* No default implementation */ +} + +VOID ReleaseMutexWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HANDLE mut) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pReleaseMutexWhenCallbackReturns) + { + pReleaseMutexWhenCallbackReturns(pci, mut); + return; + } +#endif + /* No default implementation */ +} + +VOID LeaveCriticalSectionWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, PCRITICAL_SECTION pcs) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pLeaveCriticalSectionWhenCallbackReturns) + { + pLeaveCriticalSectionWhenCallbackReturns(pci, pcs); + } +#endif + /* No default implementation */ +} + +VOID FreeLibraryWhenCallbackReturns(PTP_CALLBACK_INSTANCE pci, HMODULE mod) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pFreeLibraryWhenCallbackReturns) + { + pFreeLibraryWhenCallbackReturns(pci, mod); + return; + } +#endif + /* No default implementation */ +} + +VOID DisassociateCurrentThreadFromCallback(PTP_CALLBACK_INSTANCE pci) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pDisassociateCurrentThreadFromCallback) + { + pDisassociateCurrentThreadFromCallback(pci); + return; + } +#endif + /* No default implementation */ +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/cleanup_group.c b/winpr/libwinpr/pool/cleanup_group.c new file mode 100644 index 0000000..9c2569c --- /dev/null +++ b/winpr/libwinpr/pool/cleanup_group.c @@ -0,0 +1,140 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Clean-up Group) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> +#include <winpr/library.h> + +#include "pool.h" + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static PTP_CLEANUP_GROUP(WINAPI* pCreateThreadpoolCleanupGroup)(); +static VOID(WINAPI* pCloseThreadpoolCleanupGroupMembers)(PTP_CLEANUP_GROUP ptpcg, + BOOL fCancelPendingCallbacks, + PVOID pvCleanupContext); +static VOID(WINAPI* pCloseThreadpoolCleanupGroup)(PTP_CLEANUP_GROUP ptpcg); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + + if (kernel32) + { + pCreateThreadpoolCleanupGroup = + (void*)GetProcAddress(kernel32, "CreateThreadpoolCleanupGroup"); + pCloseThreadpoolCleanupGroupMembers = + (void*)GetProcAddress(kernel32, "CloseThreadpoolCleanupGroupMembers"); + pCloseThreadpoolCleanupGroup = + (void*)GetProcAddress(kernel32, "CloseThreadpoolCleanupGroup"); + } + + return TRUE; +} +#endif + +PTP_CLEANUP_GROUP winpr_CreateThreadpoolCleanupGroup(void) +{ + PTP_CLEANUP_GROUP cleanupGroup = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCreateThreadpoolCleanupGroup) + return pCreateThreadpoolCleanupGroup(); + + return cleanupGroup; +#else + cleanupGroup = (PTP_CLEANUP_GROUP)calloc(1, sizeof(TP_CLEANUP_GROUP)); + + if (!cleanupGroup) + return NULL; + + cleanupGroup->groups = ArrayList_New(FALSE); + + if (!cleanupGroup->groups) + { + free(cleanupGroup); + return NULL; + } + + return cleanupGroup; +#endif +} + +VOID winpr_SetThreadpoolCallbackCleanupGroup(PTP_CALLBACK_ENVIRON pcbe, PTP_CLEANUP_GROUP ptpcg, + PTP_CLEANUP_GROUP_CANCEL_CALLBACK pfng) +{ + pcbe->CleanupGroup = ptpcg; + pcbe->CleanupGroupCancelCallback = pfng; +#ifndef _WIN32 + pcbe->CleanupGroup->env = pcbe; +#endif +} + +VOID winpr_CloseThreadpoolCleanupGroupMembers(PTP_CLEANUP_GROUP ptpcg, BOOL fCancelPendingCallbacks, + PVOID pvCleanupContext) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCloseThreadpoolCleanupGroupMembers) + { + pCloseThreadpoolCleanupGroupMembers(ptpcg, fCancelPendingCallbacks, pvCleanupContext); + return; + } + +#else + + while (ArrayList_Count(ptpcg->groups) > 0) + { + PTP_WORK work = ArrayList_GetItem(ptpcg->groups, 0); + winpr_CloseThreadpoolWork(work); + } + +#endif +} + +VOID winpr_CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCloseThreadpoolCleanupGroup) + { + pCloseThreadpoolCleanupGroup(ptpcg); + return; + } + +#else + + if (ptpcg && ptpcg->groups) + ArrayList_Free(ptpcg->groups); + + if (ptpcg && ptpcg->env) + ptpcg->env->CleanupGroup = NULL; + + free(ptpcg); +#endif +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/io.c b/winpr/libwinpr/pool/io.c new file mode 100644 index 0000000..7442c37 --- /dev/null +++ b/winpr/libwinpr/pool/io.c @@ -0,0 +1,49 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (I/O) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> + +#ifdef WINPR_THREAD_POOL + +PTP_IO winpr_CreateThreadpoolIo(HANDLE fl, PTP_WIN32_IO_CALLBACK pfnio, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe) +{ + return NULL; +} + +VOID winpr_CloseThreadpoolIo(PTP_IO pio) +{ +} + +VOID winpr_StartThreadpoolIo(PTP_IO pio) +{ +} + +VOID winpr_CancelThreadpoolIo(PTP_IO pio) +{ +} + +VOID winpr_WaitForThreadpoolIoCallbacks(PTP_IO pio, BOOL fCancelPendingCallbacks) +{ +} + +#endif diff --git a/winpr/libwinpr/pool/pool.c b/winpr/libwinpr/pool/pool.c new file mode 100644 index 0000000..6c66b42 --- /dev/null +++ b/winpr/libwinpr/pool/pool.c @@ -0,0 +1,251 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Pool) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> +#include <winpr/library.h> + +#include "pool.h" + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static PTP_POOL(WINAPI* pCreateThreadpool)(PVOID reserved); +static VOID(WINAPI* pCloseThreadpool)(PTP_POOL ptpp); +static BOOL(WINAPI* pSetThreadpoolThreadMinimum)(PTP_POOL ptpp, DWORD cthrdMic); +static VOID(WINAPI* pSetThreadpoolThreadMaximum)(PTP_POOL ptpp, DWORD cthrdMost); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + if (kernel32) + { + pCreateThreadpool = (void*)GetProcAddress(kernel32, "CreateThreadpool"); + pCloseThreadpool = (void*)GetProcAddress(kernel32, "CloseThreadpool"); + pSetThreadpoolThreadMinimum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMinimum"); + pSetThreadpoolThreadMaximum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMaximum"); + } + return TRUE; +} +#endif + +static TP_POOL DEFAULT_POOL = { + 0, /* DWORD Minimum */ + 500, /* DWORD Maximum */ + NULL, /* wArrayList* Threads */ + NULL, /* wQueue* PendingQueue */ + NULL, /* HANDLE TerminateEvent */ + NULL, /* wCountdownEvent* WorkComplete */ +}; + +static DWORD WINAPI thread_pool_work_func(LPVOID arg) +{ + DWORD status = 0; + PTP_POOL pool = NULL; + PTP_WORK work = NULL; + HANDLE events[2]; + PTP_CALLBACK_INSTANCE callbackInstance = NULL; + + pool = (PTP_POOL)arg; + + events[0] = pool->TerminateEvent; + events[1] = Queue_Event(pool->PendingQueue); + + while (1) + { + status = WaitForMultipleObjects(2, events, FALSE, INFINITE); + + if (status == WAIT_OBJECT_0) + break; + + if (status != (WAIT_OBJECT_0 + 1)) + break; + + callbackInstance = (PTP_CALLBACK_INSTANCE)Queue_Dequeue(pool->PendingQueue); + + if (callbackInstance) + { + work = callbackInstance->Work; + work->WorkCallback(callbackInstance, work->CallbackParameter, work); + CountdownEvent_Signal(pool->WorkComplete, 1); + free(callbackInstance); + } + } + + ExitThread(0); + return 0; +} + +static void threads_close(void* thread) +{ + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +} + +static BOOL InitializeThreadpool(PTP_POOL pool) +{ + BOOL rc = FALSE; + wObject* obj = NULL; + HANDLE thread = NULL; + + if (pool->Threads) + return TRUE; + + pool->Minimum = 0; + pool->Maximum = 500; + + if (!(pool->PendingQueue = Queue_New(TRUE, -1, -1))) + goto fail; + + if (!(pool->WorkComplete = CountdownEvent_New(0))) + goto fail; + + if (!(pool->TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + goto fail; + + if (!(pool->Threads = ArrayList_New(TRUE))) + goto fail; + + obj = ArrayList_Object(pool->Threads); + obj->fnObjectFree = threads_close; + + for (int index = 0; index < 4; index++) + { + if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)pool, 0, NULL))) + { + goto fail; + } + + if (!ArrayList_Append(pool->Threads, thread)) + { + CloseHandle(thread); + goto fail; + } + } + + rc = TRUE; + +fail: + return rc; +} + +PTP_POOL GetDefaultThreadpool(void) +{ + PTP_POOL pool = NULL; + + pool = &DEFAULT_POOL; + + if (!InitializeThreadpool(pool)) + return NULL; + + return pool; +} + +PTP_POOL winpr_CreateThreadpool(PVOID reserved) +{ + PTP_POOL pool = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pCreateThreadpool) + return pCreateThreadpool(reserved); +#else + WINPR_UNUSED(reserved); +#endif + if (!(pool = (PTP_POOL)calloc(1, sizeof(TP_POOL)))) + return NULL; + + if (!InitializeThreadpool(pool)) + { + winpr_CloseThreadpool(pool); + return NULL; + } + + return pool; +} + +VOID winpr_CloseThreadpool(PTP_POOL ptpp) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pCloseThreadpool) + { + pCloseThreadpool(ptpp); + return; + } +#endif + SetEvent(ptpp->TerminateEvent); + + ArrayList_Free(ptpp->Threads); + Queue_Free(ptpp->PendingQueue); + CountdownEvent_Free(ptpp->WorkComplete); + CloseHandle(ptpp->TerminateEvent); + + { + TP_POOL empty = { 0 }; + *ptpp = empty; + } + + if (ptpp != &DEFAULT_POOL) + free(ptpp); +} + +BOOL winpr_SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic) +{ + HANDLE thread = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pSetThreadpoolThreadMinimum) + return pSetThreadpoolThreadMinimum(ptpp, cthrdMic); +#endif + ptpp->Minimum = cthrdMic; + + while (ArrayList_Count(ptpp->Threads) < ptpp->Minimum) + { + if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)ptpp, 0, NULL))) + { + return FALSE; + } + + if (!ArrayList_Append(ptpp->Threads, thread)) + { + CloseHandle(thread); + return FALSE; + } + } + + return TRUE; +} + +VOID winpr_SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + if (pSetThreadpoolThreadMaximum) + { + pSetThreadpoolThreadMaximum(ptpp, cthrdMost); + return; + } +#endif + ptpp->Maximum = cthrdMost; +} + +#endif /* WINPR_THREAD_POOL defined */ diff --git a/winpr/libwinpr/pool/pool.h b/winpr/libwinpr/pool/pool.h new file mode 100644 index 0000000..7e8cf4c --- /dev/null +++ b/winpr/libwinpr/pool/pool.h @@ -0,0 +1,122 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Pool) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_POOL_PRIVATE_H +#define WINPR_POOL_PRIVATE_H + +#include <winpr/windows.h> +#include <winpr/pool.h> +#include <winpr/synch.h> +#include <winpr/thread.h> +#include <winpr/collections.h> + +#if defined(_WIN32) +#if (_WIN32_WINNT < _WIN32_WINNT_WIN6) || defined(__MINGW32__) +struct S_TP_CALLBACK_INSTANCE +{ + PTP_WORK Work; +}; + +struct S_TP_POOL +{ + DWORD Minimum; + DWORD Maximum; + wArrayList* Threads; + wQueue* PendingQueue; + HANDLE TerminateEvent; + wCountdownEvent* WorkComplete; +}; + +struct S_TP_WORK +{ + PVOID CallbackParameter; + PTP_WORK_CALLBACK WorkCallback; + PTP_CALLBACK_ENVIRON CallbackEnvironment; +}; + +struct S_TP_TIMER +{ + void* dummy; +}; + +struct S_TP_WAIT +{ + void* dummy; +}; + +struct S_TP_IO +{ + void* dummy; +}; + +struct S_TP_CLEANUP_GROUP +{ + void* dummy; +}; + +#endif +#else +struct S_TP_CALLBACK_INSTANCE +{ + PTP_WORK Work; +}; + +struct S_TP_POOL +{ + DWORD Minimum; + DWORD Maximum; + wArrayList* Threads; + wQueue* PendingQueue; + HANDLE TerminateEvent; + wCountdownEvent* WorkComplete; +}; + +struct S_TP_WORK +{ + PVOID CallbackParameter; + PTP_WORK_CALLBACK WorkCallback; + PTP_CALLBACK_ENVIRON CallbackEnvironment; +}; + +struct S_TP_TIMER +{ + void* dummy; +}; + +struct S_TP_WAIT +{ + void* dummy; +}; + +struct S_TP_IO +{ + void* dummy; +}; + +struct S_TP_CLEANUP_GROUP +{ + wArrayList* groups; + PTP_CALLBACK_ENVIRON env; +}; + +#endif + +PTP_POOL GetDefaultThreadpool(void); + +#endif /* WINPR_POOL_PRIVATE_H */ diff --git a/winpr/libwinpr/pool/synch.c b/winpr/libwinpr/pool/synch.c new file mode 100644 index 0000000..1586a0e --- /dev/null +++ b/winpr/libwinpr/pool/synch.c @@ -0,0 +1,44 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Synch) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> + +#ifdef WINPR_THREAD_POOL + +PTP_WAIT winpr_CreateThreadpoolWait(PTP_WAIT_CALLBACK pfnwa, PVOID pv, PTP_CALLBACK_ENVIRON pcbe) +{ + return NULL; +} + +VOID winpr_CloseThreadpoolWait(PTP_WAIT pwa) +{ +} + +VOID winpr_SetThreadpoolWait(PTP_WAIT pwa, HANDLE h, PFILETIME pftTimeout) +{ +} + +VOID winpr_WaitForThreadpoolWaitCallbacks(PTP_WAIT pwa, BOOL fCancelPendingCallbacks) +{ +} + +#endif diff --git a/winpr/libwinpr/pool/test/CMakeLists.txt b/winpr/libwinpr/pool/test/CMakeLists.txt new file mode 100644 index 0000000..9758fff --- /dev/null +++ b/winpr/libwinpr/pool/test/CMakeLists.txt @@ -0,0 +1,30 @@ + +set(MODULE_NAME "TestPool") +set(MODULE_PREFIX "TEST_POOL") + +set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + +set(${MODULE_PREFIX}_TESTS + TestPoolIO.c + TestPoolSynch.c + TestPoolThread.c + TestPoolTimer.c + TestPoolWork.c) + +create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +target_link_libraries(${MODULE_NAME} winpr) + +set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + +foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName}) +endforeach() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") + diff --git a/winpr/libwinpr/pool/test/TestPoolIO.c b/winpr/libwinpr/pool/test/TestPoolIO.c new file mode 100644 index 0000000..d68586e --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolIO.c @@ -0,0 +1,8 @@ + +#include <winpr/crt.h> +#include <winpr/pool.h> + +int TestPoolIO(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolSynch.c b/winpr/libwinpr/pool/test/TestPoolSynch.c new file mode 100644 index 0000000..4d6f381 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolSynch.c @@ -0,0 +1,8 @@ + +#include <winpr/crt.h> +#include <winpr/pool.h> + +int TestPoolSynch(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolThread.c b/winpr/libwinpr/pool/test/TestPoolThread.c new file mode 100644 index 0000000..d006ae6 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolThread.c @@ -0,0 +1,41 @@ + +#include <winpr/crt.h> +#include <winpr/pool.h> + +/** + * Improve Scalability With New Thread Pool APIs: + * http://msdn.microsoft.com/en-us/magazine/cc16332.aspx + * + * Developing with Thread Pool Enhancements: + * http://msdn.microsoft.com/en-us/library/cc308561.aspx + * + * Introduction to the Windows Threadpool: + * http://blogs.msdn.com/b/harip/archive/2010/10/11/introduction-to-the-windows-threadpool-part-1.aspx + * http://blogs.msdn.com/b/harip/archive/2010/10/12/introduction-to-the-windows-threadpool-part-2.aspx + */ + +int TestPoolThread(int argc, char* argv[]) +{ + TP_POOL* pool = NULL; + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!(pool = CreateThreadpool(NULL))) + { + printf("CreateThreadpool failed\n"); + return -1; + } + + if (!SetThreadpoolThreadMinimum(pool, 8)) /* default is 0 */ + { + printf("SetThreadpoolThreadMinimum failed\n"); + return -1; + } + + SetThreadpoolThreadMaximum(pool, 64); /* default is 500 */ + + CloseThreadpool(pool); + + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolTimer.c b/winpr/libwinpr/pool/test/TestPoolTimer.c new file mode 100644 index 0000000..c749bc7 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolTimer.c @@ -0,0 +1,8 @@ + +#include <winpr/crt.h> +#include <winpr/pool.h> + +int TestPoolTimer(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/pool/test/TestPoolWork.c b/winpr/libwinpr/pool/test/TestPoolWork.c new file mode 100644 index 0000000..ec50a22 --- /dev/null +++ b/winpr/libwinpr/pool/test/TestPoolWork.c @@ -0,0 +1,136 @@ + +#include <winpr/wtypes.h> +#include <winpr/crt.h> +#include <winpr/pool.h> +#include <winpr/interlocked.h> + +static LONG count = 0; + +static void CALLBACK test_WorkCallback(PTP_CALLBACK_INSTANCE instance, void* context, PTP_WORK work) +{ + printf("Hello %s: %03" PRId32 " (thread: 0x%08" PRIX32 ")\n", (char*)context, + InterlockedIncrement(&count), GetCurrentThreadId()); + + for (int index = 0; index < 100; index++) + { + BYTE a[1024]; + BYTE b[1024]; + BYTE c[1024] = { 0 }; + + FillMemory(a, ARRAYSIZE(a), 0xAA); + FillMemory(b, ARRAYSIZE(b), 0xBB); + + CopyMemory(c, a, ARRAYSIZE(a)); + CopyMemory(c, b, ARRAYSIZE(b)); + } +} + +static BOOL test1(void) +{ + PTP_WORK work = NULL; + printf("Global Thread Pool\n"); + work = CreateThreadpoolWork(test_WorkCallback, "world", NULL); + + if (!work) + { + printf("CreateThreadpoolWork failure\n"); + return FALSE; + } + + /** + * You can post a work object one or more times (up to MAXULONG) without waiting for prior + * callbacks to complete. The callbacks will execute in parallel. To improve efficiency, the + * thread pool may throttle the threads. + */ + + for (int index = 0; index < 10; index++) + SubmitThreadpoolWork(work); + + WaitForThreadpoolWorkCallbacks(work, FALSE); + CloseThreadpoolWork(work); + return TRUE; +} + +static BOOL test2(void) +{ + BOOL rc = FALSE; + PTP_POOL pool = NULL; + PTP_WORK work = NULL; + PTP_CLEANUP_GROUP cleanupGroup = NULL; + TP_CALLBACK_ENVIRON environment; + printf("Private Thread Pool\n"); + + if (!(pool = CreateThreadpool(NULL))) + { + printf("CreateThreadpool failure\n"); + return FALSE; + } + + if (!SetThreadpoolThreadMinimum(pool, 4)) + { + printf("SetThreadpoolThreadMinimum failure\n"); + goto fail; + } + + SetThreadpoolThreadMaximum(pool, 8); + InitializeThreadpoolEnvironment(&environment); + SetThreadpoolCallbackPool(&environment, pool); + cleanupGroup = CreateThreadpoolCleanupGroup(); + + if (!cleanupGroup) + { + printf("CreateThreadpoolCleanupGroup failure\n"); + goto fail; + } + + SetThreadpoolCallbackCleanupGroup(&environment, cleanupGroup, NULL); + work = CreateThreadpoolWork(test_WorkCallback, "world", &environment); + + if (!work) + { + printf("CreateThreadpoolWork failure\n"); + goto fail; + } + + for (int index = 0; index < 10; index++) + SubmitThreadpoolWork(work); + + WaitForThreadpoolWorkCallbacks(work, FALSE); + rc = TRUE; +fail: + + if (cleanupGroup) + { + CloseThreadpoolCleanupGroupMembers(cleanupGroup, TRUE, NULL); + CloseThreadpoolCleanupGroup(cleanupGroup); + DestroyThreadpoolEnvironment(&environment); + /** + * See Remarks at + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682043(v=vs.85).aspx If there + * is a cleanup group associated with the work object, it is not necessary to call + * CloseThreadpoolWork ! calling the CloseThreadpoolCleanupGroupMembers function releases + * the work, wait, and timer objects associated with the cleanup group. + */ +#if 0 + CloseThreadpoolWork(work); // this would segfault, see comment above. */ +#endif + } + + CloseThreadpool(pool); + return rc; +} + +int TestPoolWork(int argc, char* argv[]) +{ + + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test1()) + return -1; + + if (!test2()) + return -1; + + return 0; +} diff --git a/winpr/libwinpr/pool/timer.c b/winpr/libwinpr/pool/timer.c new file mode 100644 index 0000000..a8aa1a7 --- /dev/null +++ b/winpr/libwinpr/pool/timer.c @@ -0,0 +1,50 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Timer) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/pool.h> + +#ifdef WINPR_THREAD_POOL + +PTP_TIMER winpr_CreateThreadpoolTimer(PTP_TIMER_CALLBACK pfnti, PVOID pv, PTP_CALLBACK_ENVIRON pcbe) +{ + return NULL; +} + +VOID winpr_CloseThreadpoolTimer(PTP_TIMER pti) +{ +} + +BOOL winpr_IsThreadpoolTimerSet(PTP_TIMER pti) +{ + return FALSE; +} + +VOID winpr_SetThreadpoolTimer(PTP_TIMER pti, PFILETIME pftDueTime, DWORD msPeriod, + DWORD msWindowLength) +{ +} + +VOID winpr_WaitForThreadpoolTimerCallbacks(PTP_TIMER pti, BOOL fCancelPendingCallbacks) +{ +} + +#endif diff --git a/winpr/libwinpr/pool/work.c b/winpr/libwinpr/pool/work.c new file mode 100644 index 0000000..e83f417 --- /dev/null +++ b/winpr/libwinpr/pool/work.c @@ -0,0 +1,199 @@ +/** + * WinPR: Windows Portable Runtime + * Thread Pool API (Work) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/assert.h> +#include <winpr/crt.h> +#include <winpr/pool.h> +#include <winpr/library.h> + +#include "pool.h" +#include "../log.h" +#define TAG WINPR_TAG("pool") + +#ifdef WINPR_THREAD_POOL + +#ifdef _WIN32 +static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT; +static PTP_WORK(WINAPI* pCreateThreadpoolWork)(PTP_WORK_CALLBACK pfnwk, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); +static VOID(WINAPI* pCloseThreadpoolWork)(PTP_WORK pwk); +static VOID(WINAPI* pSubmitThreadpoolWork)(PTP_WORK pwk); +static BOOL(WINAPI* pTrySubmitThreadpoolCallback)(PTP_SIMPLE_CALLBACK pfns, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe); +static VOID(WINAPI* pWaitForThreadpoolWorkCallbacks)(PTP_WORK pwk, BOOL fCancelPendingCallbacks); + +static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context) +{ + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + + if (kernel32) + { + pCreateThreadpoolWork = (void*)GetProcAddress(kernel32, "CreateThreadpoolWork"); + pCloseThreadpoolWork = (void*)GetProcAddress(kernel32, "CloseThreadpoolWork"); + pSubmitThreadpoolWork = (void*)GetProcAddress(kernel32, "SubmitThreadpoolWork"); + pTrySubmitThreadpoolCallback = + (void*)GetProcAddress(kernel32, "TrySubmitThreadpoolCallback"); + pWaitForThreadpoolWorkCallbacks = + (void*)GetProcAddress(kernel32, "WaitForThreadpoolWorkCallbacks"); + } + + return TRUE; +} +#endif + +static TP_CALLBACK_ENVIRON DEFAULT_CALLBACK_ENVIRONMENT = { + 1, /* Version */ + NULL, /* Pool */ + NULL, /* CleanupGroup */ + NULL, /* CleanupGroupCancelCallback */ + NULL, /* RaceDll */ + NULL, /* FinalizationCallback */ + { 0 } /* Flags */ +}; + +PTP_WORK winpr_CreateThreadpoolWork(PTP_WORK_CALLBACK pfnwk, PVOID pv, PTP_CALLBACK_ENVIRON pcbe) +{ + PTP_WORK work = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCreateThreadpoolWork) + return pCreateThreadpoolWork(pfnwk, pv, pcbe); + +#endif + work = (PTP_WORK)calloc(1, sizeof(TP_WORK)); + + if (work) + { + if (!pcbe) + { + pcbe = &DEFAULT_CALLBACK_ENVIRONMENT; + pcbe->Pool = GetDefaultThreadpool(); + } + + work->CallbackEnvironment = pcbe; + work->WorkCallback = pfnwk; + work->CallbackParameter = pv; +#ifndef _WIN32 + + if (pcbe->CleanupGroup) + ArrayList_Append(pcbe->CleanupGroup->groups, work); + +#endif + } + + return work; +} + +VOID winpr_CloseThreadpoolWork(PTP_WORK pwk) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pCloseThreadpoolWork) + { + pCloseThreadpoolWork(pwk); + return; + } + +#else + + WINPR_ASSERT(pwk); + WINPR_ASSERT(pwk->CallbackEnvironment); + if (pwk->CallbackEnvironment->CleanupGroup) + ArrayList_Remove(pwk->CallbackEnvironment->CleanupGroup->groups, pwk); + +#endif + free(pwk); +} + +VOID winpr_SubmitThreadpoolWork(PTP_WORK pwk) +{ + PTP_POOL pool = NULL; + PTP_CALLBACK_INSTANCE callbackInstance = NULL; +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pSubmitThreadpoolWork) + { + pSubmitThreadpoolWork(pwk); + return; + } + +#endif + + WINPR_ASSERT(pwk); + WINPR_ASSERT(pwk->CallbackEnvironment); + pool = pwk->CallbackEnvironment->Pool; + callbackInstance = (PTP_CALLBACK_INSTANCE)calloc(1, sizeof(TP_CALLBACK_INSTANCE)); + + if (callbackInstance) + { + callbackInstance->Work = pwk; + CountdownEvent_AddCount(pool->WorkComplete, 1); + if (!Queue_Enqueue(pool->PendingQueue, callbackInstance)) + free(callbackInstance); + } + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): Queue_Enqueue takes ownership of callbackInstance +} + +BOOL winpr_TrySubmitThreadpoolCallback(PTP_SIMPLE_CALLBACK pfns, PVOID pv, + PTP_CALLBACK_ENVIRON pcbe) +{ +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pTrySubmitThreadpoolCallback) + return pTrySubmitThreadpoolCallback(pfns, pv, pcbe); + +#endif + WLog_ERR(TAG, "TrySubmitThreadpoolCallback is not implemented"); + return FALSE; +} + +VOID winpr_WaitForThreadpoolWorkCallbacks(PTP_WORK pwk, BOOL fCancelPendingCallbacks) +{ + HANDLE event = NULL; + PTP_POOL pool = NULL; + +#ifdef _WIN32 + InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL); + + if (pWaitForThreadpoolWorkCallbacks) + { + pWaitForThreadpoolWorkCallbacks(pwk, fCancelPendingCallbacks); + return; + } + +#endif + WINPR_ASSERT(pwk); + WINPR_ASSERT(pwk->CallbackEnvironment); + + pool = pwk->CallbackEnvironment->Pool; + WINPR_ASSERT(pool); + + event = CountdownEvent_WaitHandle(pool->WorkComplete); + + if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) + WLog_ERR(TAG, "error waiting on work completion"); +} + +#endif /* WINPR_THREAD_POOL defined */ |