827 lines
18 KiB
C
827 lines
18 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1997-1999 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
frsthrd.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
Simple thread management in addition to the queue management.
|
|||
|
|
|||
|
Author:
|
|||
|
Billy J. Fuller 26-Mar-1997
|
|||
|
|
|||
|
Revised:
|
|||
|
David Orbits - May 2000 : Revised naming.
|
|||
|
|
|||
|
Environment
|
|||
|
User mode winnt
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <ntreppch.h>
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
|
|||
|
#include <frs.h>
|
|||
|
#include <perrepsr.h>
|
|||
|
|
|||
|
#define THSUP_THREAD_TOMBSTONE (5 * 1000)
|
|||
|
|
|||
|
//
|
|||
|
// Global list of threads
|
|||
|
//
|
|||
|
LIST_ENTRY FrsThreadList;
|
|||
|
|
|||
|
//
|
|||
|
// Protects FrsThreadList
|
|||
|
//
|
|||
|
CRITICAL_SECTION FrsThreadCriticalSection;
|
|||
|
|
|||
|
//
|
|||
|
// Exiting threads can queue a "wait" request
|
|||
|
//
|
|||
|
COMMAND_SERVER ThCs;
|
|||
|
|
|||
|
|
|||
|
#if _MSC_FULL_VER >= 13008827
|
|||
|
#pragma warning(push)
|
|||
|
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
|
|||
|
#endif
|
|||
|
|
|||
|
DWORD
|
|||
|
ThSupMainCs(
|
|||
|
PVOID Arg
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Entry point for thread command server thread. This thread
|
|||
|
is needed for abort and exit processing; hence this command
|
|||
|
server is never aborted and the thread never exits!
|
|||
|
|
|||
|
Arguments:
|
|||
|
Arg - thread
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupMainCs:"
|
|||
|
PCOMMAND_PACKET Cmd;
|
|||
|
PCOMMAND_SERVER Cs;
|
|||
|
PFRS_THREAD FrsThread = Arg;
|
|||
|
|
|||
|
Cs = FrsThread->Data;
|
|||
|
FRS_ASSERT(Cs == &ThCs);
|
|||
|
|
|||
|
//
|
|||
|
// Our exit routine sets Data to non-NULL as a exit flag
|
|||
|
//
|
|||
|
while (TRUE) {
|
|||
|
Cmd = FrsGetCommandServer(&ThCs);
|
|||
|
if (Cmd == NULL) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
FRS_ASSERT(Cmd->Command == CMD_WAIT);
|
|||
|
FRS_ASSERT(ThThread(Cmd));
|
|||
|
|
|||
|
//
|
|||
|
// Any thread using the CMD_WAIT must get a thread reference the thread
|
|||
|
// before enqueueing the command to protect against multiple waiters.
|
|||
|
//
|
|||
|
// Wait for the thread to terminate. The assumption is that the thread
|
|||
|
// associated with the FrsThread struct will soon be terminating, ending the wait.
|
|||
|
//
|
|||
|
FrsThread = ThThread(Cmd);
|
|||
|
ThSupWaitThread(FrsThread, INFINITE);
|
|||
|
//
|
|||
|
// When the last ref is dropped the FrsThread struct is freed.
|
|||
|
//
|
|||
|
ThSupReleaseRef(FrsThread);
|
|||
|
FrsCompleteCommand(Cmd, ERROR_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
#if _MSC_FULL_VER >= 13008827
|
|||
|
#pragma warning(pop)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
ThSupExitWithTombstone(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Mark the thread as tombstoned. If this thread does not exit within that
|
|||
|
time, any calls to ThSupWaitThread() return a timeout error.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FrsThread
|
|||
|
|
|||
|
Return Value:
|
|||
|
ERROR_SUCCESS
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupExitWithTombstone:"
|
|||
|
//
|
|||
|
// If this thread does not exit within the THSUP_THREAD_TOMBSTONE interval then
|
|||
|
// don't wait on it and count it as an exit timeout.
|
|||
|
//
|
|||
|
FrsThread->ExitTombstone = GetTickCount();
|
|||
|
if (FrsThread->ExitTombstone == 0) {
|
|||
|
FrsThread->ExitTombstone = 1;
|
|||
|
}
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
ThSupExitThreadNOP(
|
|||
|
PVOID Arg
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Use this exit function when you don't want the cleanup code
|
|||
|
to kill the thread but you have no exit processing to do. E.g.,
|
|||
|
a command server.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
ERROR_SUCCESS
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupExitThreadNOP:"
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ThSupInitialize(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Initialize the FRS thread subsystem. Must be called before any other
|
|||
|
thread function and must be called only once.
|
|||
|
|
|||
|
Arguments:
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupInitialize:"
|
|||
|
|
|||
|
InitializeCriticalSection(&FrsThreadCriticalSection);
|
|||
|
|
|||
|
InitializeListHead(&FrsThreadList);
|
|||
|
|
|||
|
FrsInitializeCommandServer(&ThCs, 1, L"ThCs: Wait on threads.", ThSupMainCs);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PVOID
|
|||
|
ThSupGetThreadData(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Return the thread specific data portion of the thread context.
|
|||
|
|
|||
|
Arguments:
|
|||
|
FrsThread - thread context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
Thread specific data
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupGetThreadData:"
|
|||
|
|
|||
|
return (FrsThread->Data);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PFRS_THREAD
|
|||
|
ThSupAllocThread(
|
|||
|
PWCHAR Name,
|
|||
|
PVOID Param,
|
|||
|
DWORD (*Main)(PVOID),
|
|||
|
DWORD (*Exit)(PVOID)
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Allocate a thread context and call its Init routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
Param - parameter to the thread
|
|||
|
Main - entry point
|
|||
|
Exit - called to force thread to exit
|
|||
|
|
|||
|
Return Value:
|
|||
|
Thread context.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupAllocThread:"
|
|||
|
ULONG Status;
|
|||
|
PFRS_THREAD FrsThread;
|
|||
|
|
|||
|
//
|
|||
|
// Create a thread context for a soon-to-be-running thread
|
|||
|
//
|
|||
|
FrsThread = FrsAllocType(THREAD_TYPE);
|
|||
|
|
|||
|
FrsThread->Name = Name;
|
|||
|
FrsThread->Running = TRUE;
|
|||
|
FrsThread->Ref = 1;
|
|||
|
FrsThread->Main = Main;
|
|||
|
FrsThread->Exit = Exit;
|
|||
|
FrsThread->Data = Param;
|
|||
|
|
|||
|
InitializeListHead(&FrsThread->List);
|
|||
|
|
|||
|
//
|
|||
|
// Add to global list of threads (Unless this is our command server)
|
|||
|
//
|
|||
|
if (Main != ThSupMainCs) {
|
|||
|
EnterCriticalSection(&FrsThreadCriticalSection);
|
|||
|
InsertTailList(&FrsThreadList, &FrsThread->List);
|
|||
|
LeaveCriticalSection(&FrsThreadCriticalSection);
|
|||
|
}
|
|||
|
|
|||
|
return FrsThread;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ThSupReleaseRef(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Dec the thread's reference count. If the count goes to 0, free
|
|||
|
the thread context.
|
|||
|
|
|||
|
Arguments:
|
|||
|
FrsThread - thread context
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupReleaseRef:"
|
|||
|
|
|||
|
if (FrsThread == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
FRS_ASSERT(FrsThread->Main != ThSupMainCs || FrsThread->Running);
|
|||
|
|
|||
|
//
|
|||
|
// If the ref count goes to 0 and the thread isn't running, free the context
|
|||
|
//
|
|||
|
EnterCriticalSection(&FrsThreadCriticalSection);
|
|||
|
FRS_ASSERT(FrsThread->Ref > 0);
|
|||
|
|
|||
|
if (--FrsThread->Ref == 0 && !FrsThread->Running) {
|
|||
|
FrsRemoveEntryList(&FrsThread->List);
|
|||
|
} else {
|
|||
|
FrsThread = NULL;
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&FrsThreadCriticalSection);
|
|||
|
|
|||
|
//
|
|||
|
// ref count is not 0 or the thread is still running
|
|||
|
//
|
|||
|
if (FrsThread == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Ref count is 0; Close the thread's handle.
|
|||
|
//
|
|||
|
FRS_CLOSE(FrsThread->Handle);
|
|||
|
|
|||
|
FrsThread = FrsFreeType(FrsThread);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PFRS_THREAD
|
|||
|
ThSupEnumThreads(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine scans the list of threads. If FrsThread is NULL,
|
|||
|
the current head is returned. Otherwise, the next entry is returned.
|
|||
|
If FrsThead is non-Null its ref count is decremented.
|
|||
|
|
|||
|
Arguments:
|
|||
|
FrsThread - thread context or NULL
|
|||
|
|
|||
|
Return Value:
|
|||
|
The next thread context or NULL if we hit the end of the list.
|
|||
|
A reference is taken on the returned thread.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupEnumThreads:"
|
|||
|
|
|||
|
PLIST_ENTRY Entry;
|
|||
|
PFRS_THREAD NextFrsThread;
|
|||
|
|
|||
|
//
|
|||
|
// Get the next thread context (the head of the list if FrsThread is NULL)
|
|||
|
//
|
|||
|
EnterCriticalSection(&FrsThreadCriticalSection);
|
|||
|
|
|||
|
Entry = (FrsThread != NULL) ? GetListNext(&FrsThread->List)
|
|||
|
: GetListNext(&FrsThreadList);
|
|||
|
|
|||
|
if (Entry == &FrsThreadList) {
|
|||
|
//
|
|||
|
// back at the head of the list
|
|||
|
//
|
|||
|
NextFrsThread = NULL;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Increment the ref count
|
|||
|
//
|
|||
|
NextFrsThread = CONTAINING_RECORD(Entry, FRS_THREAD, List);
|
|||
|
NextFrsThread->Ref++;
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&FrsThreadCriticalSection);
|
|||
|
|
|||
|
//
|
|||
|
// Release the reference on the old thread context.
|
|||
|
//
|
|||
|
if (FrsThread != NULL) {
|
|||
|
ThSupReleaseRef(FrsThread);
|
|||
|
}
|
|||
|
|
|||
|
return NextFrsThread;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
ThSupCreateThread(
|
|||
|
PWCHAR Name,
|
|||
|
PVOID Param,
|
|||
|
DWORD (*Main)(PVOID),
|
|||
|
DWORD (*Exit)(PVOID)
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Kick off the thread and return its context.
|
|||
|
|
|||
|
Note: The caller must release thread reference when done
|
|||
|
|
|||
|
Arguments:
|
|||
|
Param - parameter to the thread
|
|||
|
Main - entry point
|
|||
|
Exit - called to force thread to exit
|
|||
|
|
|||
|
Return Value:
|
|||
|
Thread context. Caller must call ThSupReleaseRef() to release it.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupCreateThread:"
|
|||
|
PFRS_THREAD FrsThread;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a thread context
|
|||
|
//
|
|||
|
FrsThread = ThSupAllocThread(Name, Param, Main, Exit);
|
|||
|
if (FrsThread == NULL) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
//
|
|||
|
// Kick off the thread
|
|||
|
//
|
|||
|
FrsThread->Handle = (HANDLE) CreateThread(NULL,
|
|||
|
10000,
|
|||
|
Main,
|
|||
|
(PVOID)FrsThread,
|
|||
|
0,
|
|||
|
&FrsThread->Id);
|
|||
|
//
|
|||
|
// thread is not running. The following ThSupReleaseRef will clean up.
|
|||
|
//
|
|||
|
if (!HANDLE_IS_VALID(FrsThread->Handle)) {
|
|||
|
DPRINT_WS(0, "Can't start thread; ",GetLastError());
|
|||
|
FrsThread->Running = FALSE;
|
|||
|
ThSupReleaseRef(FrsThread);
|
|||
|
return FALSE;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Increment the Threads started counter
|
|||
|
//
|
|||
|
PM_INC_CTR_SERVICE(PMTotalInst, ThreadsStarted, 1);
|
|||
|
|
|||
|
DPRINT3(4, ":S: Starting thread %ws: Id %d (%08x)\n",
|
|||
|
Name, FrsThread->Id, FrsThread->Id);
|
|||
|
|
|||
|
ThSupReleaseRef(FrsThread);
|
|||
|
|
|||
|
DbgCaptureThreadInfo2(Name, Main, FrsThread->Id);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
ThSupWaitThread(
|
|||
|
PFRS_THREAD FrsThread,
|
|||
|
DWORD Millisec
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Wait at most MilliSeconds for the thread to exit. If the thread has set
|
|||
|
a wait tombstone (i.e. it is terminating) then don't wait longer than the
|
|||
|
time remaining on the tombstone.
|
|||
|
|
|||
|
Arguments:
|
|||
|
FrsThread - thread context
|
|||
|
Millisec - Time to wait. Use INFINITE if no timeout desired.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of the wait if timeout or the exit code of the thread.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupWaitThread:"
|
|||
|
|
|||
|
ULONGLONG CreateTime, ExitTime, KernelTime, UserTime;
|
|||
|
|
|||
|
DWORD WStatus, Status, ExitCode;
|
|||
|
DWORD Beg, End, TimeSinceTombstone;
|
|||
|
|
|||
|
//
|
|||
|
// No problems waiting for this one!
|
|||
|
//
|
|||
|
if (!FrsThread || !HANDLE_IS_VALID(FrsThread->Handle)) {
|
|||
|
return ERROR_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Wait awhile for the thread to exit
|
|||
|
//
|
|||
|
DPRINT1(1, ":S: %ws: Waiting\n", FrsThread->Name);
|
|||
|
|
|||
|
Beg = GetTickCount();
|
|||
|
if (FrsThread->ExitTombstone != 0) {
|
|||
|
//
|
|||
|
// The thread has registered an exit tombstone so don't wait past that
|
|||
|
// time. Simply return a timeout error. Note: GetTickCount has a
|
|||
|
// period of 49.7 days so the unsigned difference handles the wrap problem.
|
|||
|
//
|
|||
|
TimeSinceTombstone = Beg - FrsThread->ExitTombstone;
|
|||
|
if (TimeSinceTombstone >= THSUP_THREAD_TOMBSTONE) {
|
|||
|
//
|
|||
|
// Tombstone expired
|
|||
|
//
|
|||
|
DPRINT1(1, ":S: %ws: Tombstone expired\n", FrsThread->Name);
|
|||
|
Status = WAIT_TIMEOUT;
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Tombstone has a ways to go; wait only up to the tombstone time.
|
|||
|
//
|
|||
|
DPRINT1(1, ":S: %ws: Tombstone expiring\n", FrsThread->Name);
|
|||
|
Status = WaitForSingleObject(FrsThread->Handle,
|
|||
|
THSUP_THREAD_TOMBSTONE - TimeSinceTombstone);
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// no tombstone; wait the requested time
|
|||
|
//
|
|||
|
DPRINT1(1, ":S: %ws: normal wait\n", FrsThread->Name);
|
|||
|
Status = WaitForSingleObject(FrsThread->Handle, Millisec);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Adjust the error status based on the outcome
|
|||
|
//
|
|||
|
if ((Status == WAIT_OBJECT_0) || (Status == WAIT_ABANDONED)) {
|
|||
|
DPRINT1_WS(1, ":S: %ws: wait successful. ", FrsThread->Name, Status);
|
|||
|
WStatus = ERROR_SUCCESS;
|
|||
|
} else {
|
|||
|
if (Status == WAIT_FAILED) {
|
|||
|
WStatus = GetLastError();
|
|||
|
DPRINT1_WS(1, ":S: %ws: wait failed;", FrsThread->Name, WStatus);
|
|||
|
} else {
|
|||
|
DPRINT1_WS(1, ":S: %ws: wait timed out. ", FrsThread->Name, Status);
|
|||
|
WStatus = ERROR_TIMEOUT;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Wait over
|
|||
|
//
|
|||
|
End = GetTickCount();
|
|||
|
DPRINT2_WS(1, ":S: Done waiting for thread %ws (%d ms); ", FrsThread->Name, End - Beg, WStatus);
|
|||
|
|
|||
|
//
|
|||
|
// Thread has exited. Get exit status and set thread struct as "not running".
|
|||
|
//
|
|||
|
if (WIN_SUCCESS(WStatus)) {
|
|||
|
FrsThread->Running = FALSE;
|
|||
|
|
|||
|
if (GetExitCodeThread(FrsThread->Handle, &ExitCode)) {
|
|||
|
WStatus = ExitCode;
|
|||
|
DPRINT1_WS(1, ":S: %ws: exit code - \n", FrsThread->Name, WStatus);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (GetThreadTimes(FrsThread->Handle,
|
|||
|
(PFILETIME)&CreateTime,
|
|||
|
(PFILETIME)&ExitTime,
|
|||
|
(PFILETIME)&KernelTime,
|
|||
|
(PFILETIME)&UserTime)) {
|
|||
|
//
|
|||
|
// Hasn't exited, yet
|
|||
|
//
|
|||
|
if (ExitTime < CreateTime) {
|
|||
|
ExitTime = CreateTime;
|
|||
|
}
|
|||
|
DPRINT4(4, ":S: %-15ws: %8d CPU Seconds (%d kernel, %d elapsed)\n",
|
|||
|
FrsThread->Name,
|
|||
|
(DWORD)((KernelTime + UserTime) / (10 * 1000 * 1000)),
|
|||
|
(DWORD)((KernelTime) / (10 * 1000 * 1000)),
|
|||
|
(DWORD)((ExitTime - CreateTime) / (10 * 1000 * 1000)));
|
|||
|
}
|
|||
|
|
|||
|
return WStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
ThSupExitThreadGroup(
|
|||
|
IN DWORD (*Main)(PVOID)
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Force the group of threads with the given Main function to exit by
|
|||
|
calling their exit routine. Wait for the threads to exit.
|
|||
|
|
|||
|
Arguments:
|
|||
|
Main - Main function or NULL
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupExitThreadGroup:"
|
|||
|
DWORD WStatus;
|
|||
|
DWORD RetWStatus;
|
|||
|
PFRS_THREAD FrsThread;
|
|||
|
ULONGLONG CreateTime;
|
|||
|
ULONGLONG ExitTime;
|
|||
|
ULONGLONG KernelTime;
|
|||
|
ULONGLONG UserTime;
|
|||
|
|
|||
|
//
|
|||
|
// call the threads exit function (forcibly terminate if none)
|
|||
|
//
|
|||
|
FrsThread = NULL;
|
|||
|
while (FrsThread = ThSupEnumThreads(FrsThread)) {
|
|||
|
if (Main == NULL || Main == FrsThread->Main) {
|
|||
|
ThSupExitSingleThread(FrsThread);
|
|||
|
}
|
|||
|
}
|
|||
|
//
|
|||
|
// wait for the threads to exit
|
|||
|
//
|
|||
|
RetWStatus = ERROR_SUCCESS;
|
|||
|
FrsThread = NULL;
|
|||
|
while (FrsThread = ThSupEnumThreads(FrsThread)) {
|
|||
|
if (Main == NULL || Main == FrsThread->Main) {
|
|||
|
WStatus = ThSupWaitThread(FrsThread, INFINITE);
|
|||
|
if (!WIN_SUCCESS(WStatus)) {
|
|||
|
RetWStatus = WStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if (GetThreadTimes(GetCurrentThread(),
|
|||
|
(PFILETIME)&CreateTime,
|
|||
|
(PFILETIME)&ExitTime,
|
|||
|
(PFILETIME)&KernelTime,
|
|||
|
(PFILETIME)&UserTime)) {
|
|||
|
//
|
|||
|
// Hasn't exited, yet
|
|||
|
//
|
|||
|
if (ExitTime < CreateTime) {
|
|||
|
ExitTime = CreateTime;
|
|||
|
}
|
|||
|
DPRINT4(4, ":S: %-15ws: %8d CPU Seconds (%d kernel, %d elapsed)\n",
|
|||
|
L"SHUTDOWN",
|
|||
|
(DWORD)((KernelTime + UserTime) / (10 * 1000 * 1000)),
|
|||
|
(DWORD)((KernelTime) / (10 * 1000 * 1000)),
|
|||
|
(DWORD)((ExitTime - CreateTime) / (10 * 1000 * 1000)));
|
|||
|
}
|
|||
|
if (GetProcessTimes(ProcessHandle,
|
|||
|
(PFILETIME)&CreateTime,
|
|||
|
(PFILETIME)&ExitTime,
|
|||
|
(PFILETIME)&KernelTime,
|
|||
|
(PFILETIME)&UserTime)) {
|
|||
|
//
|
|||
|
// Hasn't exited, yet
|
|||
|
//
|
|||
|
if (ExitTime < CreateTime) {
|
|||
|
ExitTime = CreateTime;
|
|||
|
}
|
|||
|
DPRINT4(0, ":S: %-15ws: %8d CPU Seconds (%d kernel, %d elapsed)\n",
|
|||
|
L"PROCESS",
|
|||
|
(DWORD)((KernelTime + UserTime) / (10 * 1000 * 1000)),
|
|||
|
(DWORD)((KernelTime) / (10 * 1000 * 1000)),
|
|||
|
(DWORD)((ExitTime - CreateTime) / (10 * 1000 * 1000)));
|
|||
|
}
|
|||
|
|
|||
|
return RetWStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ThSupExitSingleThread(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Force the thread to exit
|
|||
|
|
|||
|
Arguments:
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupExitSingleThread:"
|
|||
|
|
|||
|
//
|
|||
|
// call the thread's exit function (forcibly terminate if none)
|
|||
|
//
|
|||
|
if (FrsThread->Exit != NULL) {
|
|||
|
(*FrsThread->Exit)(FrsThread);
|
|||
|
} else {
|
|||
|
//
|
|||
|
// No exit function; forcibly terminate
|
|||
|
//
|
|||
|
if (HANDLE_IS_VALID(FrsThread->Handle)) {
|
|||
|
TerminateThread(FrsThread->Handle, STATUS_UNSUCCESSFUL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Increment the Threads exited counter
|
|||
|
//
|
|||
|
PM_INC_CTR_SERVICE(PMTotalInst, ThreadsExited, 1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PFRS_THREAD
|
|||
|
ThSupGetThread(
|
|||
|
DWORD (*Main)(PVOID)
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Locate a thread whose entry point is Main.
|
|||
|
|
|||
|
Arguments:
|
|||
|
Main - entry point to search for.
|
|||
|
|
|||
|
Return Value:
|
|||
|
thread context
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupGetThread:"
|
|||
|
|
|||
|
PFRS_THREAD FrsThread;
|
|||
|
|
|||
|
//
|
|||
|
// Scan the list of threads looking for one whose entry point is Main
|
|||
|
//
|
|||
|
FrsThread = NULL;
|
|||
|
while (FrsThread = ThSupEnumThreads(FrsThread)) {
|
|||
|
if (FrsThread->Main == Main) {
|
|||
|
return FrsThread;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ThSupAcquireRef(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
Inc the thread's reference count.
|
|||
|
|
|||
|
Arguments:
|
|||
|
FrsThread - thread context
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupAcquireRef:"
|
|||
|
|
|||
|
FRS_ASSERT(FrsThread);
|
|||
|
FRS_ASSERT(FrsThread->Running);
|
|||
|
|
|||
|
//
|
|||
|
// If the ref count goes to 0 and the thread isn't running, free the context
|
|||
|
//
|
|||
|
EnterCriticalSection(&FrsThreadCriticalSection);
|
|||
|
++FrsThread->Ref;
|
|||
|
LeaveCriticalSection(&FrsThreadCriticalSection);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
ThSupSubmitThreadExitCleanup(
|
|||
|
PFRS_THREAD FrsThread
|
|||
|
)
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Submit a wait command for this thread to the thread command server.
|
|||
|
|
|||
|
The thread command server (ThQs) will do an infinte wait on this thread's exit
|
|||
|
and drop the reference on its thread struct so it can be cleaned up.
|
|||
|
|
|||
|
The assumption is that the thread associated with the FrsThread struct will
|
|||
|
soon be terminating.
|
|||
|
|
|||
|
Arguments:
|
|||
|
FrsThread - thread context
|
|||
|
|
|||
|
Return Value:
|
|||
|
None.
|
|||
|
--*/
|
|||
|
{
|
|||
|
#undef DEBSUB
|
|||
|
#define DEBSUB "ThSupSubmitThreadExitCleanup:"
|
|||
|
|
|||
|
PCOMMAND_PACKET Cmd;
|
|||
|
|
|||
|
//
|
|||
|
// Reference the thread until after the wait has completed to guard
|
|||
|
// against the case of multiple waiters
|
|||
|
//
|
|||
|
ThSupAcquireRef(FrsThread);
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a command packet and send the command off to the
|
|||
|
// thread command server.
|
|||
|
//
|
|||
|
Cmd = FrsAllocCommand(&ThCs.Queue, CMD_WAIT);
|
|||
|
ThThread(Cmd) = FrsThread;
|
|||
|
FrsSubmitCommandServer(&ThCs, Cmd);
|
|||
|
}
|