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

482 lines
14 KiB
C++

/***************************************************************************\
*
* File: Context.cpp
*
* Description:
* This file implements the main Context used by the ResourceManager to manage
* independent "work contexts".
*
*
* History:
* 4/18/2000: JStall: Created
*
* Copyright (C) 2000 by Microsoft Corporation. All rights reserved.
*
\***************************************************************************/
#include "stdafx.h"
#include "Services.h"
#include "Context.h"
#include "Thread.h"
#include "ResourceManager.h"
#if !USE_DYNAMICTLS
__declspec(thread) Context * t_pContext;
#endif
/***************************************************************************\
*****************************************************************************
*
* class Context
*
*****************************************************************************
\***************************************************************************/
//------------------------------------------------------------------------------
Context::Context()
{
//
// Immediately attach this Context to the Thread object. This is required
// because creation of the Context may need the Thread (for example, to
// create the Parking Container). If something fails during Context
// creation or later, the new Thread will be unlocked, destroying this new
// Context with it.
//
GetThread()->SetContext(this);
#if DBG
m_DEBUG_pthrLock = NULL;
#endif // DBG
}
//------------------------------------------------------------------------------
Context::~Context()
{
#if DBG_CHECK_CALLBACKS
if (m_cLiveCallbacks > 0) {
AutoTrace("DirectUser Context: 0x%p\n", this);
AlwaysPromptInvalid("Can not destroy a Context while inside a callback");
}
#endif
//
// NOTE: The Context (and its SubContexts) can be destroyed on a different
// thread during destruction. It is advisable to allocate any dangling data
// on the Process heap so that it can be safely destroyed at this time.
//
//
// First, tear down the sub-contexts since they may rely on shared resources
// such as the heap.
//
// NOTE: We need to destroy the SubContext's in multiple stages so that they
// can refer to each other during destruction. This provides an opportunity
// for any callbacks to occur during the "pre-destroy" stage. We
// temporarily need to increment the lock count while we pre-destroy the
// SubContext's because they may callback. During these callbacks, the
// application may call API's to cleanup objects in the Context.
//
for (int idx = 0; idx < slCOUNT; idx++) {
if (m_rgSCs[idx] != NULL) {
ProcessDelete(SubContext, m_rgSCs[idx]);
m_rgSCs[idx] = NULL;
}
}
#if DBG_CHECK_CALLBACKS
if (m_cLiveObjects > 0) {
AutoTrace("DirectUser Context: 0x%p\n", this);
AlwaysPromptInvalid("Outstanding DirectUser objects after Context shutdown");
}
#endif
//
// Tear down low-level resources (such as the heap)
//
if (m_pHeap != NULL) {
DestroyContextHeap(m_pHeap);
}
//
// Finally, detach this Context from the Thread. This must be done here
// since the Context is created in Context::Build() and must be fully
// detached from the Thread if any stage of construction fails.
//
GetThread()->SetContext(NULL);
}
/***************************************************************************\
*
* Context::xwDestroy
*
* xwDestroy() is called to finally delete the object.
*
\***************************************************************************/
void
Context::xwDestroy()
{
ProcessDelete(Context, this);
}
/***************************************************************************\
*
* Context::xwPreDestroyNL
*
* xwPreDestroyNL() is called by a Thread when the Context is about to be
* destroyed, but before the SubTreads have been destroyed.
*
\***************************************************************************/
void
Context::xwPreDestroyNL()
{
AssertMsg(m_cRef == 0, "Locks must initially be at 0 to be destroyed");
m_cRef++;
for (int idx = 0; idx < slCOUNT; idx++) {
if (m_rgSCs[idx] != NULL) {
m_rgSCs[idx]->xwPreDestroyNL();
}
}
m_cRef--;
AssertMsg(m_cRef == 0, "Locks should be 0 after callbacks");
}
/***************************************************************************\
*
* Context::Build
*
* Build() creates a new, fully initialized Context instance.
*
* NOTE: This function is designed to be called from the ResourceManager
* and should not normally be called directly.
*
* <error> E_OUTOFMEMORY</>
* <error> E_NOTIMPL</>
* <error> E_INVALIDARG</>
*
\***************************************************************************/
HRESULT
Context::Build(
IN INITGADGET * pInit, // Context description
IN DUserHeap * pHeap, // Context heap to use
OUT Context ** ppctxNew) // Newly created Context
{
#if USE_DYNAMICTLS
AssertMsg(!IsInitContext(), "Only call on uninitialized Context's");
#else
AssertMsg(t_pContext == NULL, "Only call on uninitialized Context's");
#endif
Context * pContext = NULL;
HRESULT hr = E_INVALIDARG;
//
// Create a new Context and initialize low-level resources that other
// initialization requires (such as a heap).
//
pContext = ProcessNew(Context);
if (pContext == NULL) {
hr = E_OUTOFMEMORY;
goto ErrorExit;
}
AssertMsg(pHeap != NULL, "Must specify a valid heap");
pContext->m_pHeap = pHeap;
pContext->m_nThreadMode = pInit->nThreadMode;
pContext->m_nPerfMode = pInit->nPerfMode;
if ((pContext->m_nPerfMode == IGPM_BLEND) && IsRemoteSession()) {
//
// For "blend" models, if we are running as a TS session, optimize for
// size.
//
pContext->m_nPerfMode = IGPM_SIZE;
}
BOOL fThreadSafe;
switch (pInit->nThreadMode)
{
case IGTM_SINGLE:
case IGTM_SEPARATE:
fThreadSafe = FALSE;
break;
default:
fThreadSafe = TRUE;
}
pContext->m_lock.SetThreadSafe(fThreadSafe);
//
// Initialize each of the sub-contexts. These can safely use the heap
// which has already been initialized. We need to grab a ContextLock
// during this since we may make callbacks during construction of a
// Context.
//
#if !USE_DYNAMICTLS
t_pContext = pContext; // SubContext's may need to grab the Context
#endif
{
ContextLock cl;
Verify(cl.LockNL(ContextLock::edDefer, pContext));
for (int idx = 0; idx < slCOUNT; idx++) {
ContextPackBuilder * pBuilder = ContextPackBuilder::GetBuilder((Context::ESlot) idx);
AssertMsg(pBuilder != NULL, "Builder not initialized using INIT_SUBCONTEXT");
pContext->m_rgSCs[idx] = pBuilder->New(pContext);
if (pContext->m_rgSCs[idx] == NULL) {
hr = E_OUTOFMEMORY;
goto ErrorExit;
}
hr = pContext->m_rgSCs[idx]->Create(pInit);
if (FAILED(hr)) {
#if !USE_DYNAMICTLS
t_pContext = NULL;
#endif
goto ErrorExit;
}
}
}
AssertMsg(pContext != NULL, "Ensure Context is valid");
*ppctxNew = pContext;
return S_OK;
ErrorExit:
AssertMsg(FAILED(hr), "Must specify failure");
//
// Something went wrong while creating a new context, so need to tear it
// down.
//
// NOTE: We CAN NOT use xwUnlock() or xwDeleteHandle(), since these are
// intercepted and would go through the ResourceManager. Instead, we need
// bump down the ref count, pre-destroy the Context, and delete it.
//
if (pContext != NULL) {
VerifyMsg(--pContext->m_cRef == 0, "Should only have initial reference");
pContext->xwPreDestroyNL();
ProcessDelete(Context, pContext);
}
*ppctxNew = NULL;
return hr;
}
/***************************************************************************\
*
* Context::xwDeleteHandle
*
* xwDeleteHandle() is called from ::DeleteHandle() to destroy an object and
* free its associated resources. This function must be called on the same
* thread as originally created the Context so that the corresponding Thread
* object can also be destroyed.
*
\***************************************************************************/
BOOL
Context::xwDeleteHandle()
{
#if DBG
AssertMsg(IsInitThread(), "Thread must be initialized to destroy the Context");
Context * pctxThread = GetThread()->GetContext();
AssertMsg(pctxThread == this, "Thread currently running on should match the Context being destroyed");
#endif // DBG
#if DBG_CHECK_CALLBACKS
if (m_cLiveCallbacks > 0) {
AutoTrace("DirectUser Context: 0x%p\n", this);
AlwaysPromptInvalid("Can not DeleteHandle(Context) while inside a callback");
}
#endif
//
// We have NOT taken a ContextLock when calling DeleteHandle() on the
// Context. Therefore, we are actually an NL function, but the virtual
// function can't be renamed.
//
ResourceManager::xwNotifyThreadDestroyNL();
return FALSE;
}
/***************************************************************************\
*
* Context::AddCurrentThread
*
* AddCurrentThread() sets the current thread to use the specified Context.
*
* NOTE: This function is designed to be called from the ResourceManager
* and should not normally be called directly.
*
\***************************************************************************/
void
Context::AddCurrentThread()
{
#if USE_DYNAMICTLS
AssertMsg(!IsInitContext(), "Ensure Context is not already set");
#else
AssertMsg(t_pContext == NULL, "Ensure Context is not already set");
#endif
GetThread()->SetContext(this);
}
/***************************************************************************\
*
* Context::xwOnIdleNL
*
* xwOnIdleNL() cycles through all of the SubContext's, giving each an
* opportunity to perform any idle-time processing. This is time when there
* are no more messages to process. Each SubContext can also return a
* "delay" count that specifies how long it will have before more processing.
*
\***************************************************************************/
DWORD
Context::xwOnIdleNL()
{
DWORD dwTimeOut = INFINITE;
AssertMsg(dwTimeOut == 0xFFFFFFFF, "Ensure largest delay");
for (int idx = 0; idx < slCOUNT; idx++) {
DWORD dwNewTimeOut = m_rgSCs[idx]->xwOnIdleNL();
if (dwNewTimeOut < dwTimeOut) {
dwTimeOut = dwNewTimeOut;
}
}
return dwTimeOut;
}
#if DBG
//------------------------------------------------------------------------------
void
Context::DEBUG_AssertValid() const
{
if (IsOrphanedNL()) {
PromptInvalid("Illegally using an orphaned Context");
AssertMsg(0, "API layer let an Orphaned Context in");
}
if (m_DEBUG_tidLock != 0) {
Assert(m_DEBUG_pthrLock != NULL);
}
Assert(m_pHeap != NULL);
for (int idx = 0; idx < slCOUNT; idx++) {
AssertInstance(m_rgSCs[idx]);
}
}
#endif
/***************************************************************************\
*****************************************************************************
*
* class ContextPackBuilder
*
*****************************************************************************
\***************************************************************************/
PREINIT_SUBCONTEXT(CoreSC);
PREINIT_SUBCONTEXT(MotionSC);
ContextPackBuilder * ContextPackBuilder::s_rgBuilders[Context::slCOUNT] =
{
INIT_SUBCONTEXT(CoreSC),
INIT_SUBCONTEXT(MotionSC),
};
/***************************************************************************\
*****************************************************************************
*
* class SubContext
*
*****************************************************************************
\***************************************************************************/
#if DBG
//------------------------------------------------------------------------------
void
SubContext::DEBUG_AssertValid() const
{
// Don't use AssertInstance since it would be recursive.
Assert(m_pParent != NULL);
}
#endif
/***************************************************************************\
*****************************************************************************
*
* class ContextLock
*
*****************************************************************************
\***************************************************************************/
//------------------------------------------------------------------------------
BOOL
ContextLock::LockNL(ContextLock::EnableDefer ed, Context * pctxThread)
{
AssertMsg(pctx == NULL, "Can only Lock() once");
AssertMsg(pctxThread != NULL, "Must specify a valid Context to lock");
//
// Check if the Context has been orphaned __before__ entering the lock so
// that we access as few members as possible.
//
if (pctxThread->IsOrphanedNL()) {
PromptInvalid("Illegally using an orphaned Context");
return FALSE;
}
pctx = pctxThread;
pctx->Enter();
pctx->EnableDefer(ed, &fOldDeferred);
return TRUE;
}