482 lines
14 KiB
C++
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;
|
|
}
|
|
|