mirror of
https://github.com/reactos/reactos.git
synced 2025-03-10 10:14:44 +00:00
401 lines
11 KiB
C++
401 lines
11 KiB
C++
![]() |
/*++
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|