Windows2003-3790/windows/advcore/duser/engine/services/thread.cpp
2020-09-30 16:53:55 +02:00

394 lines
11 KiB
C++

/***************************************************************************\
*
* File: Thread.cpp
*
* Description:
* This file implements the main Thread that is maintained by the
* ResourceManager to store per-thread information.
*
*
* History:
* 4/18/2000: JStall: Created
*
* Copyright (C) 2000 by Microsoft Corporation. All rights reserved.
*
\***************************************************************************/
#include "stdafx.h"
#include "Services.h"
#include "Thread.h"
#include "Context.h"
#if !USE_DYNAMICTLS
__declspec(thread) Thread * t_pThread;
#endif
/***************************************************************************\
*****************************************************************************
*
* class Thread
*
*****************************************************************************
\***************************************************************************/
//------------------------------------------------------------------------------
Thread::~Thread()
{
m_fStartDestroy = TRUE;
//
// NOTE: The Thread object may be destroyed on its own thread or on another
// thread. Therefore, t_pThread may or may not be == this.
//
//
// Notify the Context that one less thread is using it. Want to do near
// the end since the Context heap may be destroyed after calling this.
// This means that (new / delete) will no longer be valid.
//
// Need to call Context::xwUnlock() directly because
// Context::DeleteObject() will call the ResourceManager to destroy the
// Thread (which is where we already are.)
//
//
// NOTE: We can only destroy the SubThread's when the Thread has a Context.
// This is because destruction of the SubThread's is an "xw" function that
// requires a Context. This unfortunately means that if we were unable
// to create the Context, we're going to leak the SubTread's, but there is
// little we can do about it.
//
m_poolReturn.Destroy();
if (m_pContext != NULL) {
if (m_pContext->xwUnlockNL(xwContextFinalUnlockProc, this)) {
xwDestroySubThreads();
}
m_pContext = NULL;
}
//
// Cleanup cached GDI objects
//
if (hrgnClip != NULL) {
DeleteObject(hrgnClip);
}
//
// NOTE: When m_lstReturn's destructor is called, it will check that all
// memory has been returned. It is possible this may not be empty if memory
// was returned after we emptied m_lstReturn in xwDestroySubThreads(). This
// is an application error since the memory wasn't allocated using
// SGM_RECEIVECONTEXT.
//
// This is actually a serious application problem, since T2 is still using
// memory owned by T1 when T1 is being destroyed. Unfortunately, DirectUser
// can not really do that much about it since the application is using DUser
// in an invalid manner and there are significant performance costs and
// design complications by changing this.
//
}
//------------------------------------------------------------------------------
void
Thread::xwDestroySubThreads()
{
if (m_fDestroySubThreads) {
return;
}
m_fDestroySubThreads = TRUE;
//
// Notify the sub-threads that the Thread and (potentially) the Context
// are being destroyed. This gives them an opportunity to perform any
// necessary callbacks to the application.
//
for (int idx = 0; idx < slCOUNT; idx++) {
if (m_rgSTs[idx] != NULL) {
ProcessDelete(SubThread, m_rgSTs[idx]);
m_rgSTs[idx] = NULL;
}
}
//
// Destroy any other objects that may depend on the Context (and the
// Context heap).
//
m_GdiCache.Destroy();
m_manBuffer.Destroy();
m_heapTemp.Destroy();
//
// Clean up any outstanding returned memory. We need to keep track of all
// the memory this Thread gives out since we can not go away until it has
// all returned. If we were to go away before then, the heap would be
// destroyed and the other Thread would be using bad data.
//
// Therefore, we will make an attempt to get all of the memory back. If
// it takes longer than one minute, we'll have to bail.
//
int cAttempts = 60 * 1000; // Wait a maximum of 60 seconds
while ((m_cMemAlloc > 0) && (cAttempts-- > 0)) {
while (!m_lstReturn.IsEmptyNL()) {
ReturnAllMemoryNL();
}
if (m_cMemAlloc > 0) {
Sleep(1);
}
}
m_poolReturn.Destroy();
}
//------------------------------------------------------------------------------
void CALLBACK
Thread::xwContextFinalUnlockProc(BaseObject * pobj, void * pvData)
{
Thread * pthr = reinterpret_cast<Thread *> (pvData);
Context * pctx = static_cast<Context *> (pobj);
pctx->xwPreDestroyNL();
pthr->xwDestroySubThreads();
}
//------------------------------------------------------------------------------
HRESULT
Thread::Build(
IN BOOL fSRT, // Thread is an SRT
OUT Thread ** ppthrNew) // Newly created thread
{
//
// Check if this Thread is already initialized
//
#if USE_DYNAMICTLS
Thread * pThread = reinterpret_cast<Thread *> (TlsGetValue(g_tlsThread));
if (pThread != NULL) {
*ppthrNew = pThread;
#else
Thread * pThread = t_pThread;
if (pThread != NULL) {
*ppthrNew = pThread;
#endif
return S_OK;
}
HRESULT hr = E_INVALIDARG;
//
// Create a new Thread
//
pThread = ProcessNew(Thread);
if (pThread == NULL) {
hr = E_OUTOFMEMORY;
goto ErrorExit;
}
pThread->hrgnClip = CreateRectRgn(0, 0, 0, 0);
if (pThread->hrgnClip == NULL) {
hr = E_OUTOFMEMORY;
goto ErrorExit;
}
pThread->m_fSRT = fSRT;
//
// Initialize each of the sub-contexts. These can safely use the heap
// which has already been initialized.
//
{
for (int idx = 0; idx < slCOUNT; idx++) {
ThreadPackBuilder * pBuilder = ThreadPackBuilder::GetBuilder((Thread::ESlot) idx);
AssertMsg(pBuilder != NULL, "Builder not initialized using INIT_SUBTHREAD");
SubThread * pST = pBuilder->New(pThread);
pThread->m_rgSTs[idx] = pST;
if ((pThread->m_rgSTs[idx] == NULL) ||
FAILED(hr = pThread->m_rgSTs[idx]->Create())) {
goto ErrorExit;
}
}
}
AssertMsg(pThread != NULL, "Ensure Thread is valid");
#if USE_DYNAMICTLS
AssertMsg(TlsGetValue(g_tlsThread) == NULL, "Ensure TLS is still empty");
Verify(TlsSetValue(g_tlsThread, pThread));
#else
AssertMsg(t_pThread == NULL, "Ensure TLS is still empty");
t_pThread = pThread;
#endif
*ppthrNew = pThread;
return S_OK;
ErrorExit:
//
// An error occurred while initializing the thread, so need to tear down
// the object.
//
if (pThread != NULL) {
if (pThread->hrgnClip != NULL) {
DeleteObject(pThread->hrgnClip);
}
if (pThread != NULL) {
ProcessDelete(Thread, pThread);
}
}
*ppthrNew = NULL;
return hr;
}
//------------------------------------------------------------------------------
ReturnMem *
Thread::AllocMemoryNL(
IN int cbSize) // Size of allocating, including ReturnMem
{
AssertMsg(cbSize >= sizeof(ReturnMem),
"Allocation must be at least sizeof(ReturnMem)");
AssertMsg(!m_fDestroySubThreads, "Must be before subtreads start destruction");
//
// Before allocating memory from our pool, return memory back to the pool if
// the pool is already empty. Only do this if the pool is empty.
// Otherwise, the effort is unnecessary and just slows things down.
//
if (m_poolReturn.IsEmpty()) {
ReturnAllMemoryNL();
}
//
// Now, allocate the memory. Allocate from our pool if it is within the
// pool size.
//
ReturnMem * prMem;
if (cbSize <= POOLBLOCK_SIZE) {
cbSize = POOLBLOCK_SIZE;
prMem = m_poolReturn.New();
} else {
prMem = reinterpret_cast<ReturnMem *> (ContextAlloc(GetContext()->GetHeap(), cbSize));
}
if (prMem != NULL) {
prMem->cbSize = cbSize;
m_cMemAlloc++;
}
return prMem;
}
//------------------------------------------------------------------------------
void
Thread::ReturnAllMemoryNL()
{
//
// Check if any memory has been returned. If so, we need to add it back
// into the pool if it is the right size. Only need to ExtractNL() once.
// If we loop, we unnecessarily hit the S-List memory, causing more
// slow-downs.
//
// As we return the memory, we decrement the number of outstanding
// allocations to keep track of how many are remaining.
//
ReturnMem * pNode = m_lstReturn.ExtractNL();
while (pNode != NULL) {
ReturnMem * pNext = pNode->pNext;
if (pNode->cbSize == POOLBLOCK_SIZE) {
PoolMem * pPoolMem = static_cast<PoolMem *> (pNode);
m_poolReturn.Delete(pPoolMem);
} else {
ContextFree(GetContext()->GetHeap(), pNode);
}
AssertMsg(m_cMemAlloc > 0, "Must have a remaining memory allocation");
m_cMemAlloc--;
pNode = pNext;
}
}
#if DBG
//------------------------------------------------------------------------------
void
Thread::DEBUG_AssertValid() const
{
Assert(hrgnClip != NULL);
AssertInstance(m_pContext);
for (int idx = 0; idx < slCOUNT; idx++) {
AssertInstance(m_rgSTs[idx]);
}
if (!m_fStartDestroy) {
Assert(m_cRef > 0);
Assert(!m_fDestroySubThreads);
}
}
#endif
/***************************************************************************\
*****************************************************************************
*
* class SubThread
*
*****************************************************************************
\***************************************************************************/
#if DBG
//------------------------------------------------------------------------------
void
SubThread::DEBUG_AssertValid() const
{
// Don't use AssertInstance since it would be recursive.
Assert(m_pParent != NULL);
}
#endif
/***************************************************************************\
*****************************************************************************
*
* class ThreadPackBuilder
*
*****************************************************************************
\***************************************************************************/
PREINIT_SUBTHREAD(CoreST);
ThreadPackBuilder * ThreadPackBuilder::s_rgBuilders[Thread::slCOUNT] =
{
INIT_SUBTHREAD(CoreST),
};