summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/pool
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/pool
parentInitial commit. (diff)
downloadfreerdp3-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.txt41
-rw-r--r--winpr/libwinpr/pool/ModuleOptions.cmake9
-rw-r--r--winpr/libwinpr/pool/callback.c54
-rw-r--r--winpr/libwinpr/pool/callback_cleanup.c140
-rw-r--r--winpr/libwinpr/pool/cleanup_group.c140
-rw-r--r--winpr/libwinpr/pool/io.c49
-rw-r--r--winpr/libwinpr/pool/pool.c251
-rw-r--r--winpr/libwinpr/pool/pool.h122
-rw-r--r--winpr/libwinpr/pool/synch.c44
-rw-r--r--winpr/libwinpr/pool/test/CMakeLists.txt30
-rw-r--r--winpr/libwinpr/pool/test/TestPoolIO.c8
-rw-r--r--winpr/libwinpr/pool/test/TestPoolSynch.c8
-rw-r--r--winpr/libwinpr/pool/test/TestPoolThread.c41
-rw-r--r--winpr/libwinpr/pool/test/TestPoolTimer.c8
-rw-r--r--winpr/libwinpr/pool/test/TestPoolWork.c136
-rw-r--r--winpr/libwinpr/pool/timer.c50
-rw-r--r--winpr/libwinpr/pool/work.c199
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 */