reactos/sdk/lib/drivers/wdf/shared/irphandlers/pnp/um/fxinterruptthreadpoolum.cpp

401 lines
11 KiB
C++
Raw Normal View History

/*++
Copyright (c) Microsoft Corporation
Module Name:
FxInterruptThreadpoolUm.cpp
Abstract:
Threadpool functions for interrupt handling
Author:
Environment:
User mode only
Revision History:
--*/
#include "fxmin.hpp"
#include "FxInterruptThreadpoolUm.hpp"
extern "C" {
#include "FxInterruptThreadpoolUm.tmh"
}
#define STRSAFE_LIB
#include <strsafe.h>
FxInterruptThreadpool::FxInterruptThreadpool(
PFX_DRIVER_GLOBALS FxDriverGlobals
) :
FxGlobalsStump(FxDriverGlobals),
m_Pool(NULL),
m_MinimumThreadCount(MINIMUM_THREAD_COUNT_DEFAULT)
{
InitializeThreadpoolEnvironment(&m_CallbackEnvironment);
}
FxInterruptThreadpool::~FxInterruptThreadpool()
{
//
// close pool
//
if (m_Pool != NULL) {
CloseThreadpool(m_Pool);
m_Pool = NULL;
}
DestroyThreadpoolEnvironment(&m_CallbackEnvironment);
}
HRESULT
FxInterruptThreadpool::_CreateAndInit(
_In_ PFX_DRIVER_GLOBALS DriverGlobals,
_Out_ FxInterruptThreadpool** ppThreadpool
)
{
HRESULT hr;
FxInterruptThreadpool* pool = NULL;
FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NOT_NULL(ppThreadpool),
DriverGlobals->Public.DriverName);
*ppThreadpool = NULL;
pool = new (DriverGlobals) FxInterruptThreadpool(DriverGlobals);
if (pool == NULL)
{
hr = ERROR_NOT_ENOUGH_MEMORY;
DoTraceLevelMessage(DriverGlobals,
TRACE_LEVEL_ERROR, TRACINGPNP,
"FxInterruptThreadpool creation failed, "
"%!hresult!", hr);
return hr;
}
hr = pool->Initialize();
if (SUCCEEDED(hr))
{
*ppThreadpool = pool;
}
else {
delete pool;
}
return hr;
}
HRESULT
FxInterruptThreadpool::Initialize(
)
{
HRESULT hr = S_OK;
DWORD error;
BOOL bRet;
//
// Create a thread pool using win32 APIs
//
m_Pool = CreateThreadpool(NULL);
if (m_Pool == NULL)
{
error = GetLastError();
hr = HRESULT_FROM_WIN32(error);
DoTraceLevelMessage(GetDriverGlobals(),
TRACE_LEVEL_ERROR, TRACINGPNP,
"Threadpool creation failed, %!winerr!", error);
return hr;
}
//
// Set maximum thread count to equal the total number of interrupts.
// Set minimum thread count (persistent threads) to equal the lower of
// number of interrupt and number of processors.
//
// We want minimum number of persistent threads to be at least equal to the
// number of interrupt objects so that there is no delay in servicing the
// interrupt if there are processors available (the delay can come due to
// thread pool delay in allocating and initializing a new thread). However,
// there is not much benefit in having more persistent threads than
// processors, as threads would have to wait for processor to become
// available anyways. Therefore, we chose to have the number of persistent
// equal to the Min(number of interrupts, number of processors).
//
// In the current design, if there are more interrupts than
// processors, as soon as one thread finishes servicing the interrupt, both
// the thread and processor will be available to service the next queued
// interrupt. Note that thread pool will queue all the waits and will
// satisfy them in a FIFO manner.
//
// Since we don't know the number of interrupts in the beginning, we will
// start with one and update it when we know the actual number of interrupts
// after processing driver's OnPrepareHardware callback.
//
// Note on interrupt servicing:
// When fx connects the interrupt, it queues an event-based wait block
// to thread pool for each interrupt, so that the interrupt can get serviced
// using one of the thread pool threads when the auto-reset event shared
// with reflector is set by reflector in the DpcForIsr routine queued by Isr.
// While the interrupt is being serviced by one of the threads, no wait block
// is queued to thread pool for that interrupt, so arrival of same interrupt
// signals the event in DpcForIsr but doesn't cause new threads to pick up
// the servicing. Note that other interrupts still have their wait blocks
// queued so they will be serviced as they arrive.
//
// When previous servicing is over (i.e. the ISR callback has been
// invoked and it has returned), Fx queues another wait block to thread pool
// for that interrupt. If the event is already signalled, it would result in
// the same thread (or another) picking up servicing immediately.
//
// This means the interrupt ISR routine can never run concurrently
// for same interrupt. Therefore, there is no need to have more than one
// thread for each interrupt.
//
SetThreadpoolThreadMaximum(m_Pool, 1);
//
// Create one persistent thread since atleast one interrupt is the most
// likely scenario. We will update this number to have either the same
// number of threads as there are interrupts, or the number of
// processors on the machine.
//
bRet = SetThreadpoolThreadMinimum(m_Pool, m_MinimumThreadCount);
if (bRet == FALSE) {
error = GetLastError();
hr = HRESULT_FROM_WIN32(error);
DoTraceLevelMessage(GetDriverGlobals(),
TRACE_LEVEL_ERROR, TRACINGPNP,
"%!FUNC!: Failed to set minimum threads (%d) in threadpool,"
" %!winerr!", m_MinimumThreadCount, error);
goto cleanup;
}
//
// Associate thread pool with callback environment.
//
SetThreadpoolCallbackPool(&m_CallbackEnvironment, m_Pool);
cleanup:
if (FAILED(hr)) {
CloseThreadpool(m_Pool);
m_Pool = NULL;
}
return hr;
}
HRESULT
FxInterruptThreadpool::UpdateThreadPoolThreadLimits(
_In_ ULONG InterruptCount
)
{
BOOL bRet;
HRESULT hr = S_OK;
SYSTEM_INFO sysInfo;
ULONG minThreadCount = 0;
ULONG procs;
DWORD error;
FX_VERIFY(INTERNAL, CHECK_NOT_NULL(m_Pool));
//
// if there are more than one interrupts then we need to update minimum
// thread count.
//
if (m_MinimumThreadCount >= InterruptCount) {
//
// nothing to do
//
return S_OK;
}
//
// We want to have number of minimum persistent threads
// = Min(number of interrupts, number of processors).
// See comments in Initialize routine for details.
//
GetSystemInfo(&sysInfo);
procs = sysInfo.dwNumberOfProcessors;
minThreadCount = min(InterruptCount, procs);
if (m_MinimumThreadCount < minThreadCount) {
//
// Set threadpool min
//
bRet = SetThreadpoolThreadMinimum(m_Pool, minThreadCount);
if (bRet == FALSE) {
error = GetLastError();
hr = HRESULT_FROM_WIN32(error);
DoTraceLevelMessage(GetDriverGlobals(),
TRACE_LEVEL_ERROR, TRACINGPNP,
"Failed to set minimum threads in threadpool,"
" TP_POOL 0x%p to %d %!winerr!", m_Pool, minThreadCount,
error);
return hr;
}
m_MinimumThreadCount = minThreadCount;
}
//
// set thread pool max to max number of interrupts
//
SetThreadpoolThreadMaximum(m_Pool, InterruptCount);
DoTraceLevelMessage(GetDriverGlobals(),
TRACE_LEVEL_ERROR, TRACINGPNP,
"Threads in thread pool TP_POOL 0x%p updated"
" to Max %d Min %d threads", m_Pool,
InterruptCount, minThreadCount);
return hr;
}
FxInterruptWaitblock::~FxInterruptWaitblock(
VOID
)
{
//
// close the thread pool wait structure
//
if (m_Wait) {
//
// Make sure no event is registered.
//
ClearThreadpoolWait();
//
// Wait for all the callbacks to finish.
//
WaitForOutstandingCallbackToComplete();
//
// close the wait
//
CloseThreadpoolWait();
m_Wait = NULL;
}
//
// close event handle
//
if (m_Event) {
CloseHandle(m_Event);
m_Event = NULL;
}
}
HRESULT
FxInterruptWaitblock::_CreateAndInit(
_In_ FxInterruptThreadpool* Threadpool,
_In_ FxInterrupt* Interrupt,
_In_ PTP_WAIT_CALLBACK WaitCallback,
_Out_ FxInterruptWaitblock** Waitblock
)
{
HRESULT hr = S_OK;
FxInterruptWaitblock* waitblock = NULL;
PFX_DRIVER_GLOBALS driverGlobals;
FX_VERIFY(INTERNAL, CHECK_NOT_NULL(Waitblock));
*Waitblock = NULL;
driverGlobals = Interrupt->GetDriverGlobals();
//
// create an instance of interrupt wait block
//
waitblock = new (driverGlobals) FxInterruptWaitblock(driverGlobals);
if (waitblock == NULL) {
hr = E_OUTOFMEMORY;
DoTraceLevelMessage(driverGlobals,
TRACE_LEVEL_ERROR, TRACINGPNP,
"Waitblock creation failed %!hresult!", hr);
goto exit;
}
hr = waitblock->Initialize(Threadpool,
Interrupt,
WaitCallback);
if (SUCCEEDED(hr)) {
*Waitblock = waitblock;
}
else {
DoTraceLevelMessage(driverGlobals,
TRACE_LEVEL_ERROR, TRACINGPNP,
"Waitblock init failed %!hresult!", hr);
}
exit:
if(FAILED(hr) && waitblock != NULL) {
delete waitblock;
}
return hr;
}
HRESULT
FxInterruptWaitblock::Initialize(
__in FxInterruptThreadpool* Threadpool,
__in FxInterrupt* Interrupt,
__in PTP_WAIT_CALLBACK WaitCallback
)
{
HRESULT hr = S_OK;
DWORD error;
//
// create a per-interrupt auto-reset event, non-signalled to begin with.
//
m_Event = CreateEvent(
NULL, // LPSECURITY_ATTRIBUTES lpEventAttributes,
FALSE, // BOOL bManualReset,
FALSE, // BOOL bInitialState,
NULL // LPCTSTR lpName
);
if (m_Event == NULL) {
error = GetLastError();
hr = HRESULT_FROM_WIN32(error);
DoTraceLevelMessage(GetDriverGlobals(),
TRACE_LEVEL_ERROR, TRACINGPNP,
"Event creation failed for FxInterrupt object"
" %!winerr!", error);
goto exit;
}
//
// create a per-interrupt thread pool wait structure. This wait structure is
// needed to associate an event-based wait callback with threadpool.
//
m_Wait = Threadpool->CreateThreadpoolWait(WaitCallback,
Interrupt);
if (m_Wait == NULL) {
error = GetLastError();
hr = HRESULT_FROM_WIN32(error);
DoTraceLevelMessage(GetDriverGlobals(),
TRACE_LEVEL_ERROR, TRACINGPNP,
"Event creation failed for FxInterrupt object"
" %!winerr!", error);
goto exit;
}
exit:
return hr;
}