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.
655 lines
17 KiB
655 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1992-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
worker.c
|
|
|
|
Abstract:
|
|
|
|
This module implements a worker thread and a set of functions for
|
|
passing work to it.
|
|
|
|
Author:
|
|
|
|
Larry Osterman (LarryO) 13-Jul-1992
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include "logonsrv.h" // Include files common to entire service
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Number of worker threads to create and the usage count array.
|
|
//
|
|
|
|
|
|
#define NL_MAX_WORKER_THREADS 5
|
|
ULONG NlNumberOfCreatedWorkerThreads = 0;
|
|
|
|
ULONG NlWorkerThreadStats[NL_MAX_WORKER_THREADS];
|
|
PHANDLE NlThreadArray[NL_MAX_WORKER_THREADS];
|
|
BOOLEAN NlThreadExitted[NL_MAX_WORKER_THREADS];
|
|
|
|
//
|
|
// CritSect guard the WorkQueue list.
|
|
//
|
|
|
|
BOOLEAN NlWorkerInitialized = FALSE;
|
|
CRITICAL_SECTION NlWorkerCritSect;
|
|
|
|
#define LOCK_WORK_QUEUE() EnterCriticalSection(&NlWorkerCritSect);
|
|
#define UNLOCK_WORK_QUEUE() LeaveCriticalSection(&NlWorkerCritSect);
|
|
|
|
//
|
|
// Head of singly linked list of work items queued to the worker thread.
|
|
//
|
|
|
|
LIST_ENTRY NlWorkerQueueHead = {0};
|
|
LIST_ENTRY NlWorkerHighQueueHead = {0};
|
|
|
|
VOID
|
|
NlWorkerThread(
|
|
IN PVOID StartContext
|
|
)
|
|
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
ULONG ThreadIndex = (ULONG)((ULONG_PTR)StartContext);
|
|
|
|
ULONG Index;
|
|
PWORKER_ITEM WorkItem;
|
|
|
|
HANDLE EventHandle = NULL;
|
|
|
|
|
|
//
|
|
// Every thread should loop until the queue is empty.
|
|
//
|
|
// This loop completes even though Netlogon has been asked to terminate.
|
|
// The individual worker routines are designed to terminate quickly when netlogon
|
|
// is terminating. This philosophy allows the worker routines to do there own
|
|
// cleanup.
|
|
//
|
|
|
|
while( TRUE ) {
|
|
|
|
//
|
|
// Pull an entry off the queue
|
|
//
|
|
// Prefer a workitem from the high priority queue
|
|
//
|
|
|
|
LOCK_WORK_QUEUE();
|
|
|
|
if (!IsListEmpty(&NlWorkerHighQueueHead)) {
|
|
WorkItem = (PWORKER_ITEM)RemoveHeadList( &NlWorkerHighQueueHead );
|
|
} else if (!IsListEmpty(&NlWorkerQueueHead)) {
|
|
WorkItem = (PWORKER_ITEM)RemoveHeadList( &NlWorkerQueueHead );
|
|
} else {
|
|
UNLOCK_WORK_QUEUE();
|
|
break;
|
|
}
|
|
|
|
NlAssert(WorkItem->Inserted);
|
|
WorkItem->Inserted = FALSE;
|
|
|
|
NlWorkerThreadStats[NlNumberOfCreatedWorkerThreads-1] += 1;
|
|
|
|
UNLOCK_WORK_QUEUE();
|
|
|
|
NlPrint(( NL_WORKER, "%lx: Pulling off work item %lx (%lx)\n", ThreadIndex, WorkItem, WorkItem->WorkerRoutine));
|
|
|
|
|
|
//
|
|
// Execute the specified routine.
|
|
//
|
|
|
|
(WorkItem->WorkerRoutine)( WorkItem->Parameter );
|
|
|
|
|
|
|
|
//
|
|
// A thread can ditch dangling thread handles for the other threads.
|
|
//
|
|
// This will ensure there is at most one dangling thread handle.
|
|
//
|
|
|
|
LOCK_WORK_QUEUE();
|
|
for (Index = 0; Index < NL_MAX_WORKER_THREADS; Index++ ) {
|
|
if ( ThreadIndex != Index && NlThreadArray[Index] != NULL && NlThreadExitted[Index] ) {
|
|
DWORD WaitStatus;
|
|
NlPrint(( NL_WORKER, "%lx: %lx: Ditching worker thread\n", Index, NlThreadArray[Index]));
|
|
|
|
// Always wait for the thread to exit before closing the handle to
|
|
// ensure the thread has left netlogon.dll before we unload the dll.
|
|
WaitStatus = WaitForSingleObject( NlThreadArray[Index], 0xffffffff );
|
|
if ( WaitStatus != 0 ) {
|
|
NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be awaited. %ld 0x%lX\n", Index, WaitStatus, NlThreadArray[Index] ));
|
|
}
|
|
if (!CloseHandle( NlThreadArray[Index] ) ) {
|
|
NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be closed. %ld 0x%lX\n", Index, GetLastError(), NlThreadArray[Index] ));
|
|
}
|
|
|
|
NlThreadArray[Index] = NULL;
|
|
NlThreadExitted[Index] = FALSE;
|
|
}
|
|
}
|
|
UNLOCK_WORK_QUEUE();
|
|
|
|
}
|
|
|
|
NlPrint(( NL_WORKER, "%lx: worker thread exitting\n", ThreadIndex ));
|
|
|
|
LOCK_WORK_QUEUE();
|
|
NlThreadExitted[ThreadIndex] = TRUE;
|
|
NlNumberOfCreatedWorkerThreads--;
|
|
UNLOCK_WORK_QUEUE();
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlWorkerInitialization(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG ThreadId;
|
|
|
|
NET_API_STATUS NetStatus;
|
|
|
|
//
|
|
// Perform initialization that allows us to call NlWorkerTermination
|
|
//
|
|
|
|
try {
|
|
InitializeCriticalSection( &NlWorkerCritSect );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
InitializeListHead( &NlWorkerQueueHead );
|
|
InitializeListHead( &NlWorkerHighQueueHead );
|
|
NlNumberOfCreatedWorkerThreads = 0;
|
|
|
|
|
|
RtlZeroMemory( NlThreadArray, sizeof(NlThreadArray) );
|
|
RtlZeroMemory( NlThreadExitted, sizeof(NlThreadExitted) );
|
|
RtlZeroMemory( NlWorkerThreadStats, sizeof(NlWorkerThreadStats) );
|
|
|
|
NlWorkerInitialized = TRUE;
|
|
|
|
|
|
return NERR_Success;
|
|
}
|
|
|
|
VOID
|
|
NlWorkerKillThreads(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate all worker threads.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG Index;
|
|
|
|
//
|
|
// Wait for all the threads to exit.
|
|
//
|
|
|
|
for ( Index = 0; Index < NL_MAX_WORKER_THREADS; Index ++ ) {
|
|
if ( NlThreadArray[Index] != NULL ) {
|
|
DWORD WaitStatus;
|
|
NlPrint(( NL_WORKER, "%lx: %lx: Ditching worker thread\n", Index, NlThreadArray[Index]));
|
|
|
|
// Always wait for the thread to exit before closing the handle to
|
|
// ensure the thread has left netlogon.dll before we unload the dll.
|
|
WaitStatus = WaitForSingleObject( NlThreadArray[Index], 0xffffffff );
|
|
if ( WaitStatus != 0 ) {
|
|
NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be awaited. %ld 0x%lX\n", Index, WaitStatus, NlThreadArray[Index] ));
|
|
}
|
|
|
|
if (!CloseHandle( NlThreadArray[Index] ) ) {
|
|
NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be closed. %ld 0x%lX\n", Index, GetLastError(), NlThreadArray[Index] ));
|
|
}
|
|
NlThreadArray[Index] = NULL;
|
|
NlThreadExitted[Index] = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
NlWorkerTermination(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Undo initialization of the worker threads.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status value -
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Only cleanup if we've successfully initialized.
|
|
//
|
|
|
|
if ( NlWorkerInitialized ) {
|
|
|
|
//
|
|
//
|
|
// Ensure the threads have been terminated.
|
|
//
|
|
|
|
NlWorkerKillThreads();
|
|
|
|
DeleteCriticalSection( &NlWorkerCritSect );
|
|
NlWorkerInitialized = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
NlQueueWorkItem(
|
|
IN PWORKER_ITEM WorkItem,
|
|
IN BOOL InsertNewItem,
|
|
IN BOOL HighPriority
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function modifies the work item queue by either inserting
|
|
a new specified work item to a specified queue or increasing the
|
|
priority of the already inserted item to the highest priority
|
|
in the specified queue.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Supplies a pointer to the work item to add the the queue.
|
|
It is the caller's responsibility to reclaim the storage occupied by
|
|
the WorkItem structure.
|
|
|
|
InsertNewItem - If TRUE, we are to insert the new item into the queue
|
|
specified by the value of the HighPriority parameter; the new item
|
|
will be inserted at the end of the queue. Otherwise, we are to
|
|
modify (boost) the priority of already inserted work item by moving
|
|
it to the front of the queue specified by the HighPriority value.
|
|
If TRUE and the item is already inserted (as determined by this
|
|
routine), this routine is no-op except for some possible cleanup.
|
|
If FALSE and the item is not already inserted (as determined by
|
|
this routine), this routine is no-op except for some possible cleanup.
|
|
|
|
HighPriority - The queue entry should be processed at a higher priority than
|
|
normal.
|
|
|
|
Return Value:
|
|
|
|
TRUE if item got queued or modified
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
|
|
//
|
|
// Ignore this attempt if the worker threads aren't initialized.
|
|
//
|
|
|
|
if ( !NlWorkerInitialized ) {
|
|
NlPrint(( NL_CRITICAL, "NlQueueWorkItem when worker not initialized\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Ditch any dangling thread handles
|
|
//
|
|
|
|
LOCK_WORK_QUEUE();
|
|
for (Index = 0; Index < NL_MAX_WORKER_THREADS; Index++ ) {
|
|
if ( NlThreadArray[Index] != NULL && NlThreadExitted[Index] ) {
|
|
DWORD WaitStatus;
|
|
NlPrint(( NL_WORKER, "%lx: %lx: Ditching worker thread\n", Index, NlThreadArray[Index]));
|
|
|
|
// Always wait for the thread to exit before closing the handle to
|
|
// ensure the thread has left netlogon.dll before we unload the dll.
|
|
WaitStatus = WaitForSingleObject( NlThreadArray[Index], 0xffffffff );
|
|
if ( WaitStatus != 0 ) {
|
|
NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be awaited. %ld 0x%lX\n", Index, WaitStatus, NlThreadArray[Index] ));
|
|
}
|
|
|
|
if (!CloseHandle( NlThreadArray[Index] ) ) {
|
|
NlPrint(( NL_CRITICAL, "%lx: worker thread handle cannot be closed. %ld 0x%lX\n", Index, GetLastError(), NlThreadArray[Index] ));
|
|
}
|
|
|
|
NlThreadArray[Index] = NULL;
|
|
NlThreadExitted[Index] = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we are to insert a new work item,
|
|
// do so
|
|
//
|
|
|
|
if ( InsertNewItem ) {
|
|
|
|
//
|
|
// If the work item is already inserted,
|
|
// we are done expect for some possible
|
|
// cleanup
|
|
//
|
|
if ( WorkItem->Inserted ) {
|
|
|
|
//
|
|
// If there is no worker thread processing this
|
|
// work item (i.e. we failed to create a thread
|
|
// at the time this work item was inserted),
|
|
// fall through and retry to create a thread
|
|
// below. Otherwise, we are done.
|
|
//
|
|
if ( NlNumberOfCreatedWorkerThreads > 0 ) {
|
|
UNLOCK_WORK_QUEUE();
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Otherwise, insert this work item
|
|
//
|
|
} else {
|
|
|
|
NlPrint(( NL_WORKER, "Inserting work item %lx (%lx)\n",WorkItem, WorkItem->WorkerRoutine));
|
|
|
|
if ( HighPriority ) {
|
|
InsertTailList( &NlWorkerHighQueueHead, &WorkItem->List );
|
|
} else {
|
|
InsertTailList( &NlWorkerQueueHead, &WorkItem->List );
|
|
}
|
|
WorkItem->Inserted = TRUE;
|
|
}
|
|
|
|
//
|
|
// Otherwise, we are to boost the priority
|
|
// of an already inserted work item
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the work item isn't already inserted,
|
|
// we are done
|
|
//
|
|
if ( !WorkItem->Inserted ) {
|
|
UNLOCK_WORK_QUEUE();
|
|
return TRUE;
|
|
|
|
//
|
|
// Otherwise, boost the priority
|
|
//
|
|
} else {
|
|
NlPrint(( NL_WORKER,
|
|
"Boosting %s priority work item %lx (%lx)\n",
|
|
(HighPriority ? "high" : "low"),
|
|
WorkItem,
|
|
WorkItem->WorkerRoutine ));
|
|
|
|
RemoveEntryList( &WorkItem->List );
|
|
if ( HighPriority ) {
|
|
InsertHeadList( &NlWorkerHighQueueHead, &WorkItem->List );
|
|
} else {
|
|
InsertHeadList( &NlWorkerQueueHead, &WorkItem->List );
|
|
}
|
|
|
|
//
|
|
// If there is no worker thread processing this
|
|
// work item (i.e. we failed to create a thread
|
|
// at the time this work item was inserted),
|
|
// fall through and retry to create a thread
|
|
// below. Otherwise, we are done.
|
|
//
|
|
if ( NlNumberOfCreatedWorkerThreads > 0 ) {
|
|
UNLOCK_WORK_QUEUE();
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If there isn't a worker thread to handle the request,
|
|
// create one now.
|
|
//
|
|
|
|
if ( NlNumberOfCreatedWorkerThreads < NL_MAX_WORKER_THREADS ) {
|
|
|
|
//
|
|
// Find a spot for the thread handle.
|
|
//
|
|
|
|
for (Index = 0; Index < NL_MAX_WORKER_THREADS; Index++ ) {
|
|
if ( NlThreadArray[Index] == NULL ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if ( Index >= NL_MAX_WORKER_THREADS ) {
|
|
NlPrint(( NL_CRITICAL, "NlQueueWorkItem: Internal Error\n" ));
|
|
UNLOCK_WORK_QUEUE();
|
|
return FALSE;
|
|
} else {
|
|
DWORD ThreadId;
|
|
NlThreadArray[Index] = CreateThread(
|
|
NULL, // No security attributes
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)NlWorkerThread,
|
|
(PVOID) ULongToPtr( Index ),
|
|
0, // No special creation flags
|
|
&ThreadId );
|
|
|
|
NlPrint(( NL_WORKER, "%lx: %lx: %lx: Starting worker thread\n", Index, NlThreadArray[Index], ThreadId ));
|
|
|
|
//
|
|
// Note that if we fail to create a thread,
|
|
// the work item remains queued and possibly not processed.
|
|
// This is not critical because the item will be processed
|
|
// next time a work item gets queued which will happen at
|
|
// the next scavenging time at latest.
|
|
//
|
|
if (NlThreadArray[Index] == NULL) {
|
|
NlPrint((NL_CRITICAL,
|
|
"NlQueueWorkItem: Cannot create thread %ld\n", GetLastError() ));
|
|
} else {
|
|
NlNumberOfCreatedWorkerThreads++;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
UNLOCK_WORK_QUEUE();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef notdef // Don't need timers yet
|
|
NET_API_STATUS
|
|
NlCreateTimer(
|
|
IN PNlOWSER_TIMER Timer
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
NTSTATUS Status;
|
|
|
|
InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
|
|
|
|
Status = NtCreateTimer(&Timer->TimerHandle,
|
|
TIMER_ALL_ACCESS,
|
|
&ObjA,
|
|
NotificationTimer);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NlPrint(( NL_CRITICAL, "Failed to create timer %lx: %X\n", Timer, Status));
|
|
return(NlMapStatus(Status));
|
|
}
|
|
|
|
NlPrint(( Nl_TIMER, "Creating timer %lx: Handle: %lx\n", Timer, Timer->TimerHandle));
|
|
|
|
return(NERR_Success);
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlDestroyTimer(
|
|
IN PNlOWSER_TIMER Timer
|
|
)
|
|
{
|
|
HANDLE Handle;
|
|
|
|
//
|
|
// Avoid destroying a timer twice.
|
|
//
|
|
|
|
if ( Timer->TimerHandle == NULL ) {
|
|
return NERR_Success;
|
|
}
|
|
|
|
// Closing doesn't automatically cancel the timer.
|
|
(VOID) NlCancelTimer( Timer );
|
|
|
|
//
|
|
// Close the handle and prevent future uses.
|
|
//
|
|
|
|
Handle = Timer->TimerHandle;
|
|
Timer->TimerHandle = NULL;
|
|
|
|
NlPrint(( Nl_TIMER, "Destroying timer %lx\n", Timer));
|
|
return NlMapStatus(NtClose(Handle));
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlCancelTimer(
|
|
IN PNlOWSER_TIMER Timer
|
|
)
|
|
{
|
|
//
|
|
// Avoid cancelling a destroyed timer.
|
|
//
|
|
|
|
if ( Timer->TimerHandle == NULL ) {
|
|
NlPrint(( Nl_TIMER, "Canceling destroyed timer %lx\n", Timer));
|
|
return NERR_Success;
|
|
}
|
|
|
|
NlPrint(( Nl_TIMER, "Canceling timer %lx\n", Timer));
|
|
return NlMapStatus(NtCancelTimer(Timer->TimerHandle, NULL));
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlSetTimer(
|
|
IN PNlOWSER_TIMER Timer,
|
|
IN ULONG MillisecondsToExpire,
|
|
IN PNlOWSER_WORKER_ROUTINE WorkerFunction,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
LARGE_INTEGER TimerDueTime;
|
|
NTSTATUS NtStatus;
|
|
//
|
|
// Avoid setting a destroyed timer.
|
|
//
|
|
|
|
if ( Timer->TimerHandle == NULL ) {
|
|
NlPrint(( Nl_TIMER, "Setting a destroyed timer %lx\n", Timer));
|
|
return NERR_Success;
|
|
}
|
|
|
|
NlPrint(( Nl_TIMER, "Setting timer %lx to %ld milliseconds, WorkerFounction %lx, Context: %lx\n", Timer, MillisecondsToExpire, WorkerFunction, Context));
|
|
|
|
//
|
|
// Figure out the timeout.
|
|
//
|
|
|
|
TimerDueTime.QuadPart = Int32x32To64( MillisecondsToExpire, -10000 );
|
|
|
|
NlInitializeWorkItem(&Timer->WorkItem, WorkerFunction, Context);
|
|
|
|
//
|
|
// Set the timer to go off when it expires.
|
|
//
|
|
|
|
NtStatus = NtSetTimer(Timer->TimerHandle,
|
|
&TimerDueTime,
|
|
NlTimerRoutine,
|
|
Timer,
|
|
FALSE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
#if DBG
|
|
NlPrint(( NL_CRITICAL, "Unable to set Netlogon timer expiration: %X (%lx)\n", NtStatus, Timer));
|
|
DbgbreakPoint();
|
|
#endif
|
|
|
|
return(NlMapStatus(NtStatus));
|
|
}
|
|
|
|
return NERR_Success;
|
|
|
|
|
|
}
|
|
|
|
VOID
|
|
NlTimerRoutine(
|
|
IN PVOID TimerContext,
|
|
IN ULONG TImerLowValue,
|
|
IN LONG TimerHighValue
|
|
)
|
|
{
|
|
PNlOWSER_TIMER Timer = TimerContext;
|
|
|
|
NlPrint(( Nl_TIMER, "Timer %lx fired\n", Timer));
|
|
|
|
NlQueueWorkItem(&Timer->WorkItem);
|
|
}
|
|
#endif // notdef // Don't need timers yet
|