/** * WinPR: Windows Portable Runtime * Thread Pool API (Pool) * * Copyright 2012 Marc-Andre Moreau * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "pool.h" #ifdef _WIN32 static BOOL module_initialized = FALSE; static BOOL module_available = FALSE; static HMODULE kernel32_module = NULL; 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 void module_init() { if (module_initialized) return; kernel32_module = LoadLibraryA("kernel32.dll"); module_initialized = TRUE; if (!kernel32_module) return; module_available = TRUE; pCreateThreadpool = (void*) GetProcAddress(kernel32_module, "CreateThreadpool"); pCloseThreadpool = (void*) GetProcAddress(kernel32_module, "CloseThreadpool"); pSetThreadpoolThreadMinimum = (void*) GetProcAddress(kernel32_module, "SetThreadpoolThreadMinimum"); pSetThreadpoolThreadMaximum = (void*) GetProcAddress(kernel32_module, "SetThreadpoolThreadMaximum"); } #else static TP_POOL DEFAULT_POOL = { 0, /* Minimum */ 500, /* Maximum */ NULL, /* Threads */ 0, /* ThreadCount */ }; static void* thread_pool_work_func(void* arg) { DWORD status; PTP_POOL pool; PTP_WORK work; HANDLE events[2]; PTP_CALLBACK_INSTANCE callbackInstance; 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); } } return NULL; } static void threads_close(void *thread) { CloseHandle(thread); } void InitializeThreadpool(PTP_POOL pool) { int index; HANDLE thread; if (!pool->Threads) { pool->Minimum = 0; pool->Maximum = 500; pool->Threads = ArrayList_New(TRUE); pool->Threads->object.fnObjectFree = threads_close; pool->PendingQueue = Queue_New(TRUE, -1, -1); pool->WorkComplete = CountdownEvent_New(0); pool->TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL); for (index = 0; index < 4; index++) { thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) thread_pool_work_func, (void*) pool, 0, NULL); ArrayList_Add(pool->Threads, thread); } } } PTP_POOL GetDefaultThreadpool() { PTP_POOL pool = NULL; pool = &DEFAULT_POOL; InitializeThreadpool(pool); return pool; } #endif #ifdef WINPR_THREAD_POOL PTP_POOL CreateThreadpool(PVOID reserved) { PTP_POOL pool = NULL; #ifdef _WIN32 module_init(); if (pCreateThreadpool) return pCreateThreadpool(reserved); #else pool = (PTP_POOL) calloc(1, sizeof(TP_POOL)); if (pool) InitializeThreadpool(pool); #endif return pool; } VOID CloseThreadpool(PTP_POOL ptpp) { #ifdef _WIN32 module_init(); if (pCloseThreadpool) pCloseThreadpool(ptpp); #else int index; HANDLE thread; SetEvent(ptpp->TerminateEvent); index = ArrayList_Count(ptpp->Threads) - 1; while (index >= 0) { thread = (HANDLE) ArrayList_GetItem(ptpp->Threads, index); WaitForSingleObject(thread, INFINITE); index--; } ArrayList_Free(ptpp->Threads); Queue_Free(ptpp->PendingQueue); CountdownEvent_Free(ptpp->WorkComplete); CloseHandle(ptpp->TerminateEvent); free(ptpp); #endif } BOOL SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic) { #ifdef _WIN32 module_init(); if (pSetThreadpoolThreadMinimum) return pSetThreadpoolThreadMinimum(ptpp, cthrdMic); #else HANDLE thread; ptpp->Minimum = cthrdMic; while (ArrayList_Count(ptpp->Threads) < ptpp->Minimum) { thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) thread_pool_work_func, (void*) ptpp, 0, NULL); ArrayList_Add(ptpp->Threads, thread); } #endif return TRUE; } VOID SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost) { #ifdef _WIN32 module_init(); if (pSetThreadpoolThreadMaximum) pSetThreadpoolThreadMaximum(ptpp, cthrdMost); #else ptpp->Maximum = cthrdMost; #endif } #endif /* dummy */ void winpr_pool_dummy() { }