|
|
/*===================================================================
Microsoft Denali
Microsoft Confidential. Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Session Object Manager
File: Sessmgr.cpp
Owner: PramodD
This is the Session Manager source file. ===================================================================*/ #include "denpre.h"
#pragma hdrstop
#include "idgener.h"
#include "perfdata.h"
#include "randgen.h"
// ATQ Scheduler
#include "issched.hxx"
#include "MemChk.h"
#pragma warning (disable: 4355) // ignore: "'this' used in base member init
/*===================================================================
G l o b a l s ===================================================================*/
PTRACE_LOG CSession::gm_pTraceLog = NULL; unsigned long g_nSessions = 0; CIdGenerator g_SessionIdGenerator; CIdGenerator g_ExposedSessionIdGenerator; LONG g_nSessionObjectsActive = 0;
// On app restart post session cleanup requests so many at a time
#define SUGGESTED_SESSION_CLEANUP_REQUESTS_MAX 500
/*===================================================================
C S e s s i o n V a r i a n t s ===================================================================*/
/*===================================================================
CSessionVariants::CSessionVariants
Constructor
Parameters:
Returns: ===================================================================*/ CSessionVariants::CSessionVariants() : m_cRefs(1), m_pSession(NULL), m_ctColType(ctUnknown), m_ISupportErrImp(this, this, IID_IVariantDictionary) { CDispatch::Init(IID_IVariantDictionary); }
/*===================================================================
CSessionVariants::~CSessionVariants
Destructor
Parameters:
Returns: ===================================================================*/ CSessionVariants::~CSessionVariants() { Assert(!m_pSession); } /*===================================================================
CSessionVariants::Init
Initialize object
Parameters: pSession Session ctColType Type of variables to expose in the collection e.g. Tagged objects or Properties
Returns: HRESULT ===================================================================*/ HRESULT CSessionVariants::Init ( CSession *pSession, CompType ctColType ) { Assert(pSession); pSession->AddRef();
Assert(!m_pSession);
m_pSession = pSession; m_ctColType = ctColType; return S_OK; }
/*===================================================================
CSessionVariants::UnInit
UnInitialize object
Parameters:
Returns: HRESULT ===================================================================*/ HRESULT CSessionVariants::UnInit() { if (m_pSession) { m_pSession->Release(); m_pSession = NULL; } return S_OK; } /*===================================================================
CSessionVariants::QueryInterface CSessionVariants::AddRef CSessionVariants::Release
IUnknown members for CSessionVariables object. ===================================================================*/ STDMETHODIMP CSessionVariants::QueryInterface ( REFIID iid, void **ppvObj ) { if (iid == IID_IUnknown || iid == IID_IDispatch || iid == IID_IVariantDictionary) { AddRef(); *ppvObj = this; return S_OK; } else if (iid == IID_ISupportErrorInfo) { m_ISupportErrImp.AddRef(); *ppvObj = &m_ISupportErrImp; return S_OK; } *ppvObj = NULL; return E_NOINTERFACE; }
STDMETHODIMP_(ULONG) CSessionVariants::AddRef() { return InterlockedIncrement((LPLONG)&m_cRefs); }
STDMETHODIMP_(ULONG) CSessionVariants::Release() { if (InterlockedDecrement((LPLONG)&m_cRefs) > 0) return m_cRefs;
delete this; return 0; }
/*===================================================================
CSessionVariants::ObjectNameFromVariant
Gets name from variant. Resolves operations by index. Allocates memory for name.
Parameters: vKey VARIANT ppwszName [out] allocated name fVerify flag - check existance if named
Returns: HRESULT ===================================================================*/ HRESULT CSessionVariants::ObjectNameFromVariant ( VARIANT &vKey, WCHAR **ppwszName, BOOL fVerify ) { *ppwszName = NULL; VARIANT *pvarKey = &vKey; VARIANT varKeyCopy; VariantInit(&varKeyCopy); if (V_VT(pvarKey) != VT_BSTR && V_VT(pvarKey) != VT_I2 && V_VT(pvarKey) != VT_I4) { if (FAILED(VariantResolveDispatch(&varKeyCopy, &vKey, IID_IVariantDictionary, IDE_SESSION))) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_EXPECTING_STR); VariantClear(&varKeyCopy); return E_FAIL; } pvarKey = &varKeyCopy; }
LPWSTR pwszName = NULL;
switch (V_VT(pvarKey)) { case VT_BSTR: { pwszName = V_BSTR(pvarKey);
if (fVerify && pwszName) { CComponentObject *pObj = NULL;
Assert(m_pSession); Assert(m_pSession->PCompCol());
if (m_ctColType == ctTagged) m_pSession->PCompCol()->GetTagged(pwszName, &pObj); else m_pSession->PCompCol()->GetProperty(pwszName, &pObj);
if (!pObj || pObj->GetType() != m_ctColType) pwszName = NULL; // as if not found
} break; }
case VT_I1: case VT_I2: case VT_I8: case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8: case VT_R4: case VT_R8: // Coerce all integral types to VT_I4
if (FAILED(VariantChangeType(pvarKey, pvarKey, 0, VT_I4))) return E_FAIL;
// fallthru to VT_I4
case VT_I4: { int i; HRESULT hr;
// Look up the object by index
i = V_I4(pvarKey);
if (i > 0) { Assert(m_pSession); Assert(m_pSession->PCompCol()); hr = m_pSession->PCompCol()->GetNameByIndex ( m_ctColType, i, &pwszName );
if (FAILED(hr)) return DISP_E_BADINDEX; } else { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_BAD_ARRAY_INDEX); return E_FAIL; } break; } } VariantClear(&varKeyCopy);
if (!pwszName) return S_OK;
// Copy name
*ppwszName = StringDupW(pwszName);
return S_OK; }
/*===================================================================
CSessionVariants::get_Item
Function called from DispInvoke to get values from the SessionVariables collection.
Parameters: vKey VARIANT [in], which parameter to get the value of - integers access collection as an array pvarReturn VARIANT *, [out] value of the requested parameter
Returns: S_OK on success, E_FAIL on failure. ===================================================================*/ HRESULT CSessionVariants::get_Item ( VARIANT varKey, VARIANT *pVar ) { if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL; // Initialize return value
VariantInit(pVar);
if (!m_pSession->PHitObj() || !m_pSession->PCompCol()) return S_OK; // return empty variant
CHitObj *pHitObj = m_pSession->PHitObj();
// Get name
WCHAR *pwszName = NULL; HRESULT hr = ObjectNameFromVariant(varKey, &pwszName); if (!pwszName) return S_OK; // bogus index - no error
// Find object by name
CComponentObject *pObj = NULL;
if (m_ctColType == ctTagged) { pHitObj->GetComponent(csSession, pwszName, CbWStr(pwszName), &pObj); if (pObj && (pObj->GetType() != ctTagged)) pObj = NULL; } else { pHitObj->GetPropertyComponent(csSession, pwszName, &pObj); }
free(pwszName);
if (!pObj) return S_OK;
// return the variant
return pObj->GetVariant(pVar); }
/*===================================================================
CSessionVariants::put_Item
IVariantsDictionary implementation.
Implement property put by dereferencing variants before calling putref.
Parameters: VARIANT varKey Name of the variable to set VARIANT Var Value/object to set for the variable
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSessionVariants::put_Item ( VARIANT varKey, VARIANT Var ) { if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL;
if (m_ctColType == ctTagged) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_CANT_MOD_STATICOBJECTS); return E_FAIL; }
if (!m_pSession->PHitObj()) return E_FAIL;
Assert(m_ctColType == ctProperty);
// Resolve the variant
VARIANT varResolved; HRESULT hr = VariantResolveDispatch ( &varResolved, &Var, IID_ISessionObject, IDE_SESSION ); if (FAILED(hr)) return hr; // exception already raised
// Get name
WCHAR *pwszName = NULL; hr = ObjectNameFromVariant(varKey, &pwszName); if (!pwszName) return hr;
hr = m_pSession->PHitObj()->SetPropertyComponent ( csSession, pwszName, &varResolved );
free(pwszName); VariantClear(&varResolved); return hr; }
/*===================================================================
CSessionVariants::putref_Item
IVariantsDictionary implementation.
Implement property put be reference.
Parameters: VARIANT varKey Name of the variable to set VARIANT Var Value/object to set for the variable
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSessionVariants::putref_Item ( VARIANT varKey, VARIANT Var ) { if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL;
if (m_ctColType == ctTagged) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_CANT_MOD_STATICOBJECTS); return E_FAIL; }
if (FIsIntrinsic(&Var)) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_SESSION_CANT_STORE_INTRINSIC); return E_FAIL; }
if (!m_pSession->PHitObj()) return E_FAIL;
Assert(m_ctColType == ctProperty);
// Get name
WCHAR *pwszName = NULL; HRESULT hr = ObjectNameFromVariant(varKey, &pwszName); if (!pwszName) return hr;
hr = m_pSession->PHitObj()->SetPropertyComponent ( csSession, pwszName, &Var );
free(pwszName); return hr; }
/*===================================================================
CSessionVariants::get_Key
Function called from DispInvoke to get Keys from the SessionVariables collection.
Parameters: vKey VARIANT [in], which parameter to get the value of - integers access collection as an array pvarReturn VARIANT *, [out] value of the requested parameter
Returns: S_OK on success, E_FAIL on failure. ===================================================================*/ HRESULT CSessionVariants::get_Key ( VARIANT varKey, VARIANT *pVar ) { if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL; VariantInit(pVar);
if (!m_pSession->PHitObj() || !m_pSession->PCompCol()) return S_OK;
// Get name
WCHAR *pwszName = NULL; HRESULT hr = ObjectNameFromVariant(varKey, &pwszName, TRUE); if (!pwszName) return S_OK; // no error if bogus index
// Return BSTr
BSTR bstrT = SysAllocString(pwszName); free(pwszName); if (!bstrT) return E_OUTOFMEMORY; V_VT(pVar) = VT_BSTR; V_BSTR(pVar) = bstrT; return S_OK; }
/*===================================================================
CSessionVariants::get_Count
Parameters: pcValues - count is stored in *pcValues ===================================================================*/ STDMETHODIMP CSessionVariants::get_Count ( int *pcValues ) { *pcValues = 0;
if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL;
if (m_pSession->PCompCol()) { if (m_ctColType == ctTagged) *pcValues = m_pSession->PCompCol()->GetTaggedObjectCount(); else *pcValues = m_pSession->PCompCol()->GetPropertyCount(); } return S_OK; }
/*===================================================================
CSessionVariants::get__NewEnum
Return a new enumerator ===================================================================*/ HRESULT CSessionVariants::get__NewEnum ( IUnknown **ppEnumReturn ) { *ppEnumReturn = NULL;
if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL; CVariantsIterator *pIterator = new CVariantsIterator ( m_pSession, m_ctColType ); if (pIterator == NULL) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_OOM); return E_OUTOFMEMORY; }
*ppEnumReturn = pIterator; return S_OK; }
/*===================================================================
CSessionVariants::Remove
Remove item from the collection
Parameters: varKey VARIANT [in]
Returns: S_OK on success, E_FAIL on failure. ===================================================================*/ STDMETHODIMP CSessionVariants::Remove ( VARIANT varKey ) { if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL;
if (m_ctColType == ctTagged) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_CANT_MOD_STATICOBJECTS); return E_FAIL; }
Assert(m_ctColType == ctProperty);
// Get name
WCHAR *pwszName = NULL; ObjectNameFromVariant(varKey, &pwszName); if (!pwszName) return S_OK;
CComponentCollection *pCompCol = m_pSession->PCompCol();
if (pCompCol) pCompCol->RemoveProperty(pwszName); free(pwszName); return S_OK; }
/*===================================================================
CSessionVariants::RemoveAll
Remove all items from the collection
Parameters:
Returns: S_OK on success, E_FAIL on failure. ===================================================================*/ STDMETHODIMP CSessionVariants::RemoveAll() { if (!m_pSession || FAILED(m_pSession->CheckForTombstone())) return E_FAIL;
if (m_ctColType == ctTagged) { ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_CANT_MOD_STATICOBJECTS); return E_FAIL; }
Assert(m_ctColType == ctProperty);
CComponentCollection *pCompCol = m_pSession->PCompCol();
if (pCompCol) { pCompCol->RemoveAllProperties(); } return S_OK; }
/*===================================================================
C S e s s i o n ===================================================================*/
/*===================================================================
CSession::CSession
Constructor
Parameters:
Returns: ===================================================================*/ CSession::CSession() : m_fInited(FALSE), m_fLightWeight(FALSE), m_fOnStartFailed(FALSE), m_fOnStartInvoked(FALSE), m_fOnEndPresent(FALSE), m_fTimedOut(FALSE), m_fStateAcquired(FALSE), m_fCustomTimeout(FALSE), m_fAbandoned(FALSE), m_fTombstone(FALSE), m_fInTOBucket(FALSE), m_fSessCompCol(FALSE), m_fCodePageSet(FALSE), m_fLCIDSet(FALSE), m_Request(static_cast<ISessionObject *>(this)), m_Response(static_cast<ISessionObject *>(this)), m_Server(static_cast<ISessionObject *>(this)), m_pAppln(NULL), m_pHitObj(NULL), m_pTaggedObjects(NULL), m_pProperties(NULL), m_Id(INVALID_ID, 0, 0), m_dwExternId(INVALID_ID), m_cRefs(1), m_cRequests(0), m_dwmTimeoutTime(0), m_nTimeout(0), #ifndef PERF_DISABLE
m_dwtInitTimestamp(0), #endif
m_lCodePage(0), m_lcid(LOCALE_SYSTEM_DEFAULT), m_fSecureSession(FALSE) { m_lCodePage = GetACP(); m_ISuppErrImp.Init(static_cast<ISessionObject *>(this), static_cast<ISessionObject *>(this), IID_ISessionObject); CDispatch::Init(IID_ISessionObject);
InterlockedIncrement(&g_nSessionObjectsActive);
IF_DEBUG(SESSION) WriteRefTraceLog(gm_pTraceLog, m_cRefs, this); }
/*===================================================================
CSession::~CSession
Destructor
Parameters:
Returns: ===================================================================*/ CSession::~CSession() { Assert(m_fTombstone); // must be tombstoned before destructor
Assert(m_cRefs == 0); // must have 0 ref count
InterlockedDecrement(&g_nSessionObjectsActive);
}
/*===================================================================
CSession::Init
Initialize CSession object
Parameters: pAppln session's application to remember Id session id
Returns: HRESULT ===================================================================*/ HRESULT CSession::Init ( CAppln *pAppln, const CSessionId &Id ) { // Update global sessions counter
InterlockedIncrement((LPLONG)&g_nSessions); #ifndef PERF_DISABLE
g_PerfData.Incr_SESSIONCURRENT(); g_PerfData.Incr_SESSIONSTOTAL(); m_dwtInitTimestamp = GetTickCount(); #endif
// Setup the object
HRESULT hr = S_OK;
m_pAppln = pAppln; m_Id = Id; m_dwExternId = g_ExposedSessionIdGenerator.NewId(); // Update application's session count
m_pAppln->IncrementSessionCount();
// default to system's ansi code page
m_lCodePage = pAppln->QueryAppConfig()->uCodePage(); m_lcid = pAppln->QueryAppConfig()->uLCID();
// default session timeout
m_nTimeout = pAppln->QueryAppConfig()->dwSessionTimeout();
// initialize Viper activity
if (SUCCEEDED(hr)) hr = m_Activity.Init(pAppln->PServicesConfig());
// mark as Inited and update timestamp
if (SUCCEEDED(hr)) m_fInited = TRUE;
return hr; } /*===================================================================
CSession::UnInit
UnInitialize CSession object. Convert to tombstone state.
Parameters:
Returns: HRESULT (S_OK) ===================================================================*/ HRESULT CSession::UnInit() { Assert(!m_fTombstone); // don't do it twice
// Remove from timeout bucket if any
if (m_fInTOBucket) m_pAppln->PSessionMgr()->RemoveSessionFromTOBucket(this);
// Cleanup the object
RemoveComponentCollection();
// Get rid of the intrinsics
m_Request.UnInit(); m_Response.UnInit(); m_Server.UnInit(); // Get rid of Viper activity
m_Activity.UnInit();
// Update global counters
#ifndef PERF_DISABLE
if (m_fTimedOut) g_PerfData.Incr_SESSIONTIMEOUT(); g_PerfData.Decr_SESSIONCURRENT(); DWORD dwt = GetTickCount(); if (dwt >= m_dwtInitTimestamp) dwt = dwt - m_dwtInitTimestamp; else dwt = (DWT_MAX - m_dwtInitTimestamp) + dwt; g_PerfData.Set_SESSIONLIFETIME(dwt); #endif
m_pAppln->DecrementSessionCount(); InterlockedDecrement((LPLONG)&g_nSessions);
m_pAppln = NULL; m_pHitObj = NULL;
// Mark this session as Tombstone
m_fTombstone = TRUE;
// Disconnect proxies NOW (in case we are in shutdown, or enter shutdown later & a proxy has a ref.)
CoDisconnectObject(static_cast<ISessionObject *>(this), 0);
return S_OK; } /*===================================================================
CSession::MakeLightWeight
Convert to 'light-weight' state if possible
Parameters:
Returns: HRESULT ===================================================================*/ HRESULT CSession::MakeLightWeight() { Assert(m_fInited); if (m_fLightWeight) return S_OK; if (m_cRequests > 1) // requests pending for this session?
return S_OK;
if (m_fSessCompCol && !m_SessCompCol.FHasStateInfo()) { // don't remove component collection from under enumerators
if (!m_pTaggedObjects && !m_pProperties) RemoveComponentCollection(); }
m_fLightWeight = TRUE; return S_OK; }
/*===================================================================
CSession::CreateComponentCollection
Create and Init Session's component collection.
The actual object is aggregated by the session. Its state is controlled be m_fSessCompCol flag.
Parameters:
Returns: HRESULT ===================================================================*/ HRESULT CSession::CreateComponentCollection() { Assert(!m_fSessCompCol);
HRESULT hr = m_SessCompCol.Init(csSession);
if (SUCCEEDED(hr)) { m_fSessCompCol = TRUE; } else { RemoveComponentCollection(); } return hr; }
/*===================================================================
CSession::RemoveComponentCollection
Remove Session's component collection
The actual object is aggregated by the session. Its state is controlled be m_fSessCompCol flag.
Parameters:
Returns: HRESULT ===================================================================*/ HRESULT CSession::RemoveComponentCollection() { if (m_pTaggedObjects) { m_pTaggedObjects->UnInit(); m_pTaggedObjects->Release(); m_pTaggedObjects = NULL; }
if (m_pProperties) { m_pProperties->UnInit(); m_pProperties->Release(); m_pProperties = NULL; } if (m_fSessCompCol) { m_SessCompCol.UnInit(); m_fSessCompCol = FALSE; }
return S_OK; }
/*===================================================================
CSession::FShouldBeDeletedNow
Tests if the session should be deleted
Parameters: BOOL fAtEndOfRequest TRUE if at the end of a request
Returns: BOOL TRUE (should be deleted) or FALSE (shouldn't) ===================================================================*/ BOOL CSession::FShouldBeDeletedNow ( BOOL fAtEndOfRequest ) { if (fAtEndOfRequest) { // Any OTHER requests pending -> don't delete
if (m_cRequests > 1) return FALSE; } else { // Any requests pending -> don't delete
if (m_cRequests > 0) return FALSE; }
// GLOBAL.ASA changed - delete
if (m_pAppln->FGlobalChanged()) return TRUE;
// Failed to start or abandoned - delete
if (m_fOnStartFailed || m_fAbandoned) return TRUE;
// Is stateless session? No need for Session_OnEnd?
if (!m_fSessCompCol && // CompCol gone in MakeLightWeight()
!m_fStateAcquired && // no other properties set
!m_fOnStartInvoked && // on start was never invoked
!m_fOnEndPresent) // on end is not present
return TRUE; // -> delete this session
// don't check timeout here
return FALSE; }
/*===================================================================
CSession::QueryInterface
QueryInterface() -- IUnknown implementation.
Parameters: REFIID riid void **ppv Returns: HRESULT ===================================================================*/ STDMETHODIMP CSession::QueryInterface ( REFIID riid, void **ppv ) { *ppv = NULL;
if (IID_IUnknown == riid || IID_IDispatch == riid || IID_ISessionObject == riid || IID_IDenaliIntrinsic == riid) { *ppv = static_cast<ISessionObject *>(this); ((IUnknown *)*ppv)->AddRef(); return S_OK; } else if (IID_ISupportErrorInfo == riid) { m_ISuppErrImp.AddRef(); *ppv = &m_ISuppErrImp; return S_OK; } else if (IID_IMarshal == riid) { *ppv = static_cast<IMarshal *>(this); ((IUnknown *)*ppv)->AddRef(); return S_OK; } else { return E_NOINTERFACE; } }
/*===================================================================
CSession::AddRef
AddRef() -- IUnknown implementation.
Parameters: Returns: Ref count ===================================================================*/ STDMETHODIMP_(ULONG) CSession::AddRef() { DWORD cRefs = InterlockedIncrement((LPLONG)&m_cRefs);
IF_DEBUG(SESSION) WriteRefTraceLog(gm_pTraceLog, m_cRefs, this);
return cRefs; }
/*===================================================================
CSession::Release
Release() -- IUnknown implementation.
Parameters: Returns: Ref count ===================================================================*/ STDMETHODIMP_(ULONG) CSession::Release() { DWORD cRefs = InterlockedDecrement((LPLONG)&m_cRefs);
IF_DEBUG(SESSION) WriteRefTraceLog(gm_pTraceLog, m_cRefs, this);
if (cRefs) return cRefs;
delete this; return 0; }
/*===================================================================
CSession::CheckForTombstone
Tombstone stub for ISessionObject methods. If the object is tombstone, does ExceptionId and fails.
Parameters: Returns: HRESULT E_FAIL if Tombstone S_OK if not ===================================================================*/ HRESULT CSession::CheckForTombstone() { if (!m_fTombstone) return S_OK; ExceptionId ( IID_ISessionObject, IDE_SESSION, IDE_INTRINSIC_OUT_OF_SCOPE ); return E_FAIL; }
/*===================================================================
CSession::get_SessionID
ISessionObject implementation.
Return the session ID to the caller
Parameters: BSTR *pbstrRet [out] session id value Returns: HRESULT ===================================================================*/ STDMETHODIMP CSession::get_SessionID ( BSTR *pbstrRet ) { if (FAILED(CheckForTombstone())) return E_FAIL; HRESULT hr = S_OK;
wchar_t wszId[15]; _ultow(m_dwExternId, wszId, 10); *pbstrRet = SysAllocString(wszId); if (*pbstrRet == NULL) { ExceptionId ( IID_ISessionObject, IDE_SESSION_ID, IDE_SESSION_MAP_FAILED ); hr = E_FAIL; }
m_fStateAcquired = TRUE; return hr; } /*===================================================================
CSession::get_Timeout
ISessionObject implementation.
Return the default or user set timeout interval (in minutes)
Parameters: long *plVar [out] timeout value (in minutes) Returns: HRESULT ===================================================================*/ STDMETHODIMP CSession::get_Timeout ( long *plVar ) { if (FAILED(CheckForTombstone())) return E_FAIL; *plVar = m_nTimeout; return S_OK; } /*===================================================================
CSession::put_Timeout
ISessionObject implementation.
Allows the user to set the timeout interval (in minutes)
Parameters: long lVar timeout value (in minutes) Returns: HRESULT ===================================================================*/ STDMETHODIMP CSession::put_Timeout ( long lVar ) { if (FAILED(CheckForTombstone())) return E_FAIL; if (lVar < SESSION_TIMEOUT_MIN || lVar > SESSION_TIMEOUT_MAX) { ExceptionId ( IID_ISessionObject, IDE_SESSION_ID, IDE_SESSION_INVALID_TIMEOUT ); return E_FAIL; }
m_fStateAcquired = TRUE; m_fCustomTimeout = TRUE; m_nTimeout = lVar; return S_OK; }
/*===================================================================
CSession::get_CodePage
ISessionObject implementation.
Returns the current code page value for the request
Parameters: long *plVar [out] code page value
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::get_CodePage ( long *plVar ) { if (FAILED(CheckForTombstone())) return E_FAIL; Assert(m_pHitObj);
*plVar = m_lCodePage;
// If code page is 0, look up default ANSI code page
if (*plVar == 0) { *plVar = (long) GetACP(); } return S_OK; }
/*===================================================================
CSession::put_CodePage
ISessionObject implementation.
Sets the current code page value for the request
Parameters: long lVar code page to assign to this session
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::put_CodePage ( long lVar ) { if (FAILED(CheckForTombstone())) return E_FAIL; // set code page member variable
Assert(m_pHitObj); HRESULT hr = m_pHitObj->SetCodePage(lVar);
if (FAILED(hr)) { ExceptionId ( IID_ISessionObject, IDE_SESSION_ID, IDE_SESSION_INVALID_CODEPAGE ); return E_FAIL; }
m_fCodePageSet = TRUE;
m_lCodePage = lVar; // we need to preserve session since the user has set
// its code page member variable
m_fStateAcquired = TRUE; return S_OK; }
/*===================================================================
CSession::get_LCID
ISessionObject implementation.
Returns the current lcid value for the request
Parameters: long *plVar [out] code page value
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::get_LCID ( long *plVar ) { if (FAILED(CheckForTombstone())) return E_FAIL; Assert(m_pHitObj);
*plVar = m_lcid;
if (*plVar == LOCALE_SYSTEM_DEFAULT) { *plVar = GetSystemDefaultLCID(); } return S_OK; }
/*===================================================================
CSession::put_LCID
ISessionObject implementation.
Sets the current LCID value for the request
Parameters: long lVar LCID to assign to this session
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::put_LCID ( long lVar ) { if (FAILED(CheckForTombstone())) return E_FAIL; // set code page member variable
Assert(m_pHitObj); HRESULT hr = m_pHitObj->SetLCID(lVar);
if (FAILED(hr)) { ExceptionId ( IID_ISessionObject, IDE_SESSION_ID, IDE_TEMPLATE_BAD_LCID ); return E_FAIL; } m_fLCIDSet = TRUE; m_lcid = lVar;
// we need to preserve session since the user has set
// its lcid member variable
m_fStateAcquired = TRUE; return S_OK; }
/*===================================================================
CSession::get_Value
ISessionObject implementation.
Will allow the user to retreive a session state variable, the variable will come as a named pair, bstr is the name and var is the value or object to be returned for that name
Parameters: BSTR bstrName Name of the variable to get VARIANT *pVar Value/object to get for the variable
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::get_Value ( BSTR bstrName, VARIANT *pVar ) { if (FAILED(CheckForTombstone())) return E_FAIL;
if (bstrName == NULL) { ExceptionId(IID_ISessionObject, IDE_SESSION, IDE_EXPECTING_STR); return E_FAIL; }
VariantInit(pVar); // default variant empty
WCHAR *pwszName; STACK_BUFFER(rgbName, 42); WSTR_STACK_DUP(bstrName, &rgbName, &pwszName); if (pwszName == NULL) return S_OK; // no name - no value - no error
//_wcsupr(pwszName);
CComponentObject *pObj = NULL; HRESULT hr = S_OK;
Assert(m_pHitObj); m_pHitObj->AssertValid(); hr = m_pHitObj->GetPropertyComponent(csSession, pwszName, &pObj); if (SUCCEEDED(hr) && pObj) hr = pObj->GetVariant(pVar);
return S_OK; }
/*===================================================================
CSession::putref_Value
ISessionObject implementation.
Will allow the user to assign a session state variable to be saved the variable will come as a named pair, bstr is the name and var is the value or object to be stored for that name
Parameters: BSTR bstrName Name of the variable to set VARIANT Var Value/object to set for the variable
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::putref_Value ( BSTR bstrName, VARIANT Var ) { if (FAILED(CheckForTombstone())) return E_FAIL;
if (FIsIntrinsic(&Var)) { ExceptionId(IID_ISessionObject, IDE_SESSION, IDE_SESSION_CANT_STORE_INTRINSIC); return E_FAIL; }
if (bstrName == NULL) { ExceptionId(IID_ISessionObject, IDE_SESSION, IDE_EXPECTING_STR); return E_FAIL; } HRESULT hr;
Assert(m_pHitObj); m_pHitObj->AssertValid();
WCHAR *pwszName; STACK_BUFFER(rgbName, 42); WSTR_STACK_DUP(bstrName, &rgbName, &pwszName); if (pwszName == NULL) { ExceptionId ( IID_ISessionObject, IDE_SESSION, IDE_EXPECTING_STR ); return E_FAIL; } //_wcsupr(pwszName);
hr = m_pHitObj->SetPropertyComponent(csSession, pwszName, &Var);
return hr; }
/*===================================================================
CSession::put_Value
ISessionObject implementation.
Implement property put by dereferencing variants before calling putref.
Parameters: BSTR bstrName Name of the variable to set VARIANT Var Value/object to set for the variable
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::put_Value ( BSTR bstrName, VARIANT Var ) { if (FAILED(CheckForTombstone())) return E_FAIL; if (bstrName == NULL) { ExceptionId(IID_ISessionObject, IDE_SESSION, IDE_EXPECTING_STR); return E_FAIL; } HRESULT hr; VARIANT varResolved; hr = VariantResolveDispatch ( &varResolved, &Var, IID_ISessionObject, IDE_SESSION ); if (FAILED(hr)) return hr; // exception already raised
Assert(m_pHitObj); m_pHitObj->AssertValid();
WCHAR *pwszName; STACK_BUFFER(rgbName, 42); WSTR_STACK_DUP(bstrName, &rgbName, &pwszName); if (pwszName == NULL) { ExceptionId ( IID_ISessionObject, IDE_SESSION, IDE_EXPECTING_STR ); VariantClear( &varResolved ); return E_FAIL; } //_wcsupr(pwszName);
hr = m_pHitObj->SetPropertyComponent ( csSession, pwszName, &varResolved ); VariantClear( &varResolved ); return hr; }
/*===================================================================
CSession::Abandon
ISessionObject implementation.
Abandon reassignes session id to avoid hitting this session with incoming requests. Abandoned sessions get deleted ASAP.
Parameters: None
Returns: HRESULT S_OK on success ===================================================================*/ STDMETHODIMP CSession::Abandon() { if (FAILED(CheckForTombstone())) return E_FAIL; m_fAbandoned = TRUE;
// The new session logic allows only one session id per
// client need to disassociate session from client
// (good idea when abandoning anyway)
Assert(m_pHitObj);
// If execution Session_OnEnd (not a browser request), do nothing
if (!m_pHitObj->FIsBrowserRequest()) return S_OK; return m_pHitObj->ReassignAbandonedSession(); }
/*===================================================================
CSession::get_StaticObjects
Return the session static objects dictionary ===================================================================*/ STDMETHODIMP CSession::get_StaticObjects ( IVariantDictionary **ppDictReturn ) { if (FAILED(CheckForTombstone())) return E_FAIL;
if (!m_pTaggedObjects) { m_pTaggedObjects = new CSessionVariants; if (!m_pTaggedObjects) return E_OUTOFMEMORY; HRESULT hr = m_pTaggedObjects->Init(this, ctTagged); if (FAILED(hr)) { m_pTaggedObjects->UnInit(); m_pTaggedObjects->Release(); m_pTaggedObjects = NULL; } }
Assert(m_pTaggedObjects); return m_pTaggedObjects->QueryInterface(IID_IVariantDictionary, reinterpret_cast<void **>(ppDictReturn)); }
/*===================================================================
CSession::get_Contents
Return the session contents dictionary ===================================================================*/ STDMETHODIMP CSession::get_Contents ( IVariantDictionary **ppDictReturn ) { if (FAILED(CheckForTombstone())) return E_FAIL;
if (!m_pProperties) { m_pProperties = new CSessionVariants; if (!m_pProperties) return E_OUTOFMEMORY; HRESULT hr = m_pProperties->Init(this, ctProperty); if (FAILED(hr)) { m_pProperties->UnInit(); m_pProperties->Release(); m_pProperties = NULL; } } Assert(m_pProperties); return m_pProperties->QueryInterface(IID_IVariantDictionary, reinterpret_cast<void **>(ppDictReturn)); }
#ifdef DBG
/*===================================================================
CSession::AssertValid
Test to make sure that the CSession object is currently correctly formed and assert if it is not.
Returns: None
Side effects: None. ===================================================================*/ VOID CSession::AssertValid() const { Assert(m_fInited);
if (!m_fTombstone) Assert(m_pAppln); } #endif // DBG
/*===================================================================
C S e s s i o n M g r ===================================================================*/
/*===================================================================
CSessionMgr::CSessionMgr
CSessionMgr constructor.
Parameters: NONE
Returns: ===================================================================*/ CSessionMgr::CSessionMgr() : m_fInited(FALSE), m_pAppln(NULL), m_cSessionCleanupRequests(0), m_cTimeoutBuckets(0), m_rgolTOBuckets(NULL), m_idSessionKiller(0), m_dwmCurrentTime(0), m_dwtNextSessionKillerTime(0) { } /*===================================================================
CSessionMgr::~CSessionMgr
CSessionMgr destructor.
Parameters: NONE
Returns: ===================================================================*/ CSessionMgr::~CSessionMgr() { UnInit(); }
/*===================================================================
HRESULT CSessionMgr::Init
Initializes the Session Manager. Initializes hash tables. Schedules session killer.
Parameters: pAppln Application
Returns: HRESULT ===================================================================*/ HRESULT CSessionMgr::Init ( CAppln *pAppln ) { Assert(!m_fInited);
HRESULT hr; m_pAppln = pAppln;
// Master hash table
hr = m_htidMaster.Init ( SESSION_MASTERHASH_SIZE1_MAX, SESSION_MASTERHASH_SIZE2_MAX, SESSION_MASTERHASH_SIZE3_MAX ); if (FAILED(hr)) return hr;
// Number of timeout buckets = session timeout in minutes + 1
m_cTimeoutBuckets = m_pAppln->QueryAppConfig()->dwSessionTimeout() + 1; if (m_cTimeoutBuckets < SESSION_TIMEOUTBUCKETS_MIN) m_cTimeoutBuckets = SESSION_TIMEOUTBUCKETS_MIN; else if (m_cTimeoutBuckets > SESSION_TIMEOUTBUCKETS_MAX) m_cTimeoutBuckets = SESSION_TIMEOUTBUCKETS_MAX;
// Timeout buckets hash tables array
m_rgolTOBuckets = new CObjectListWithLock[m_cTimeoutBuckets]; if (!m_rgolTOBuckets) return E_OUTOFMEMORY;
// Each timeout bucket hash table
for (DWORD i = 0; i < m_cTimeoutBuckets; i++) { hr = m_rgolTOBuckets[i].Init ( OBJECT_LIST_ELEM_FIELD_OFFSET(CSession, m_TOBucketElem) ); if (FAILED(hr)) return hr; }
// Schedule session killer
hr = ScheduleSessionKiller(); if (FAILED(hr)) return hr;
// Start counting time
m_dwmCurrentTime = 0; // Remember the time of the next session killer
m_dwtNextSessionKillerTime = ::GetTickCount() + MSEC_ONE_MINUTE;
m_fInited = TRUE; return S_OK; }
/*===================================================================
HRESULT CSessionMgr::UnInit
UnInitializes the Session Manager.
Parameters:
Returns: S_OK ===================================================================*/ HRESULT CSessionMgr::UnInit( void ) { // Un-schedule session killer
if (m_idSessionKiller) UnScheduleSessionKiller();
// Timeout buckets
if (m_rgolTOBuckets) { for (DWORD i = 0; i < m_cTimeoutBuckets; i++) m_rgolTOBuckets[i].UnInit(); delete [] m_rgolTOBuckets; m_rgolTOBuckets = NULL; } m_cTimeoutBuckets = 0;
// Master hash
m_htidMaster.UnInit();
m_fInited = FALSE; return S_OK; }
/*===================================================================
CSessionMgr::ScheduleSessionKiller
Sets up the session killer workitem for ATQ scheduler
Parameters: Returns: HRESULT ===================================================================*/ HRESULT CSessionMgr::ScheduleSessionKiller() { Assert(!m_idSessionKiller); m_idSessionKiller = ScheduleWorkItem ( CSessionMgr::SessionKillerSchedulerCallback, // callback
this, // context
MSEC_ONE_MINUTE, // timeout
TRUE // periodic
); return m_idSessionKiller ? S_OK : E_FAIL; } /*===================================================================
CSessionMgr::UnScheduleSessionKiller
Removes the session killer workitem for ATQ scheduler
Parameters: Returns: S_OK ===================================================================*/ HRESULT CSessionMgr::UnScheduleSessionKiller() { if (m_idSessionKiller) { RemoveWorkItem(m_idSessionKiller); m_idSessionKiller = 0; } return S_OK; }
/*===================================================================
CSessionMgr::GenerateIdAndCookie
Generate new ID and cookie to be used create new session or reassign the session ID for an existing session.
Parameters: pId [out] ID pszNewCookie [out] Cookie (buf must be long enough)
Returns: S_OK ===================================================================*/ HRESULT CSessionMgr::GenerateIdAndCookie ( CSessionId *pId, char *pszNewCookie ) { pId->m_dwId = g_SessionIdGenerator.NewId(); GenerateRandomDwords(&pId->m_dwR1, 2); EncodeSessionIdCookie ( pId->m_dwId, pId->m_dwR1, pId->m_dwR2, pszNewCookie ); return S_OK; }
/*===================================================================
CSessionMgr::NewSession
Creates and Inits a new CSession object
Parameters: Id session id ppSession [out] session created
Returns: HRESULT ===================================================================*/ HRESULT CSessionMgr::NewSession ( const CSessionId &Id, CSession **ppSession ) { Assert(m_fInited); HRESULT hr = S_OK; CSession *pSession = new CSession; if (!pSession) hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr)) hr = pSession->Init(m_pAppln, Id);
if (SUCCEEDED(hr)) { Assert(pSession); *ppSession = pSession; } else { // failed - do cleanup
if (pSession) { pSession->UnInit(); pSession->Release(); } *ppSession = NULL; } return hr; }
/*===================================================================
CSessionMgr::ChangeSessionId
Reassigns different session Id to a session. Updates the master hash.
This method is called when abandoning a session to disassociate it from the client.
Parameters: pSession session to change id on Id new session id to assign
Returns: S_OK on success E_FAIL on failure ===================================================================*/ HRESULT CSessionMgr::ChangeSessionId ( CSession *pSession, const CSessionId &Id ) { HRESULT hr; // During request processing session's not supposed to be
// in any timeout bucket
Assert(!pSession->m_fInTOBucket);
LockMaster();
// Remove from master hash by Id
hr = RemoveFromMasterHash(pSession); if (SUCCEEDED(hr)) { // Assign new id
pSession->AssignNewId(Id);
// Reinsert into master hash by id
hr = AddToMasterHash(pSession); }
UnLockMaster(); return hr; }
/*===================================================================
CSessionMgr::FindInMasterHash
Finds Session in master hash session id. Doesn't Lock.
Parameters: Id session id to find **ppSession [out] session found Returns: S_OK good session found S_FALSE session not found or bad session found ===================================================================*/ HRESULT CSessionMgr::FindInMasterHash ( const CSessionId &Id, CSession **ppSession ) { Assert(m_fInited); // Find in the hash table
HRESULT hr = m_htidMaster.FindObject(Id.m_dwId, (void **)ppSession); if (hr != S_OK) { // Not found
*ppSession = NULL; return hr; }
// Session found, check if valid
if ((*ppSession)->m_fAbandoned || (*ppSession)->m_fTombstone || !(*ppSession)->FPassesIdSecurityCheck(Id.m_dwR1, Id.m_dwR2)) { // Bad session
hr = S_FALSE; }
return hr; }
/*===================================================================
CSessionMgr::AddSessionToTOBucket
Adds session to the correct timeout bucket. Locks the timeout bucket.
Parameters: pSession - session to add Returns: HRESULT ===================================================================*/ HRESULT CSessionMgr::AddSessionToTOBucket ( CSession *pSession ) { HRESULT hr; // Should not be already in a timeout bucket
Assert(!pSession->m_fInTOBucket);
DWORD iBucket = GetSessionTOBucket(pSession);
LockTOBucket(iBucket); hr = m_rgolTOBuckets[iBucket].AddObject(pSession); if (SUCCEEDED(hr)) pSession->m_fInTOBucket = TRUE; UnLockTOBucket(iBucket); return hr; } /*===================================================================
CSessionMgr::RemoveSessionToTOBucket
Removes from its timeout bucket if any. Locks the timeout bucket.
Parameters: pSession - session to remove fLock - lock bucket? (not needed during shutdown) Returns: HRESULT ===================================================================*/ HRESULT CSessionMgr::RemoveSessionFromTOBucket ( CSession *pSession, BOOL fLock ) { HRESULT hr; Assert(m_fInited); Assert(pSession->m_fInited);
if (!pSession->m_fInTOBucket) // not there - no error
return S_OK;
DWORD iBucket = GetSessionTOBucket(pSession);
if (fLock) LockTOBucket(iBucket);
if (pSession->m_fInTOBucket) // recheck after locking
hr = m_rgolTOBuckets[iBucket].RemoveObject(pSession); else hr = S_OK;
if (SUCCEEDED(hr)) pSession->m_fInTOBucket = FALSE;
if (fLock) UnLockTOBucket(iBucket); return hr; }
/*===================================================================
CSessionMgr::DeleteSession
Delete now or post for deletion. Should be called after the session is gone from the hash table
Parameters: CSession *pSession - session to release BOOL fInSessionActivity - TRUE when deleting HitObj's session at the end of request (no Async calls needed) Returns: HRESULT (S_OK) ===================================================================*/ HRESULT CSessionMgr::DeleteSession ( CSession *pSession, BOOL fInSessionActivity ) { Assert(pSession); pSession->AssertValid();
// Take care of DELETE NOW case
BOOL fDeleteNow = pSession->FCanDeleteWithoutExec();
// If called not from the session's activity and session
// has objects then need to switch to the right activity
// to remove the session level objects
if (!fInSessionActivity && pSession->FHasObjects()) fDeleteNow = FALSE; if (fDeleteNow) { pSession->UnInit(); pSession->Release(); return S_OK; }
// THE ASYNC DELETE LOGIC
HRESULT hr = S_OK;
// Make sure session object exists after AsyncCall
pSession->AddRef();
// Create new HitObj and init it for session delete
CHitObj *pHitObj = new CHitObj; if (pHitObj) { pHitObj->SessionCleanupInit(pSession);
if (fInSessionActivity) { // Already inside the correct activity no need to
// push the call through Viper
BOOL fRequestReposted = FALSE; pHitObj->ViperAsyncCallback(&fRequestReposted); Assert(!fRequestReposted); // this better not happen
delete pHitObj; } else { // Ask Viper to post the request
hr = pHitObj->PostViperAsyncCall(); if (FAILED(hr)) delete pHitObj; } } else { hr = E_OUTOFMEMORY; }
if (FAILED(hr)) { // Out of memery or Viper failed to post a request
// Force the issue inside TRY CATCH
// (not always safe to delete in the wrong thread)
TRY hr = pSession->UnInit(); CATCH(nExcept) pSession->m_fTombstone = TRUE; CoDisconnectObject(static_cast<ISessionObject *>(pSession), 0); hr = E_UNEXPECTED; END_TRY pSession->Release(); }
pSession->Release(); // Undo AddRef()
return S_OK; } /*===================================================================
CSessionMgr::DeleteExpiredSessions
Removes expired Sessions from a given timeout bucket
Parameters: iBucket timeout bucket #
Returns: HRESULT (S_OK) ===================================================================*/ HRESULT CSessionMgr::DeleteExpiredSessions ( DWORD iBucket ) { LockTOBucket(iBucket);
void *pvSession = m_rgolTOBuckets[iBucket].PFirstObject();
while (pvSession && !IsShutDownInProgress()) { CSession *pSession = reinterpret_cast<CSession *>(pvSession); pvSession = m_rgolTOBuckets[iBucket].PNextObject(pvSession);
if (pSession->GetRequestsCount() == 0) { BOOL fTimedOut = pSession->GetTimeoutTime() <= GetCurrentTime(); BOOL fRemovedFromMasterHash = FALSE; if (fTimedOut || pSession->FShouldBeDeletedNow(FALSE)) { LockMaster();
if (pSession->GetRequestsCount() == 0) // recheck after lock
{ RemoveFromMasterHash(pSession); fRemovedFromMasterHash = TRUE; } UnLockMaster(); }
if (fRemovedFromMasterHash) { if (fTimedOut) pSession->m_fTimedOut = TRUE;
// delete from timeout bucket
m_rgolTOBuckets[iBucket].RemoveObject(pSession); pSession->m_fInTOBucket = FALSE;
// delete session object itself (or schedule for deletion)
DeleteSession(pSession); } } }
UnLockTOBucket(iBucket); return S_OK; }
/*===================================================================
CSessionMgr::DeleteAllSessions
Application shutdown code.
Parameters: fForce flag - force delete?
Returns: HRESULT (S_OK) ===================================================================*/ HRESULT CSessionMgr::DeleteAllSessions ( BOOL fForce ) { // Remove session killer so that it wouldn't interfere
UnScheduleSessionKiller(); LockMaster();
HRESULT hr = m_htidMaster.IterateObjects ( DeleteAllSessionsCB, this, &fForce );
if (fForce) m_htidMaster.RemoveAllObjects(); UnLockMaster(); return hr; } /*===================================================================
CSessionMgr::DeleteAllSessionsCB
Static iterator callback. Removes Session regardless.
Parameters: pvSession session passed as void* pvSessionMgr session manager passed as void* pvfForce flag if TRUE - force the issue
Returns: IteratorCallbackCode ===================================================================*/ IteratorCallbackCode CSessionMgr::DeleteAllSessionsCB ( void *pvSession, void *pvSessionMgr, void *pvfForce ) { IteratorCallbackCode rc = iccContinue; CSession *pSession = reinterpret_cast<CSession *>(pvSession); CSessionMgr *pMgr = reinterpret_cast<CSessionMgr *>(pvSessionMgr); BOOL fForce = *(reinterpret_cast<BOOL *>(pvfForce));
// Try for 5 seconds to post delete for each session
for (int iT = 0; iT < 10; iT++) { if (pSession->GetRequestsCount() == 0) { if (fForce) { // When forcing delete and there are too many
// session cleanup requests quequed
// wait for the queue to drain
while (pMgr->GetNumSessionCleanupRequests() >= SUGGESTED_SESSION_CLEANUP_REQUESTS_MAX) Sleep(100); } else // if (!fForce)
{ // When not forcing delete (on application restart)
// don't queue too many sessions at one time
if (pMgr->GetNumSessionCleanupRequests() < SUGGESTED_SESSION_CLEANUP_REQUESTS_MAX) rc = iccRemoveAndContinue; else rc = iccRemoveAndStop; } if (pSession->FInTOBucket()) pMgr->RemoveSessionFromTOBucket(pSession, !fForce); pMgr->DeleteSession(pSession); break; }
if (!fForce) break; Sleep(500); }
return rc; }
/*===================================================================
CSessionMgr::SessionKillerSchedulerCallback
Static method implements ATQ scheduler callback functions. Replaces session killer thread
Parameters: void *pv context pointer (points to appl)
Returns:
Side effects: None. ===================================================================*/ void WINAPI CSessionMgr::SessionKillerSchedulerCallback ( void *pv ) { if (IsShutDownInProgress()) return;
Assert(pv); CSessionMgr *pMgr = reinterpret_cast<CSessionMgr *>(pv);
// Advance session killer time by 1 [minute]
pMgr->m_dwmCurrentTime++;
// Choose the bucket
DWORD iBucket = pMgr->m_dwmCurrentTime % pMgr->m_cTimeoutBuckets;
// Kill the sessions
pMgr->DeleteExpiredSessions(iBucket);
// Adjust the timeout to stay on the minute boundary
pMgr->m_dwtNextSessionKillerTime += MSEC_ONE_MINUTE;
// Calculate wait till next callback wakes up
DWORD dwtCur = ::GetTickCount(); DWORD dwtWait = 5000; // 5 sec if we are already late
// if (dwtCur < pMgr->m_dwtNextSessionKillerTime)
// {
dwtWait = pMgr->m_dwtNextSessionKillerTime - dwtCur; if (dwtWait > MSEC_ONE_MINUTE) dwtWait = MSEC_ONE_MINUTE; // in case of wrap-around
// }
ScheduleAdjustTime(pMgr->m_idSessionKiller, dwtWait); }
#ifdef DBG
/*===================================================================
CSessionMgr::AssertValid
Test to make sure that this is currently correctly formed and assert if it is not.
Returns: ===================================================================*/ void CSessionMgr::AssertValid() const { Assert(m_fInited); } #endif
|