// -------------------------------------------------------------------------- // Module Name: Thread.cpp // // Copyright (c) 1999-2000, Microsoft Corporation // // Base class that implements thread functionality. Subclass this class and // implement the virtual ThreadEntry function. When you instantiate this // class a thread gets created which will call ThreadEntry and when that // function exits will call ThreadExit. These objects should be created using // operator new because the default implementation of ThreadExit does // "delete this". You should override this function if you don't want this // behavior. The threads are also created SUSPENDED. You make any changes // that are required in the subclass' constructor. At the end of the // constructor or from the caller of operator new a "->Resume()" can be // invoked to start the thread. // // History: 1999-08-24 vtan created // 2000-02-01 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- #include "StandardHeader.h" #include "Thread.h" #include "Access.h" #include "Impersonation.h" #include "StatusCode.h" #include "TokenInformation.h" // -------------------------------------------------------------------------- // CThread::CThread // // Arguments: stackSpace = Size of stack to reserve for this // thread. Default = system default. // createFlags = Additional flags designating how // the thread should be created. // Default = none. // hToken = User token to assign to the // thread. Default = none. // pSecurityDescriptor = SecurityDescriptor to assign to // thread. Default = none. // // Returns: // // Purpose: Initializes the CThread object. Creates the thread SUSPENDED // with given security attributes and assigns the hToken to the // thread. The token need not have SecurityImpersonation as a // duplicate is made with this access mode. // // History: 1999-08-24 vtan created // 2000-02-01 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- CThread::CThread (DWORD stackSpace, DWORD createFlags, HANDLE hToken) : CCountedObject(), _hThread(NULL), _fCompleted(false) { DWORD dwThreadID; // It is important to create the thread suspended. This constructor could // get pre-empted by the system and does. If pre-empted whilst executing // the constructor, the derived class is NOT fully constructed and the // virtual table isn't correctly initialized. _hThread = CreateThread(NULL, stackSpace, ThreadEntryProc, this, createFlags | CREATE_SUSPENDED, &dwThreadID); if (_hThread != NULL) { // Make a call to CCountedObject::AddRef here. This reference belongs // to the thread. It's necessary to do it now because the creator of // this thread can release its reference before the thread even begins // executing which would cause the object to be released! // CThread::ThreadEntryProc will release this reference when the // thread's execution is finished. The creator of this thread should // release its reference when it's done with the thread which may be // immediately in the case of an asynhronous operation in which the // thread cleans itself up. AddRef(); // Impersonate the user token if given. Also grant access to the thread // object to the user. This will allow them to query thread information. if (hToken != NULL) { TSTATUS(SetToken(hToken)); } } } // -------------------------------------------------------------------------- // CThread::~CThread // // Arguments: // // Returns: // // Purpose: Releases resources used by the CThread object on thread // termination. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- CThread::~CThread (void) { ASSERTMSG(_fCompleted, "CThread::~CThread called before ThreadEntry() completed"); ReleaseHandle(_hThread); } // -------------------------------------------------------------------------- // CThread::operator HANDLE // // Arguments: // // Returns: // // Purpose: Magically converts a CThread to a HANDLE // // History: 1999-09-21 vtan created // -------------------------------------------------------------------------- CThread::operator HANDLE (void) const { return(_hThread); } // -------------------------------------------------------------------------- // CThread::IsCreated // // Arguments: // // Returns: bool // // Purpose: Returns whether a thread was created or not. // // History: 2000-09-08 vtan created // -------------------------------------------------------------------------- bool CThread::IsCreated (void) const { return(_hThread != NULL); } // -------------------------------------------------------------------------- // CThread::Suspend // // Arguments: // // Returns: // // Purpose: Suspends thread execution. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- void CThread::Suspend (void) const { if (SuspendThread(_hThread) == 0xFFFFFFFF) { DISPLAYMSG("SuspendThread failed for thread handle in CThread::Suspend"); } } // -------------------------------------------------------------------------- // CThread::Resume // // Arguments: // // Returns: // // Purpose: Resumes thread execution. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- void CThread::Resume (void) const { if ((_hThread == NULL) || (ResumeThread(_hThread) == 0xFFFFFFFF)) { DISPLAYMSG("ResumeThread failed for thread handle in CThread::Resume"); } } // -------------------------------------------------------------------------- // CThread::Terminate // // Arguments: // // Returns: NTSTATUS // // Purpose: Forcibly terminates the thread. Use this with care. It should // only be used in case a sub-class constructor fails and the // thread is suspended and hasn't even run yet. // // History: 2000-10-18 vtan created // -------------------------------------------------------------------------- NTSTATUS CThread::Terminate (void) { NTSTATUS status; if (TerminateThread(_hThread, 0) != FALSE) { _fCompleted = true; Release(); ReleaseHandle(_hThread); status = STATUS_SUCCESS; } else { status = CStatusCode::StatusCodeOfLastError(); } return(status); } // -------------------------------------------------------------------------- // CThread::IsCompleted // // Arguments: // // Returns: bool // // Purpose: Determines whether the thread has completed execution. This // does not check the signaled state of the thread handle but // rather checks member variables. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- bool CThread::IsCompleted (void) const { DWORD dwExitCode; return((GetExitCodeThread(_hThread, &dwExitCode) != FALSE) && (dwExitCode != STILL_ACTIVE)); } // -------------------------------------------------------------------------- // CThread::WaitForCompletion // // Arguments: dwMilliseconds = Number of milliseconds to wait for // thread completion. // // Returns: DWORD // // Purpose: Waits for the thread handle to become signaled. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- DWORD CThread::WaitForCompletion (DWORD dwMilliseconds) const { DWORD dwWaitResult; do { // When waiting for the object check to see that it's not signaled. // If signaled then abandon the wait loop. Otherwise allow user32 // to continue processing messages for this thread. dwWaitResult = WaitForSingleObject(_hThread, 0); if (dwWaitResult != WAIT_OBJECT_0) { dwWaitResult = MsgWaitForMultipleObjects(1, &_hThread, FALSE, dwMilliseconds, QS_ALLINPUT); if (dwWaitResult == WAIT_OBJECT_0 + 1) { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE) { (BOOL)TranslateMessage(&msg); (LRESULT)DispatchMessage(&msg); } } } } while (dwWaitResult == WAIT_OBJECT_0 + 1); return(dwWaitResult); } // -------------------------------------------------------------------------- // CThread::GetResult // // Arguments: // // Returns: DWORD // // Purpose: Gets the thread's exit code. This assumes it has completed // execution and returns STILL_ACTIVE if not completed. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- DWORD CThread::GetResult (void) const { DWORD dwResult; if (GetExitCodeThread(_hThread, &dwResult) == FALSE) { dwResult = STILL_ACTIVE; } return(dwResult); } // -------------------------------------------------------------------------- // CThread::GetPriority // // Arguments: // // Returns: int // // Purpose: Gets the thread's priority. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- int CThread::GetPriority (void) const { return(GetThreadPriority(_hThread)); } // -------------------------------------------------------------------------- // CThread::SetPriority // // Arguments: newPriority = New priority for the thread. // // Returns: // // Purpose: Sets the thread's priority. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- void CThread::SetPriority (int newPriority) const { if (SetThreadPriority(_hThread, newPriority) == 0) { DISPLAYMSG("SetThreadPriorty failed in CThread::SetPriority"); } } // -------------------------------------------------------------------------- // CThread::ThreadExit // // Arguments: // // Returns: // // Purpose: Default base class implementation of thread exit. For threads // whose execution is self contained and termination is not an // issue then this will clean up after the thread. This function // should be overriden if this behavior is NOT desired. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- void CThread::Exit (void) { } // -------------------------------------------------------------------------- // CThread::SetToken // // Arguments: hToken = HANDLE to the user token to assign to this thread. // // Returns: NTSTATUS // // Purpose: Sets the impersonation token associated with this thread so // the thread will execute in the user's context from the start. // // History: 1999-09-23 vtan created // -------------------------------------------------------------------------- NTSTATUS CThread::SetToken (HANDLE hToken) { PSID pLogonSID; CTokenInformation tokenInformation(hToken); pLogonSID = tokenInformation.GetLogonSID(); if (pLogonSID != NULL) { CSecuredObject threadSecurity(_hThread, SE_KERNEL_OBJECT); TSTATUS(threadSecurity.Allow(pLogonSID, THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, 0)); TSTATUS(CImpersonation::ImpersonateUser(_hThread, hToken)); } return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CThread::ThreadEntryProc // // Arguments: pParameter = "this" object. // // Returns: DWORD // // Purpose: Entry procedure for the thread. This manages the type-casting // and invokation of CThread::ThreadEntry and CThread::ThreadExit // as well as the _fCompleted member variable. // // History: 1999-08-24 vtan created // -------------------------------------------------------------------------- DWORD WINAPI CThread::ThreadEntryProc (void *parameter) { DWORD dwThreadResult; CThread *pThread; pThread = static_cast(parameter); dwThreadResult = pThread->Entry(); pThread->_fCompleted = true; pThread->Exit(); pThread->Release(); return(dwThreadResult); }