Windows API has a very beautiful Thread Pool API that allows to execute work tasks, wait for waitable handles or completion ports and schedule timer callbacks very efficiently.
This API is designed for C programming language, but C++ provides more useful features, that make working with such APIs easier. This library is a wrapper for Thread Pool API, that provides a C++ abstraction and makes easier to manage all threadpool resources.
For further information about internal API refer to Microsoft documentation.
TODO :)
Usage of ntp
is as simple as possible.
Just add the following lines in your CMakeLists.txt:
target_link_libraries(your-project PRIVATE ntp)
target_include_directories(your-project PRIVATE "/path/to/ntp/include")
After that you can just include header and use library features:
#include "ntp.hpp"
#include "ntp.hpp"
void DoWork()
{
ntp::SystemThreadPool pool;
pool.SubmitWork([]() {
// Do some long operation.
});
// Do some other work.
pool.WaitWorks();
}
#include "ntp.hpp"
void DoWork(const std::vector<Callable>& tasks)
{
//
// For RpcTestCancel refer to: https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-rpctestcancel
//
ntp::SystemThreadPool pool(RpcTestCancel);
for (const auto& task : tasks)
{
pool.SumbitWork(task);
}
// Do some other work.
const bool completed = pool.WaitWorks();
if (!completed)
{
//
// If WaitWorks returns false, then external cancellation test was
// triggered while tasks were awaited.
//
HandleIncompleteWork();
}
}
Callbacks may optionally accept PTP_CALLBACK_INSTANCE
as their first argument.
It allows user to call the following functions:
FreeLibraryWhenCallbackReturns
LeaveCriticalSectionWhenCallbackReturns
ReleaseMutexWhenCallbackReturns
ReleaseSemaphoreWhenCallbackReturns
SetEventWhenCallbackReturns
#include "ntp.hpp"
void DoWork(HANDLE event)
{
ntp::SystemThreadPool pool;
pool.SubmitWork([event](PTP_CALLBACK_INSTANCE instance) {
SetEventWhenCallbackReturns(instance, event);
// Do some work. After callback ends event will be set.
});
// Do further work...
}
#include "ntp.hpp"
class ProcessManager final
{
public:
void LaunchProcess(const wchar_t* command_line)
{
STARTUPINFO si{ sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi{};
//
// Don't handle errors for simplicity
//
CreateProcess(nullptr, command_line, nullptr, nullptr,
FALSE, 0, nullptr, nullptr, &si, &pi);
//
// Set wait callback
//
pool_.SubmitWait(pi.hProcess, OnProcessCompleted,
pi.hProcess /* Pass handle to callback too */); // (1)
}
private:
static void OnProcessCompleted(TP_WAIT_RESULT wait_result, HANDLE process)
{
if (wait_result == WAIT_OBJECT_0)
{
// Process has been ended
}
//
// Close process handle, passed as parameter at (1)
//
CloseHandle(process);
}
private:
ntp::SystemThreadPool pool_;
}
ntp
library supports custom logger callback, that accepts severity and a message string.
Severity has values from ntp::logger::Severity
enumeration.
#include "ntp.hpp"
void TraceCallback(ntp::logger::Severity severity, const wchar_t* message)
{
if (severity >= ntp::logger::Severity::kNormal)
{
//
// Skip ntp::logger::Severity::kExtended
//
std::wcerr << message << L'\n';
}
}
int main()
{
ntp::logger::SetLogger(TraceCallback);
// Do work with thread pools
}
#include "ntp.hpp"
void DoWork()
{
//
// You can create your own pool with, for
// instance, 4 to 16 threads
//
ntp::ThreadPool pool(4, 16);
pool.SubmitWork([]() {
// Do more work
});
// ...
}
- Work callbacks (
PTP_WORK
) - Wait callbacks (
PTP_WAIT
) - Timer callbacks (
PTP_TIMER
) - IO callbacks (
PTP_IO
) - Tests
- Cute documentation
- Support for
PTP_CALLBACK_INSTANCE
as optional first argument in callbacks - Refactor tests
- Extend code coverage
- (Optional) Alpc callbacks (
PTP_ALPC
)