Blame winpr/libwinpr/pool/pool.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * WinPR: Windows Portable Runtime
Packit 1fb8d4
 * Thread Pool API (Pool)
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/pool.h>
Packit 1fb8d4
#include <winpr/library.h>
Packit 1fb8d4
Packit 1fb8d4
#include "pool.h"
Packit 1fb8d4
Packit 1fb8d4
#ifdef WINPR_THREAD_POOL
Packit 1fb8d4
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
static INIT_ONCE init_once_module = INIT_ONCE_STATIC_INIT;
Packit Service 5a9772
static PTP_POOL(WINAPI* pCreateThreadpool)(PVOID reserved);
Packit Service 5a9772
static VOID(WINAPI* pCloseThreadpool)(PTP_POOL ptpp);
Packit Service 5a9772
static BOOL(WINAPI* pSetThreadpoolThreadMinimum)(PTP_POOL ptpp, DWORD cthrdMic);
Packit Service 5a9772
static VOID(WINAPI* pSetThreadpoolThreadMaximum)(PTP_POOL ptpp, DWORD cthrdMost);
Packit 1fb8d4
Packit Service 5a9772
static BOOL CALLBACK init_module(PINIT_ONCE once, PVOID param, PVOID* context)
Packit 1fb8d4
{
Packit 1fb8d4
	HMODULE kernel32 = LoadLibraryA("kernel32.dll");
Packit 1fb8d4
	if (kernel32)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCreateThreadpool = (void*)GetProcAddress(kernel32, "CreateThreadpool");
Packit 1fb8d4
		pCloseThreadpool = (void*)GetProcAddress(kernel32, "CloseThreadpool");
Packit 1fb8d4
		pSetThreadpoolThreadMinimum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMinimum");
Packit 1fb8d4
		pSetThreadpoolThreadMaximum = (void*)GetProcAddress(kernel32, "SetThreadpoolThreadMaximum");
Packit 1fb8d4
	}
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit Service 5a9772
static TP_POOL DEFAULT_POOL = {
Packit 1fb8d4
	0,    /* DWORD Minimum */
Packit 1fb8d4
	500,  /* DWORD Maximum */
Packit 1fb8d4
	NULL, /* wArrayList* Threads */
Packit 1fb8d4
	NULL, /* wQueue* PendingQueue */
Packit 1fb8d4
	NULL, /* HANDLE TerminateEvent */
Packit 1fb8d4
	NULL, /* wCountdownEvent* WorkComplete */
Packit 1fb8d4
};
Packit 1fb8d4
Packit 1fb8d4
static DWORD WINAPI thread_pool_work_func(LPVOID arg)
Packit 1fb8d4
{
Packit 1fb8d4
	DWORD status;
Packit 1fb8d4
	PTP_POOL pool;
Packit 1fb8d4
	PTP_WORK work;
Packit 1fb8d4
	HANDLE events[2];
Packit 1fb8d4
	PTP_CALLBACK_INSTANCE callbackInstance;
Packit 1fb8d4
Packit Service 5a9772
	pool = (PTP_POOL)arg;
Packit 1fb8d4
Packit 1fb8d4
	events[0] = pool->TerminateEvent;
Packit 1fb8d4
	events[1] = Queue_Event(pool->PendingQueue);
Packit 1fb8d4
Packit 1fb8d4
	while (1)
Packit 1fb8d4
	{
Packit 1fb8d4
		status = WaitForMultipleObjects(2, events, FALSE, INFINITE);
Packit 1fb8d4
Packit 1fb8d4
		if (status == WAIT_OBJECT_0)
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		if (status != (WAIT_OBJECT_0 + 1))
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit Service 5a9772
		callbackInstance = (PTP_CALLBACK_INSTANCE)Queue_Dequeue(pool->PendingQueue);
Packit 1fb8d4
Packit 1fb8d4
		if (callbackInstance)
Packit 1fb8d4
		{
Packit 1fb8d4
			work = callbackInstance->Work;
Packit 1fb8d4
			work->WorkCallback(callbackInstance, work->CallbackParameter, work);
Packit 1fb8d4
			CountdownEvent_Signal(pool->WorkComplete, 1);
Packit 1fb8d4
			free(callbackInstance);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	ExitThread(0);
Packit 1fb8d4
	return 0;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static void threads_close(void* thread)
Packit 1fb8d4
{
Packit 1fb8d4
	WaitForSingleObject(thread, INFINITE);
Packit 1fb8d4
	CloseHandle(thread);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL InitializeThreadpool(PTP_POOL pool)
Packit 1fb8d4
{
Packit 1fb8d4
	int index;
Packit 1fb8d4
	HANDLE thread;
Packit 1fb8d4
Packit 1fb8d4
	if (pool->Threads)
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
Packit 1fb8d4
	pool->Minimum = 0;
Packit 1fb8d4
	pool->Maximum = 500;
Packit 1fb8d4
Packit 1fb8d4
	if (!(pool->PendingQueue = Queue_New(TRUE, -1, -1)))
Packit 1fb8d4
		goto fail_queue_new;
Packit 1fb8d4
Packit 1fb8d4
	if (!(pool->WorkComplete = CountdownEvent_New(0)))
Packit 1fb8d4
		goto fail_countdown_event;
Packit 1fb8d4
Packit 1fb8d4
	if (!(pool->TerminateEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
Packit 1fb8d4
		goto fail_terminate_event;
Packit 1fb8d4
Packit 1fb8d4
	if (!(pool->Threads = ArrayList_New(TRUE)))
Packit 1fb8d4
		goto fail_thread_array;
Packit 1fb8d4
Packit 1fb8d4
	pool->Threads->object.fnObjectFree = threads_close;
Packit 1fb8d4
Packit 1fb8d4
	for (index = 0; index < 4; index++)
Packit 1fb8d4
	{
Packit Service 5a9772
		if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)pool, 0, NULL)))
Packit 1fb8d4
		{
Packit 1fb8d4
			goto fail_create_threads;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (ArrayList_Add(pool->Threads, thread) < 0)
Packit 1fb8d4
			goto fail_create_threads;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
Packit 1fb8d4
fail_create_threads:
Packit 1fb8d4
	SetEvent(pool->TerminateEvent);
Packit 1fb8d4
	ArrayList_Free(pool->Threads);
Packit 1fb8d4
	pool->Threads = NULL;
Packit 1fb8d4
fail_thread_array:
Packit 1fb8d4
	CloseHandle(pool->TerminateEvent);
Packit 1fb8d4
	pool->TerminateEvent = NULL;
Packit 1fb8d4
fail_terminate_event:
Packit 1fb8d4
	CountdownEvent_Free(pool->WorkComplete);
Packit 1fb8d4
	pool->WorkComplete = NULL;
Packit 1fb8d4
fail_countdown_event:
Packit 1fb8d4
	Queue_Free(pool->PendingQueue);
Packit 1fb8d4
	pool->WorkComplete = NULL;
Packit 1fb8d4
fail_queue_new:
Packit 1fb8d4
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
PTP_POOL GetDefaultThreadpool()
Packit 1fb8d4
{
Packit 1fb8d4
	PTP_POOL pool = NULL;
Packit 1fb8d4
Packit 1fb8d4
	pool = &DEFAULT_POOL;
Packit 1fb8d4
Packit 1fb8d4
	if (!InitializeThreadpool(pool))
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	return pool;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
PTP_POOL winpr_CreateThreadpool(PVOID reserved)
Packit 1fb8d4
{
Packit 1fb8d4
	PTP_POOL pool = NULL;
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
	InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
Packit 1fb8d4
	if (pCreateThreadpool)
Packit 1fb8d4
		return pCreateThreadpool(reserved);
Packit Service 5a9772
#else
Packit Service 5a9772
	WINPR_UNUSED(reserved);
Packit 1fb8d4
#endif
Packit Service 5a9772
	if (!(pool = (PTP_POOL)calloc(1, sizeof(TP_POOL))))
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (!InitializeThreadpool(pool))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(pool);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return pool;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
VOID winpr_CloseThreadpool(PTP_POOL ptpp)
Packit 1fb8d4
{
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
	InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
Packit 1fb8d4
	if (pCloseThreadpool)
Packit 1fb8d4
	{
Packit 1fb8d4
		pCloseThreadpool(ptpp);
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
#endif
Packit 1fb8d4
	SetEvent(ptpp->TerminateEvent);
Packit 1fb8d4
Packit 1fb8d4
	ArrayList_Free(ptpp->Threads);
Packit 1fb8d4
	Queue_Free(ptpp->PendingQueue);
Packit 1fb8d4
	CountdownEvent_Free(ptpp->WorkComplete);
Packit 1fb8d4
	CloseHandle(ptpp->TerminateEvent);
Packit 1fb8d4
Packit 1fb8d4
	if (ptpp == &DEFAULT_POOL)
Packit 1fb8d4
	{
Packit 1fb8d4
		ptpp->Threads = NULL;
Packit 1fb8d4
		ptpp->PendingQueue = NULL;
Packit 1fb8d4
		ptpp->WorkComplete = NULL;
Packit 1fb8d4
		ptpp->TerminateEvent = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		free(ptpp);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_SetThreadpoolThreadMinimum(PTP_POOL ptpp, DWORD cthrdMic)
Packit 1fb8d4
{
Packit 1fb8d4
	HANDLE thread;
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
	InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
Packit 1fb8d4
	if (pSetThreadpoolThreadMinimum)
Packit 1fb8d4
		return pSetThreadpoolThreadMinimum(ptpp, cthrdMic);
Packit 1fb8d4
#endif
Packit 1fb8d4
	ptpp->Minimum = cthrdMic;
Packit 1fb8d4
Packit Service 5a9772
	while (ArrayList_Count(ptpp->Threads) < (INT64)ptpp->Minimum)
Packit 1fb8d4
	{
Packit Service 5a9772
		if (!(thread = CreateThread(NULL, 0, thread_pool_work_func, (void*)ptpp, 0, NULL)))
Packit 1fb8d4
		{
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		if (ArrayList_Add(ptpp->Threads, thread) < 0)
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
VOID winpr_SetThreadpoolThreadMaximum(PTP_POOL ptpp, DWORD cthrdMost)
Packit 1fb8d4
{
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
	InitOnceExecuteOnce(&init_once_module, init_module, NULL, NULL);
Packit 1fb8d4
	if (pSetThreadpoolThreadMaximum)
Packit 1fb8d4
	{
Packit 1fb8d4
		pSetThreadpoolThreadMaximum(ptpp, cthrdMost);
Packit 1fb8d4
		return;
Packit 1fb8d4
	}
Packit 1fb8d4
#endif
Packit 1fb8d4
	ptpp->Maximum = cthrdMost;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif /* WINPR_THREAD_POOL defined */