Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1420 lines
36 KiB

/*++
Copyright (c) 1998-2002 Microsoft Corporation
Module Name:
thrdpool.c
Abstract:
This module implements the thread pool package.
Author:
Keith Moore (keithmo) 10-Jun-1998
Revision History:
--*/
#include <precomp.h>
#include "thrdpoolp.h"
//
// Private globals.
//
DECLSPEC_ALIGN(UL_CACHE_LINE)
UL_ALIGNED_THREAD_POOL
g_UlThreadPool[(MAXIMUM_PROCESSORS * 2) + MaxThreadPools];
PUL_WORK_ITEM g_pKillerWorkItems = NULL;
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, UlInitializeThreadPool )
// #pragma alloc_text( PAGE, UlTerminateThreadPool )
#pragma alloc_text( PAGE, UlpCreatePoolThread )
#pragma alloc_text( PAGE, UlpThreadPoolWorker )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlpInitThreadTracker
NOT PAGEABLE -- UlpDestroyThreadTracker
NOT PAGEABLE -- UlpPopThreadTracker
#endif
//
// Public functions.
//
/***************************************************************************++
Routine Description:
Initialize the thread pool.
Arguments:
ThreadsPerCpu - Supplies the number of threads to create per CPU.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlInitializeThreadPool(
IN USHORT ThreadsPerCpu
)
{
NTSTATUS Status = STATUS_SUCCESS;
PUL_THREAD_POOL pThreadPool;
CLONG i;
USHORT j;
//
// Sanity check.
//
PAGED_CODE();
RtlZeroMemory( g_UlThreadPool, sizeof(g_UlThreadPool) );
//
// Preallocate the small array of special work items used by
// UlTerminateThreadPool, so that we can safely shut down even
// in low-memory conditions. This must be the first allocation
// or shutdown will not work.
//
g_pKillerWorkItems = UL_ALLOCATE_ARRAY(
NonPagedPool,
UL_WORK_ITEM,
((g_UlNumberOfProcessors * 2) + MaxThreadPools)
* ThreadsPerCpu,
UL_WORK_ITEM_POOL_TAG
);
if (g_pKillerWorkItems == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < (g_UlNumberOfProcessors * 2) + MaxThreadPools; i++)
{
pThreadPool = &g_UlThreadPool[i].ThreadPool;
UlTrace(WORK_ITEM,
("Initializing threadpool[%d] @ %p\n", i, pThreadPool));
//
// Initialize the kernel structures.
//
InitializeSListHead( &pThreadPool->WorkQueueSList );
KeInitializeEvent(
&pThreadPool->WorkQueueEvent,
SynchronizationEvent,
FALSE
);
UlInitializeSpinLock( &pThreadPool->ThreadSpinLock, "ThreadSpinLock" );
//
// Initialize the other fields.
//
pThreadPool->pIrpThread = NULL;
pThreadPool->ThreadCount = 0;
pThreadPool->Initialized = FALSE;
pThreadPool->ThreadCpu = (UCHAR) i;
pThreadPool->LookOnOtherQueues = FALSE;
InitializeListHead( &pThreadPool->ThreadListHead );
}
for (i = 0; i < (g_UlNumberOfProcessors * 2) + MaxThreadPools; i++)
{
ULONG NumThreads = (i < (g_UlNumberOfProcessors * 2)) ?
ThreadsPerCpu : 1;
pThreadPool = &g_UlThreadPool[i].ThreadPool;
//
// Create the threads.
//
for (j = 0; j < NumThreads; j++)
{
UlTrace(WORK_ITEM,
("Creating thread[%d,%d] @ %p\n", i, j, pThreadPool));
Status = UlpCreatePoolThread( pThreadPool );
if (NT_SUCCESS(Status))
{
pThreadPool->Initialized = TRUE;
pThreadPool->ThreadCount++;
}
else
{
break;
}
}
if (!NT_SUCCESS(Status))
{
break;
}
}
return Status;
} // UlInitializeThreadPool
/***************************************************************************++
Routine Description:
Terminates the thread pool, waiting for all worker threads to exit.
--***************************************************************************/
VOID
UlTerminateThreadPool(
VOID
)
{
PUL_THREAD_POOL pThreadPool;
PUL_THREAD_TRACKER pThreadTracker;
CLONG i, j;
PUL_WORK_ITEM pKiller = g_pKillerWorkItems;
//
// Sanity check.
//
PAGED_CODE();
//
// If there is no killer, the thread pool has never been initialized.
//
if (pKiller == NULL)
{
return;
}
for (i = 0; i < (g_UlNumberOfProcessors * 2) + MaxThreadPools; i++)
{
pThreadPool = &g_UlThreadPool[i].ThreadPool;
if (pThreadPool->Initialized)
{
//
// Queue a killer work item for each thread. Each
// killer tells one thread to kill itself.
//
for (j = 0; j < pThreadPool->ThreadCount; j++)
{
//
// Need a separate work item for each thread.
// Worker threads will free the below memory
// before termination. UlpKillThreadWorker is
// a sign to a worker thread for self termination.
//
UlInitializeWorkItem(pKiller);
pKiller->pWorkRoutine = &UlpKillThreadWorker;
QUEUE_UL_WORK_ITEM( pThreadPool, pKiller );
pKiller++;
}
//
// Wait for all threads to go away.
//
while (NULL != (pThreadTracker = UlpPopThreadTracker(pThreadPool)))
{
UlpDestroyThreadTracker( pThreadTracker );
}
}
ASSERT( IsListEmpty( &pThreadPool->ThreadListHead ) );
}
UL_FREE_POOL(g_pKillerWorkItems, UL_WORK_ITEM_POOL_TAG);
g_pKillerWorkItems = NULL;
} // UlTerminateThreadPool
//
// Private functions.
//
/***************************************************************************++
Routine Description:
Creates a new pool thread, setting pIrpThread if necessary.
Arguments:
pThreadPool - Supplies the pool that is to receive the new thread.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpCreatePoolThread(
IN PUL_THREAD_POOL pThreadPool
)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
PUL_THREAD_TRACKER pThreadTracker;
PETHREAD pThread;
//
// Sanity check.
//
PAGED_CODE();
//
// Ensure we can allocate a thread tracker.
//
pThreadTracker = (PUL_THREAD_TRACKER) UL_ALLOCATE_POOL(
NonPagedPool,
sizeof(*pThreadTracker),
UL_THREAD_TRACKER_POOL_TAG
);
if (pThreadTracker != NULL)
{
RtlZeroMemory(pThreadTracker, sizeof(*pThreadTracker));
pThreadTracker->pThreadPool = pThreadPool;
pThreadTracker->State = ThreadPoolCreated;
//
// Create the thread.
//
InitializeObjectAttributes(
&ObjectAttributes, // ObjectAttributes
NULL, // ObjectName
OBJ_KERNEL_HANDLE, // Attributes
NULL, // RootDirectory
NULL // SecurityDescriptor
);
Status = PsCreateSystemThread(
&pThreadTracker->ThreadHandle, // ThreadHandle
THREAD_ALL_ACCESS, // DesiredAccess
&ObjectAttributes, // ObjectAttributes
NULL, // ProcessHandle
NULL, // ClientId
UlpThreadPoolWorker, // StartRoutine
pThreadTracker // StartContext
);
if (NT_SUCCESS(Status))
{
//
// Get a pointer to the thread.
//
Status = ObReferenceObjectByHandle(
pThreadTracker->ThreadHandle,// ThreadHandle
FILE_READ_ACCESS, // DesiredAccess
*PsThreadType, // ObjectType
KernelMode, // AccessMode
(PVOID*) &pThread, // Object
NULL // HandleInformation
);
if (NT_SUCCESS(Status))
{
//
// Set up the thread tracker.
//
UlpInitThreadTracker(pThreadPool, pThread, pThreadTracker);
//
// If this is the first thread created for this pool,
// make it into the special IRP thread.
//
if (pThreadPool->pIrpThread == NULL)
{
pThreadPool->pIrpThread = pThread;
}
}
else
{
//
// That call really should not fail.
//
ASSERT(NT_SUCCESS(Status));
//
// Preserve return val from ObReferenceObjectByHandle.
//
ZwClose( pThreadTracker->ThreadHandle );
UL_FREE_POOL(
pThreadTracker,
UL_THREAD_TRACKER_POOL_TAG
);
}
}
else
{
//
// Couldn't create the thread, kill the tracker.
//
UL_FREE_POOL(
pThreadTracker,
UL_THREAD_TRACKER_POOL_TAG
);
}
}
else
{
//
// Couldn't create a thread tracker.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
} // UlpCreatePoolThread
/***************************************************************************++
Routine Description:
This is the main worker function for pool threads.
Arguments:
pContext - Supplies a context value for the thread. This is actually
a UL_THREAD_TRACKER pointer.
--***************************************************************************/
VOID
UlpThreadPoolWorker(
IN PVOID pContext
)
{
//
// Note: we have very few local variables. Instead, most variables
// are member variables of pThreadPool. This allows us to inspect the
// entire state of all the thread pool workers easily in !ulkd.thrdpool.
//
PUL_THREAD_TRACKER pThreadTracker = (PUL_THREAD_TRACKER) pContext;
PUL_THREAD_POOL pThreadPool = pThreadTracker->pThreadPool;
NTSTATUS Status = STATUS_SUCCESS;
PSLIST_ENTRY pListEntry = NULL;
//
// Sanity check.
//
PAGED_CODE();
pThreadTracker->State = ThreadPoolInit;
//
// Is this a regular work queue?
//
if ( pThreadPool->ThreadCpu < (g_UlNumberOfProcessors * 2) )
{
KAFFINITY AffinityMask;
ULONG ThreadCpu;
ThreadCpu = pThreadPool->ThreadCpu % g_UlNumberOfProcessors;
//
// Only regular worker threads can pull workitems from
// other regular queues on other processors.
//
if (pThreadPool->ThreadCpu < g_UlNumberOfProcessors &&
g_UlNumberOfProcessors > 1)
{
pThreadPool->LookOnOtherQueues = TRUE;
}
//
// Set this thread's hard affinity if enabled plus ideal processor.
//
if ( g_UlEnableThreadAffinity )
{
AffinityMask = 1U << ThreadCpu;
}
else
{
AffinityMask = (KAFFINITY) g_UlThreadAffinityMask;
}
Status = ZwSetInformationThread(
pThreadTracker->ThreadHandle,
ThreadAffinityMask,
&AffinityMask,
sizeof(AffinityMask)
);
ASSERT( NT_SUCCESS( Status ) );
//
// Always set thread's ideal processor.
//
Status = ZwSetInformationThread(
pThreadTracker->ThreadHandle,
ThreadIdealProcessor,
&ThreadCpu,
sizeof(ThreadCpu)
);
ASSERT( NT_SUCCESS( Status ) );
}
else if ( pThreadPool == WAIT_THREAD_POOL())
{
// no special initialization needed
}
else if ( pThreadPool == HIGH_PRIORITY_THREAD_POOL())
{
//
// Boost the base priority of the High Priority thread(s)
//
PKTHREAD CurrentThread = KeGetCurrentThread();
LONG OldIncrement
= KeSetBasePriorityThread(CurrentThread, IO_NETWORK_INCREMENT+1);
UlTrace(WORK_ITEM,
("Set base priority of hi-pri thread, %p. Was %d\n",
CurrentThread, OldIncrement
));
UNREFERENCED_PARAMETER( OldIncrement );
}
else
{
ASSERT(! "Unknown worker thread");
}
//
// Disable hard error popups from IoRaiseHardError(), which can cause
// deadlocks. Highest-level drivers, particularly file system drivers,
// call IoRaiseHardError().
//
IoSetThreadHardErrorMode( FALSE );
//
// Loop forever, or at least until we're told to stop.
//
while ( TRUE )
{
//
// Flush the accumulated work items. The inner loop will handle
// them all.
//
pThreadTracker->State = ThreadPoolFlush;
pListEntry = InterlockedFlushSList( &pThreadPool->WorkQueueSList );
//
// If the list is empty, see if we can do work for some other
// processor. Only the regular worker threads should look for
// work on the other processors' work queues. We don't want the
// blocking (wait) queue executing workitems that could block and
// we don't want the high-priority queue executing regular work
// items.
//
if ( NULL == pListEntry && pThreadPool->LookOnOtherQueues )
{
ULONG Cpu;
ULONG NextCpu;
ASSERT( pThreadPool->ThreadCpu < g_UlNumberOfProcessors );
//
// Try to get a workitem from the other regular queues.
//
pThreadTracker->State = ThreadPoolSearchOther;
NextCpu = pThreadPool->ThreadCpu + 1;
for (Cpu = 0; Cpu < g_UlNumberOfProcessors; Cpu++, NextCpu++)
{
PUL_THREAD_POOL pThreadPoolNext;
if (NextCpu >= g_UlNumberOfProcessors)
{
NextCpu = 0;
}
pThreadPoolNext = &g_UlThreadPool[NextCpu].ThreadPool;
ASSERT( WAIT_THREAD_POOL() != pThreadPoolNext );
ASSERT( HIGH_PRIORITY_THREAD_POOL() != pThreadPoolNext );
ASSERT( pThreadPoolNext->ThreadCpu < g_UlNumberOfProcessors );
//
// Only take an item if the other processor's work
// queue has at least g_UlMinWorkDequeueDepth items.
//
if ( ExQueryDepthSList( &pThreadPoolNext->WorkQueueSList ) >=
g_UlMinWorkDequeueDepth )
{
pListEntry = InterlockedPopEntrySList(
&pThreadPoolNext->WorkQueueSList
);
if ( NULL != pListEntry )
{
//
// Make sure we didn't pop up a killer. If so,
// push it back to where it is popped from.
//
PUL_WORK_ITEM pWorkItem
= CONTAINING_RECORD(
pListEntry,
UL_WORK_ITEM,
QueueListEntry
);
if (pWorkItem->pWorkRoutine != &UlpKillThreadWorker)
{
//
// Clear next pointer because we only
// took one item, not the whole queue
//
pListEntry->Next = NULL;
}
else
{
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_PUSH_BACK_WORK_ITEM,
0,
pWorkItem,
__FILE__,
__LINE__
);
QUEUE_UL_WORK_ITEM(pThreadPoolNext, pWorkItem);
pListEntry = NULL;
}
break;
}
}
}
}
//
// No work to be done? Then block until the thread is signaled
//
if ( NULL == pListEntry )
{
pThreadTracker->State = ThreadPoolBlock;
KeWaitForSingleObject(
&pThreadPool->WorkQueueEvent,
Executive,
KernelMode,
FALSE,
0
);
// back to the top of the outer loop
continue;
}
ASSERT( NULL != pListEntry );
//
// Initialize CurrentListHead to reverse the order of items
// from InterlockedFlushSList. The SList is a stack. Reversing
// a stack gives us a queue; i.e., we'll execute the work
// items in the order they were received.
//
pThreadTracker->State = ThreadPoolReverseList;
pThreadTracker->CurrentListHead.Next = NULL;
pThreadTracker->ListLength = 0;
//
// Rebuild the list with reverse order of what we received.
//
while ( pListEntry != NULL )
{
PSLIST_ENTRY pNext;
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_FLUSH_WORK_ITEM,
0,
pListEntry,
__FILE__,
__LINE__
);
pNext = pListEntry->Next;
pListEntry->Next = pThreadTracker->CurrentListHead.Next;
pThreadTracker->CurrentListHead.Next = pListEntry;
++pThreadTracker->ListLength;
pListEntry = pNext;
}
//
// Update per-pool statistics
//
pThreadTracker->QueueFlushes += 1;
pThreadTracker->SumQueueLengths += pThreadTracker->ListLength;
pThreadTracker->MaxQueueLength = max(pThreadTracker->ListLength,
pThreadTracker->MaxQueueLength);
//
// We can now process the work items in the order that they
// were received
//
pThreadTracker->State = ThreadPoolExecute;
while (NULL != ( pListEntry = pThreadTracker->CurrentListHead.Next ))
{
--pThreadTracker->ListLength;
pThreadTracker->CurrentListHead.Next = pListEntry->Next;
pThreadTracker->pWorkItem
= CONTAINING_RECORD(
pListEntry,
UL_WORK_ITEM,
QueueListEntry
);
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_PROCESS_WORK_ITEM,
0,
pThreadTracker->pWorkItem,
__FILE__,
__LINE__
);
//
// Call the actual work item routine.
//
ASSERT( pThreadTracker->pWorkItem->pWorkRoutine != NULL );
if ( pThreadTracker->pWorkItem->pWorkRoutine
== &UlpKillThreadWorker )
{
//
// Received a special signal for self-termination.
// Push all remaining work items back to the queue
// before we exit the current thread. This is necessary
// when we have more than one worker thread per thread pool.
// Each thread needs to be given a chance to pick up one
// killer work item, but each thread greedily picks up
// all of the unhandled work items. Pushing back any
// remaining work items ensures that all threads in this
// thread pool will get killed.
//
while (NULL != ( pListEntry
= pThreadTracker->CurrentListHead.Next ))
{
pThreadTracker->CurrentListHead.Next = pListEntry->Next;
pThreadTracker->pWorkItem
= CONTAINING_RECORD(
pListEntry,
UL_WORK_ITEM,
QueueListEntry
);
ASSERT( pThreadTracker->pWorkItem->pWorkRoutine
== &UlpKillThreadWorker );
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_PUSH_BACK_WORK_ITEM,
0,
pThreadTracker->pWorkItem,
__FILE__,
__LINE__
);
QUEUE_UL_WORK_ITEM(
pThreadPool,
pThreadTracker->pWorkItem
);
}
goto exit;
}
//
// Regular workitem. Use the UL_ENTER_DRIVER debug
// stuff to keep track of ERESOURCES acquired, etc,
// while executing the workitem.
//
UL_ENTER_DRIVER( "UlpThreadPoolWorker", NULL );
//
// Clear the workitem as an indication that this item has
// started processing. Must do this before calling the
// routine, as the routine may destroy the work item or queue
// it again. Also, this means that callers need only
// explicitly initialize a workitem struct once, when the
// enclosing object is first allocated.
//
pThreadTracker->pWorkRoutine
= pThreadTracker->pWorkItem->pWorkRoutine;
UlInitializeWorkItem(pThreadTracker->pWorkItem);
(*pThreadTracker->pWorkRoutine)(
pThreadTracker->pWorkItem
);
UL_LEAVE_DRIVER( "UlpThreadPoolWorker" );
//
// Sanity check
//
PAGED_CODE();
++pThreadTracker->Executions;
pThreadTracker->pWorkRoutine = NULL;
pThreadTracker->pWorkItem = NULL;
}
} // while (TRUE)
exit:
pThreadTracker->State = ThreadPoolTerminated;
//
// Suicide is painless.
//
PsTerminateSystemThread( STATUS_SUCCESS );
} // UlpThreadPoolWorker
/***************************************************************************++
Routine Description:
Initializes a new thread tracker and inserts it into the thread pool.
Arguments:
pThreadPool - Supplies the thread pool to own the new tracker.
pThread - Supplies the thread for the tracker.
pThreadTracker - Supplise the tracker to be initialized
--***************************************************************************/
VOID
UlpInitThreadTracker(
IN PUL_THREAD_POOL pThreadPool,
IN PETHREAD pThread,
IN PUL_THREAD_TRACKER pThreadTracker
)
{
KIRQL oldIrql;
ASSERT( pThreadPool != NULL );
ASSERT( pThread != NULL );
ASSERT( pThreadTracker != NULL );
pThreadTracker->pThread = pThread;
UlAcquireSpinLock( &pThreadPool->ThreadSpinLock, &oldIrql );
InsertTailList(
&pThreadPool->ThreadListHead,
&pThreadTracker->ThreadListEntry
);
UlReleaseSpinLock( &pThreadPool->ThreadSpinLock, oldIrql );
} // UlpInitThreadTracker
/***************************************************************************++
Routine Description:
Removes the specified thread tracker from the thread pool.
Arguments:
pThreadPool - Supplies the thread pool that owns the tracker.
pThreadTracker - Supplies the thread tracker to remove.
Return Value:
None
--***************************************************************************/
VOID
UlpDestroyThreadTracker(
IN PUL_THREAD_TRACKER pThreadTracker
)
{
//
// Sanity check.
//
ASSERT( pThreadTracker != NULL );
//
// Wait for the thread to die.
//
KeWaitForSingleObject(
(PVOID)pThreadTracker->pThread, // Object
UserRequest, // WaitReason
KernelMode, // WaitMode
FALSE, // Alertable
NULL // Timeout
);
//
// Cleanup.
//
ObDereferenceObject( pThreadTracker->pThread );
//
// Release the thread handle.
//
ZwClose( pThreadTracker->ThreadHandle );
//
// Do it.
//
UL_FREE_POOL(
pThreadTracker,
UL_THREAD_TRACKER_POOL_TAG
);
} // UlpDestroyThreadTracker
/***************************************************************************++
Routine Description:
Removes a thread tracker from the thread pool.
Arguments:
pThreadPool - Supplies the thread pool that owns the tracker.
Return Value:
A pointer to the tracker or NULL (if list is empty)
--***************************************************************************/
PUL_THREAD_TRACKER
UlpPopThreadTracker(
IN PUL_THREAD_POOL pThreadPool
)
{
PLIST_ENTRY pEntry;
PUL_THREAD_TRACKER pThreadTracker;
KIRQL oldIrql;
ASSERT( pThreadPool != NULL );
ASSERT( pThreadPool->Initialized );
UlAcquireSpinLock( &pThreadPool->ThreadSpinLock, &oldIrql );
if (IsListEmpty(&pThreadPool->ThreadListHead))
{
pThreadTracker = NULL;
}
else
{
pEntry = RemoveHeadList(&pThreadPool->ThreadListHead);
pThreadTracker = CONTAINING_RECORD(
pEntry,
UL_THREAD_TRACKER,
ThreadListEntry
);
}
UlReleaseSpinLock( &pThreadPool->ThreadSpinLock, oldIrql );
return pThreadTracker;
} // UlpPopThreadTracker
/***************************************************************************++
Routine Description:
A dummy function to indicate that the thread should be terminated.
Arguments:
pWorkItem - Supplies the dummy work item.
Return Value:
None
--***************************************************************************/
VOID
UlpKillThreadWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
UNREFERENCED_PARAMETER( pWorkItem );
return;
} // UlpKillThreadWorker
/***************************************************************************++
Routine Description:
A function that queues a worker item to a thread pool.
Arguments:
pWorkItem - Supplies the work item.
pWorkRoutine - Supplies the work routine.
Return Value:
None
--***************************************************************************/
VOID
UlQueueWorkItem(
IN PUL_WORK_ITEM pWorkItem,
IN PUL_WORK_ROUTINE pWorkRoutine,
IN PCSTR pFileName,
IN USHORT LineNumber
)
{
PUL_THREAD_POOL pThreadPool;
CLONG Cpu, NextCpu;
//
// Sanity check.
//
ASSERT( pWorkItem != NULL );
ASSERT( pWorkRoutine != NULL );
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_QUEUE_WORK_ITEM,
0,
pWorkItem,
pFileName,
LineNumber
);
UlpValidateWorkItem(pWorkItem, pFileName, LineNumber);
//
// Save the pointer to the worker routine, then queue the item.
//
pWorkItem->pWorkRoutine = pWorkRoutine;
//
// Queue the work item on the idle processor if possible.
//
NextCpu = KeGetCurrentProcessorNumber();
for (Cpu = 0; Cpu < g_UlNumberOfProcessors; Cpu++, NextCpu++)
{
if (NextCpu >= g_UlNumberOfProcessors)
{
NextCpu = 0;
}
pThreadPool = &g_UlThreadPool[NextCpu].ThreadPool;
ASSERT(WAIT_THREAD_POOL() != pThreadPool);
ASSERT(HIGH_PRIORITY_THREAD_POOL() != pThreadPool);
if (ExQueryDepthSList(&pThreadPool->WorkQueueSList) <=
g_UlMaxWorkQueueDepth)
{
QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
return;
}
}
//
// Queue the work item on the current thread pool.
//
pThreadPool = CURRENT_THREAD_POOL();
QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
} // UlQueueWorkItem
/***************************************************************************++
Routine Description:
A function that queues a worker item that involves sync I/O to a special
thread pool.
Arguments:
pWorkItem - Supplies the work item.
pWorkRoutine - Supplies the work routine.
Return Value:
None
--***************************************************************************/
VOID
UlQueueSyncItem(
IN PUL_WORK_ITEM pWorkItem,
IN PUL_WORK_ROUTINE pWorkRoutine,
IN PCSTR pFileName,
IN USHORT LineNumber
)
{
PUL_THREAD_POOL pThreadPool;
//
// Sanity check.
//
ASSERT( pWorkItem != NULL );
ASSERT( pWorkRoutine != NULL );
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_QUEUE_SYNC_ITEM,
0,
pWorkItem,
pFileName,
LineNumber
);
UlpValidateWorkItem(pWorkItem, pFileName, LineNumber);
//
// Save the pointer to the worker routine, then queue the item.
//
pWorkItem->pWorkRoutine = pWorkRoutine;
//
// Queue the work item on the special wait thread pool.
//
pThreadPool = CURRENT_SYNC_THREAD_POOL();
QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
} // UlQueueSyncItem
/***************************************************************************++
Routine Description:
A function that queues a blocking worker item to a special thread pool.
Arguments:
pWorkItem - Supplies the work item.
pWorkRoutine - Supplies the work routine.
Return Value:
None
--***************************************************************************/
VOID
UlQueueWaitItem(
IN PUL_WORK_ITEM pWorkItem,
IN PUL_WORK_ROUTINE pWorkRoutine,
IN PCSTR pFileName,
IN USHORT LineNumber
)
{
PUL_THREAD_POOL pThreadPool;
//
// Sanity check.
//
ASSERT( pWorkItem != NULL );
ASSERT( pWorkRoutine != NULL );
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_QUEUE_WAIT_ITEM,
0,
pWorkItem,
pFileName,
LineNumber
);
UlpValidateWorkItem(pWorkItem, pFileName, LineNumber);
//
// Save the pointer to the worker routine, then queue the item.
//
pWorkItem->pWorkRoutine = pWorkRoutine;
//
// Queue the work item on the special wait thread pool.
//
pThreadPool = WAIT_THREAD_POOL();
QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
} // UlQueueWaitItem
/***************************************************************************++
Routine Description:
A function that queues a blocking worker item to the high-priority
thread pool.
Arguments:
pWorkItem - Supplies the work item.
pWorkRoutine - Supplies the work routine.
Return Value:
None
--***************************************************************************/
VOID
UlQueueHighPriorityItem(
IN PUL_WORK_ITEM pWorkItem,
IN PUL_WORK_ROUTINE pWorkRoutine,
IN PCSTR pFileName,
IN USHORT LineNumber
)
{
PUL_THREAD_POOL pThreadPool;
//
// Sanity check.
//
ASSERT( pWorkItem != NULL );
ASSERT( pWorkRoutine != NULL );
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_QUEUE_HIGH_PRIORITY_ITEM,
0,
pWorkItem,
pFileName,
LineNumber
);
UlpValidateWorkItem(pWorkItem, pFileName, LineNumber);
//
// Save the pointer to the worker routine, then queue the item.
//
pWorkItem->pWorkRoutine = pWorkRoutine;
//
// Queue the work item on the special wait thread pool.
//
pThreadPool = HIGH_PRIORITY_THREAD_POOL();
QUEUE_UL_WORK_ITEM( pThreadPool, pWorkItem );
} // UlQueueHighPriorityItem
/***************************************************************************++
Routine Description:
A function that either queues a worker item to a thread pool if the
caller is at DISPATCH_LEVEL/APC_LEVEL or it simply calls the work
routine directly.
Note: if the work item has to execute on a system thread, then
you must use UL_QUEUE_WORK_ITEM instead.
Arguments:
pWorkItem - Supplies the work item.
pWorkRoutine - Supplies the work routine.
Return Value:
None
--***************************************************************************/
VOID
UlCallPassive(
IN PUL_WORK_ITEM pWorkItem,
IN PUL_WORK_ROUTINE pWorkRoutine,
IN PCSTR pFileName,
IN USHORT LineNumber
)
{
//
// Sanity check.
//
ASSERT( pWorkItem != NULL );
ASSERT( pWorkRoutine != NULL );
WRITE_REF_TRACE_LOG(
g_pWorkItemTraceLog,
REF_ACTION_CALL_PASSIVE,
0,
pWorkItem,
pFileName,
LineNumber
);
UlpValidateWorkItem(pWorkItem, pFileName, LineNumber);
if (KeGetCurrentIrql() == PASSIVE_LEVEL)
{
//
// Clear this for consistency with UlpThreadPoolWorker.
//
UlInitializeWorkItem(pWorkItem);
(*pWorkRoutine)(pWorkItem);
}
else
{
UL_QUEUE_WORK_ITEM(pWorkItem, pWorkRoutine);
}
} // UlCallPassive
/***************************************************************************++
Routine Description:
Queries the "IRP thread", the special worker thread used as the
target for all asynchronous IRPs.
Arguments:
None
Return Value:
None
--***************************************************************************/
PETHREAD
UlQueryIrpThread(
VOID
)
{
PUL_THREAD_POOL pThreadPool;
//
// Sanity check.
//
pThreadPool = CURRENT_THREAD_POOL();
ASSERT( pThreadPool->Initialized );
//
// Return the IRP thread.
//
return pThreadPool->pIrpThread;
} // UlQueryIrpThread