/***************************************************************************\ * * 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. * * E_OUTOFMEMORY * E_NOTIMPL * 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; }