You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1997 lines
51 KiB
1997 lines
51 KiB
/*===================================================================
|
|
Microsoft Denali
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1997 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Component: Viper Integration Objects
|
|
|
|
File: viperint.cpp
|
|
|
|
Owner: DmitryR
|
|
|
|
This file contains the implementation of viper integration classes
|
|
===================================================================*/
|
|
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include "Context.h"
|
|
#include "package.h"
|
|
#include "memchk.h"
|
|
#include "denali.h"
|
|
#include "perfdata.h"
|
|
#include "etwtrace.hxx"
|
|
|
|
extern HDESK ghDesktop;
|
|
|
|
//
|
|
// COM holds the last reference to a CViperAsyncRequest
|
|
// we need to track these objects to ensure that we don't
|
|
// exit before the activity threads have released them.
|
|
//
|
|
|
|
volatile LONG g_nViperRequests = 0;
|
|
|
|
LONG g_nThreadsExecuting = 0;
|
|
|
|
CViperReqManager g_ViperReqMgr;
|
|
DWORD g_ReqDisconnected = 0;
|
|
|
|
BOOL g_fFirstMTAHit = TRUE;
|
|
BOOL g_fFirstSTAHit = TRUE;
|
|
|
|
extern CRITICAL_SECTION g_csFirstMTAHitLock;
|
|
extern CRITICAL_SECTION g_csFirstSTAHitLock;
|
|
|
|
#if REFTRACE_VIPER_REQUESTS
|
|
PTRACE_LOG CViperAsyncRequest::gm_pTraceLog=NULL;
|
|
#endif
|
|
|
|
|
|
/*===================================================================
|
|
C V i p e r A s y n c R e q u e s t
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::CViperAsyncRequest
|
|
|
|
CViperAsyncRequest constructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CViperAsyncRequest::CViperAsyncRequest()
|
|
: m_cRefs(1),
|
|
m_pHitObj(NULL),
|
|
m_hrOnError(S_OK),
|
|
m_pActivity(NULL),
|
|
m_fTestingConnection(0),
|
|
m_dwRepostAttempts(0),
|
|
m_fAsyncCallPosted(0)
|
|
{
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperAsyncRequest::CViperAsyncRequest (%p)\n", this));
|
|
#endif
|
|
|
|
#if REFTRACE_VIPER_REQUESTS
|
|
WriteRefTraceLog(gm_pTraceLog, m_cRefs, this);
|
|
#endif
|
|
|
|
InterlockedIncrement( (LONG *)&g_nViperRequests );
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::~CViperAsyncRequest
|
|
|
|
CViperAsyncRequest destructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CViperAsyncRequest::~CViperAsyncRequest()
|
|
{
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperAsyncRequest::~CViperAsyncRequest (%p)\n", this));
|
|
#endif
|
|
InterlockedDecrement( (LONG *)&g_nViperRequests );
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::Init
|
|
|
|
Initialize CViperAsyncRequest with CHitObj object
|
|
|
|
Parameters:
|
|
CHitObj *pHitObj Denali HitObj
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperAsyncRequest::Init
|
|
(
|
|
CHitObj *pHitObj,
|
|
IServiceActivity *pActivity
|
|
)
|
|
{
|
|
Assert(m_pHitObj == NULL);
|
|
|
|
m_pHitObj = pHitObj;
|
|
m_pActivity = pActivity;
|
|
m_fBrowserRequest = pHitObj->FIsBrowserRequest();
|
|
m_dwLastTestTimeStamp = GetTickCount();
|
|
|
|
// establish the timeout value for this request. It will be a factor
|
|
// of the configured QueueConnectionTestTime. In proc, it will be
|
|
// this value times 2, and out of proc it will be multiplied by 4.
|
|
// NOTE if the QueueConnectionTestTime is zero, we'll use hard
|
|
// constants 12 and 6 for oop and inproc, respectively.
|
|
|
|
DWORD dwQueueConnectionTestTime = pHitObj->PAppln()->QueryAppConfig()->dwQueueConnectionTestTime();
|
|
m_dwTimeout = dwQueueConnectionTestTime
|
|
? dwQueueConnectionTestTime * (g_fOOP ? 4 : 2)
|
|
: g_fOOP ? 12 : 6;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef DBG
|
|
/*===================================================================
|
|
CViperAsyncRequest::AssertValid
|
|
|
|
Test to make sure that this is currently correctly formed
|
|
and assert if it is not.
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
void CViperAsyncRequest::AssertValid() const
|
|
{
|
|
Assert(m_pHitObj);
|
|
Assert(m_cRefs > 0);
|
|
}
|
|
#endif
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::QueryInterface
|
|
|
|
Standard IUnknown method
|
|
|
|
Parameters:
|
|
REFIID iid
|
|
void **ppv
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CViperAsyncRequest::QueryInterface
|
|
(
|
|
REFIID iid,
|
|
void **ppv
|
|
)
|
|
{
|
|
if (iid == IID_IUnknown || iid == IID_IServiceCall) {
|
|
*ppv = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else if (iid == IID_IAsyncErrorNotify) {
|
|
*ppv = static_cast<IAsyncErrorNotify *>(this);
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::AddRef
|
|
|
|
Standard IUnknown method
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
Ref count
|
|
===================================================================*/
|
|
STDMETHODIMP_(ULONG) CViperAsyncRequest::AddRef()
|
|
{
|
|
|
|
LONG refs = InterlockedIncrement(&m_cRefs);
|
|
#if REFTRACE_VIPER_REQUESTS
|
|
WriteRefTraceLog(gm_pTraceLog, refs, this);
|
|
#endif
|
|
|
|
return refs;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::Release
|
|
|
|
Standard IUnknown method
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
Ref count
|
|
===================================================================*/
|
|
STDMETHODIMP_(ULONG) CViperAsyncRequest::Release()
|
|
{
|
|
LONG refs = InterlockedDecrement(&m_cRefs);
|
|
|
|
#if REFTRACE_VIPER_REQUESTS
|
|
WriteRefTraceLog(gm_pTraceLog, refs, this);
|
|
#endif
|
|
|
|
if (refs != 0)
|
|
return refs;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::OnCall
|
|
|
|
IMTSCall method implementation. This method is called by Viper
|
|
from the right thread when it's time to process a request
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CViperAsyncRequest::OnCall()
|
|
{
|
|
Assert(m_pHitObj);
|
|
CIsapiReqInfo *pIReq;
|
|
HCONN ConnId = 0;
|
|
|
|
InterlockedIncrement(&g_nThreadsExecuting);
|
|
|
|
// check to see if the request has been removed from the queue
|
|
// already. If so, get out of here quick.
|
|
|
|
if (g_ViperReqMgr.RemoveReqObj(this) == FALSE) {
|
|
Release();
|
|
InterlockedDecrement(&g_nThreadsExecuting);
|
|
return S_OK;
|
|
}
|
|
|
|
pIReq = m_pHitObj->PIReq();
|
|
|
|
BOOL fRequestReposted = FALSE;
|
|
|
|
// add an extra addref here to prevent the deletion of the
|
|
// hitobj deleting the CIsapiReqInfo for this request.
|
|
|
|
if (pIReq) {
|
|
pIReq->AddRef();
|
|
|
|
//
|
|
// Add an ETW trace event when the request is getting processed.
|
|
//
|
|
if (ETW_IS_TRACE_ON(ETW_LEVEL_CP) && g_pEtwTracer ) {
|
|
|
|
ConnId = pIReq->ECB()->ConnID;
|
|
|
|
g_pEtwTracer->EtwTraceEvent(&AspEventGuid,
|
|
EVENT_TRACE_TYPE_START,
|
|
&ConnId, sizeof(HCONN),
|
|
NULL, 0);
|
|
}
|
|
}
|
|
|
|
m_pHitObj->ViperAsyncCallback(&fRequestReposted);
|
|
|
|
// Make sure there always is DONE_WITH_SESSION
|
|
if (m_pHitObj->FIsBrowserRequest() && !fRequestReposted)
|
|
{
|
|
if (!m_pHitObj->FDoneWithSession())
|
|
m_pHitObj->ReportServerError(IDE_UNEXPECTED);
|
|
}
|
|
|
|
if (!fRequestReposted)
|
|
delete m_pHitObj; // don't delete if reposted
|
|
|
|
|
|
m_pHitObj = NULL; // set to NULL even if not deleted
|
|
Release(); // release this, Viper holds another ref
|
|
|
|
//
|
|
// Add an ETW event when we are done with the request
|
|
//
|
|
|
|
if (pIReq) {
|
|
if ( ETW_IS_TRACE_ON(ETW_LEVEL_CP) && g_pEtwTracer) {
|
|
|
|
g_pEtwTracer->EtwTraceEvent(&AspEventGuid,
|
|
ETW_TYPE_END,
|
|
&ConnId, sizeof(HCONN),
|
|
NULL, 0);
|
|
}
|
|
pIReq->Release();
|
|
}
|
|
|
|
InterlockedDecrement(&g_nThreadsExecuting);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperAsyncRequest::OnError
|
|
|
|
Called by COM+ when it is unable to dispatch the request properly
|
|
on the configured thread
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CViperAsyncRequest::OnError(HRESULT hrViperError)
|
|
{
|
|
Assert(m_pHitObj);
|
|
CIsapiReqInfo *pIReq;
|
|
HRESULT hr = S_OK;
|
|
|
|
// check to see if the request has been removed from the queue
|
|
// already. If so, get out of here quick.
|
|
|
|
if (g_ViperReqMgr.RemoveReqObj(this) == FALSE) {
|
|
Release();
|
|
return S_OK;
|
|
}
|
|
|
|
pIReq = m_pHitObj->PIReq();
|
|
|
|
if (pIReq)
|
|
pIReq->AddRef();
|
|
|
|
m_dwRepostAttempts++;
|
|
|
|
// attempt to repost the request if it hasn't errored out three
|
|
// times yet.
|
|
|
|
if (m_dwRepostAttempts <= 3) {
|
|
|
|
hr = m_pActivity->AsynchronousCall(this);
|
|
|
|
Assert(SUCCEEDED(hr));
|
|
}
|
|
|
|
// if it has errored out three times or the repost failed,
|
|
// pitch the request
|
|
|
|
if (FAILED(hr) || (m_dwRepostAttempts > 3)) {
|
|
|
|
// DONE_WITH_SESSION -- ServerSupportFunction
|
|
// does not need bracketing
|
|
if (m_pHitObj->FIsBrowserRequest())
|
|
m_pHitObj->ReportServerError(IDE_UNEXPECTED);
|
|
|
|
// We never called to process request, there should
|
|
// be no state and it's probably save to delete it
|
|
// outside of bracketing
|
|
|
|
delete m_pHitObj;
|
|
|
|
m_pHitObj = NULL; // set to NULL even if not deleted
|
|
Release(); // release this, Viper holds another ref
|
|
}
|
|
|
|
if (pIReq)
|
|
pIReq->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD CViperAsyncRequest::SecsSinceLastTest()
|
|
{
|
|
DWORD dwt = GetTickCount();
|
|
if (dwt >= m_dwLastTestTimeStamp)
|
|
return ((dwt - m_dwLastTestTimeStamp)/1000);
|
|
else
|
|
return (((0xffffffff - m_dwLastTestTimeStamp) + dwt)/1000);
|
|
}
|
|
|
|
/*===================================================================
|
|
C V i p e r A c t i v i t y
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CViperActivity::CViperActivity
|
|
|
|
CViperActivity constructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CViperActivity::CViperActivity()
|
|
: m_pActivity(NULL), m_cBind(0)
|
|
{
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::~CViperActivity
|
|
|
|
CViperActivity destructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CViperActivity::~CViperActivity()
|
|
{
|
|
UnInit();
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::Init
|
|
|
|
Create actual Viper activity using MTSCreateActivity()
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::Init(IUnknown *pServicesConfig)
|
|
{
|
|
Assert(!FInited());
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = CoCreateActivity(pServicesConfig, IID_IServiceActivity, (void **)&m_pActivity);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
m_cBind = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::InitClone
|
|
|
|
Clone Viper activity (AddRef() it)
|
|
|
|
Parameters:
|
|
CViperActivity *pActivity activity to clone from
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::InitClone
|
|
(
|
|
CViperActivity *pActivity
|
|
)
|
|
{
|
|
Assert(!FInited());
|
|
Assert(pActivity);
|
|
pActivity->AssertValid();
|
|
|
|
m_pActivity = pActivity->m_pActivity;
|
|
m_pActivity->AddRef();
|
|
|
|
m_cBind = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::UnInit
|
|
|
|
Release Viper activity
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::UnInit()
|
|
{
|
|
if (m_pActivity)
|
|
{
|
|
while (m_cBind > 1) // 1 is for inited flag
|
|
{
|
|
m_pActivity->UnbindFromThread();
|
|
m_cBind--;
|
|
}
|
|
|
|
m_pActivity->Release();
|
|
m_pActivity = NULL;
|
|
}
|
|
|
|
m_cBind = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::BindToThread
|
|
|
|
Bind Activity to current thread using IMTSActivity method
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::BindToThread()
|
|
{
|
|
Assert(FInited());
|
|
|
|
m_pActivity->BindToCurrentThread();
|
|
m_cBind++;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::UnBindFromThread
|
|
|
|
UnBind Activity from using IMTSActivity method
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::UnBindFromThread()
|
|
{
|
|
Assert(FInited());
|
|
Assert(m_cBind > 1);
|
|
|
|
m_pActivity->UnbindFromThread();
|
|
m_cBind--;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::PostAsyncRequest
|
|
|
|
Call HitObj Async.
|
|
Creates IMTSCCall object to do it.
|
|
|
|
Parameters:
|
|
CHitObj *pHitObj Denali's HitObj
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::PostAsyncRequest
|
|
(
|
|
CHitObj *pHitObj
|
|
)
|
|
{
|
|
AssertValid();
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CViperAsyncRequest *pViperCall = new CViperAsyncRequest;
|
|
if (!pViperCall)
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
hr = pViperCall->Init(pHitObj, m_pActivity);
|
|
|
|
|
|
// Revert to Self before exiting so that it will be in a consistent (impersonated) state
|
|
// when it leaves this function.
|
|
RevertToSelf();
|
|
|
|
// hr here can be either S_OK or E_OUTOFMEMORY both cases are handled in the last IF.
|
|
// if (FAILED(hr) && pViperCall). However, if Init ever returns an HR other than S_OK we
|
|
// will need to place a return right here.
|
|
if (FAILED(hr))
|
|
{
|
|
if (pViperCall)
|
|
pViperCall->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Make the locking of the light weight queue and the subsequent queuing to COM atomic
|
|
//
|
|
g_ViperReqMgr.LockQueue();
|
|
|
|
hr = g_ViperReqMgr.AddReqObj(pViperCall);
|
|
|
|
//
|
|
// Assume that the ViperAsyncCall will succeed and set it the POSTed flag,
|
|
// if it fails then cleanup. This avoids a race condition where the object is touched after we
|
|
// have posted to request to Viper and it has deleted the object.
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pViperCall->SetAsyncCallPosted();
|
|
hr = m_pActivity->AsynchronousCall(pViperCall);
|
|
}
|
|
|
|
//
|
|
// We can release the lock as the request has now been queued to COM
|
|
//
|
|
g_ViperReqMgr.UnlockQueue();
|
|
|
|
//
|
|
// If we added it to the queue and the async call failed we have to be sure
|
|
// that the watch thread did not remove the object off the light weight queue.
|
|
// If RemoveReqObj returns false the watch thread has done the needful.
|
|
//
|
|
if (FAILED(hr) && g_ViperReqMgr.RemoveReqObj(pViperCall))
|
|
{ // cleanup if AsyncCall failed
|
|
pViperCall->ClearAsyncCallPosted();
|
|
pViperCall->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperActivity::PostGlobalAsyncRequest
|
|
|
|
Static method.
|
|
Post async request without an activity.
|
|
Creates temporary activity
|
|
|
|
Parameters:
|
|
CHitObj *pHitObj Denali's HitObj
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CViperActivity::PostGlobalAsyncRequest
|
|
(
|
|
CHitObj *pHitObj
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CViperActivity *pTmpActivity = new CViperActivity;
|
|
if (!pTmpActivity)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pTmpActivity->Init(pHitObj->PAppln()->PServicesConfig());
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// remember this activity as HitObj's activity
|
|
// HitObj will get rid of it on its destructor
|
|
pHitObj->SetActivity(pTmpActivity);
|
|
|
|
hr = pTmpActivity->PostAsyncRequest(pHitObj);
|
|
|
|
pTmpActivity = NULL; // don't delete, HitObj will
|
|
}
|
|
|
|
if (pTmpActivity)
|
|
delete pTmpActivity;
|
|
|
|
return hr;
|
|
}
|
|
|
|
#ifdef DBG
|
|
/*===================================================================
|
|
CViperAsyncRequest::AssertValid
|
|
|
|
Test to make sure that this is currently correctly formed
|
|
and assert if it is not.
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
void CViperActivity::AssertValid() const
|
|
{
|
|
Assert(FInited());
|
|
Assert(m_pActivity);
|
|
}
|
|
#endif
|
|
|
|
#ifdef UNUSED
|
|
/*===================================================================
|
|
C V i p e r T h r e a d E v e n t s
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CViperThreadEvents::CViperThreadEvents
|
|
|
|
CViperThreadEvents constructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CViperThreadEvents::CViperThreadEvents()
|
|
: m_cRefs(1)
|
|
{
|
|
}
|
|
|
|
#ifdef DBG
|
|
/*===================================================================
|
|
CViperThreadEvents::AssertValid
|
|
|
|
Test to make sure that this is currently correctly formed
|
|
and assert if it is not.
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
void CViperThreadEvents::AssertValid() const
|
|
{
|
|
Assert(m_cRefs > 0);
|
|
Assert(ghDesktop != NULL);
|
|
}
|
|
#endif
|
|
|
|
/*===================================================================
|
|
CViperThreadEvents::QueryInterface
|
|
|
|
Standard IUnknown method
|
|
|
|
Parameters:
|
|
REFIID iid
|
|
void **ppv
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CViperThreadEvents::QueryInterface
|
|
(
|
|
REFIID iid,
|
|
void **ppv
|
|
)
|
|
{
|
|
if (iid == IID_IUnknown || iid == IID_IThreadEvents)
|
|
{
|
|
*ppv = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperThreadEvents::AddRef
|
|
|
|
Standard IUnknown method
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
Ref count
|
|
===================================================================*/
|
|
STDMETHODIMP_(ULONG) CViperThreadEvents::AddRef()
|
|
{
|
|
DWORD cRefs = InterlockedIncrement((LPLONG)&m_cRefs);
|
|
return cRefs;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperThreadEvents::Release
|
|
|
|
Standard IUnknown method
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
Ref count
|
|
===================================================================*/
|
|
STDMETHODIMP_(ULONG) CViperThreadEvents::Release()
|
|
{
|
|
DWORD cRefs = InterlockedDecrement((LPLONG)&m_cRefs);
|
|
if (cRefs)
|
|
return cRefs;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperThreadEvents::OnStartup
|
|
|
|
IThreadEvents method implementation. This method is called by Viper
|
|
whenever they start up a thread.
|
|
|
|
Parameters:
|
|
None
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CViperThreadEvents::OnStartup()
|
|
{
|
|
HRESULT hr;
|
|
|
|
AssertValid();
|
|
|
|
// Set the desktop for this thread
|
|
hr = SetDesktop();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperThreadEvents::OnShutdown
|
|
|
|
IThreadEvents method implementation. This method is called by Viper
|
|
whenever they shut down a thread.
|
|
|
|
Parameters:
|
|
None
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CViperThreadEvents::OnShutdown()
|
|
{
|
|
AssertValid();
|
|
|
|
return S_OK;
|
|
}
|
|
#endif //UNUSED
|
|
|
|
/*===================================================================
|
|
C V i p e r A c t i v i t y
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CViperReqManager::CViperReqManager
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
VOID
|
|
|
|
===================================================================*/
|
|
CViperReqManager::CViperReqManager()
|
|
{
|
|
m_dwReqObjs = 0;
|
|
m_fCsInited = FALSE;
|
|
m_fCsQueueInited = FALSE;
|
|
m_fShutdown = FALSE;
|
|
m_fDisabled = FALSE;
|
|
m_hThreadAlive = NULL;
|
|
m_hWakeUpEvent = NULL;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperReqManager::Init
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
===================================================================*/
|
|
HRESULT CViperReqManager::Init()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwRegValue;
|
|
|
|
if (m_fDisabled == TRUE)
|
|
return S_OK;
|
|
|
|
m_dwQueueMin = Glob(dwRequestQueueMax) / (g_fOOP ? 5 : 10);
|
|
m_dwLastAwakened = GetTickCount()/1000;
|
|
m_dwQueueAlwaysWakeupMin = (Glob(dwRequestQueueMax)*80)/100;
|
|
|
|
#if REFTRACE_VIPER_REQUESTS
|
|
CViperAsyncRequest::gm_pTraceLog = CreateRefTraceLog(10000, 0);
|
|
#endif
|
|
|
|
ErrInitCriticalSection(&m_csLock, hr);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
m_fCsInited = TRUE;
|
|
|
|
ErrInitCriticalSection(&m_csQueueLock, hr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_fCsQueueInited = TRUE;
|
|
|
|
m_hWakeUpEvent = IIS_CREATE_EVENT("CViperReqManager::m_hWakeUpEvent",
|
|
this,
|
|
TRUE, // flag for manual-reset event
|
|
FALSE); // flag for initial state
|
|
if (!m_hWakeUpEvent)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
m_hThreadAlive = CreateThread(NULL, 0, CViperReqManager::WatchThread, 0, 0, NULL);
|
|
|
|
if (!m_hThreadAlive) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) && m_hWakeUpEvent)
|
|
CloseHandle(m_hWakeUpEvent);
|
|
|
|
if (SUCCEEDED(g_AspRegistryParams.GetF5AttackValue(&dwRegValue)))
|
|
m_fDisabled = !dwRegValue;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperReqManager::UnInit
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
===================================================================*/
|
|
HRESULT CViperReqManager::UnInit()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_fDisabled == TRUE)
|
|
return S_OK;
|
|
|
|
// mark that we're in shutdown
|
|
|
|
m_fShutdown = TRUE;
|
|
|
|
#if REFTRACE_VIPER_REQUESTS
|
|
DestroyRefTraceLog(CViperAsyncRequest::gm_pTraceLog);
|
|
#endif
|
|
|
|
// if the watch thread is still alive, wake it up and wait for
|
|
// it to die off
|
|
|
|
if (m_hThreadAlive)
|
|
{
|
|
WakeUp(TRUE);
|
|
|
|
if (WaitForSingleObject (m_hThreadAlive, INFINITE) == WAIT_FAILED)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
CloseHandle (m_hThreadAlive);
|
|
|
|
m_hThreadAlive = NULL;
|
|
}
|
|
|
|
if (m_fCsInited)
|
|
{
|
|
DeleteCriticalSection(&m_csLock);
|
|
}
|
|
|
|
if (m_fCsQueueInited)
|
|
{
|
|
DeleteCriticalSection(&m_csQueueLock);
|
|
}
|
|
|
|
if (m_hWakeUpEvent)
|
|
CloseHandle(m_hWakeUpEvent);
|
|
|
|
m_hWakeUpEvent = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperReqManager::WatchThread
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
DWORD
|
|
|
|
This thread is awakened to search for queued requests that should
|
|
be tested to see if they still have connected clients on the other
|
|
end.
|
|
|
|
::GetNext() is the key routine called to access a queued request.
|
|
::GetNext() will determine if there are requests that should be
|
|
tested and return only those.
|
|
|
|
===================================================================*/
|
|
DWORD __stdcall CViperReqManager::WatchThread(VOID *pArg)
|
|
{
|
|
DWORD dwReqsToTest = Glob(dwRequestQueueMax)/10;
|
|
DWORD dwF5AttackThreshold = (Glob(dwRequestQueueMax)*90)/100;
|
|
|
|
// live in a do-while loop
|
|
|
|
do {
|
|
|
|
CViperAsyncRequest *pViperReq = NULL;
|
|
CViperAsyncRequest *pNextViperReq = NULL;
|
|
|
|
BOOL fTestForF5Attack = FALSE;
|
|
DWORD dwReqsDiscToCont = 0;
|
|
DWORD dwReqsDisconnected = 0;
|
|
DWORD dwReqsTested = 0;
|
|
|
|
// wait for single object
|
|
|
|
ResetEvent(g_ViperReqMgr.m_hWakeUpEvent);
|
|
|
|
// check for shutdown
|
|
|
|
if (g_ViperReqMgr.m_fShutdown)
|
|
goto LExit;
|
|
|
|
WaitForSingleObject(g_ViperReqMgr.m_hWakeUpEvent, INFINITE);
|
|
|
|
g_ViperReqMgr.m_dwLastAwakened = GetTickCount()/1000;
|
|
|
|
// check for shutdown
|
|
|
|
if (g_ViperReqMgr.m_fShutdown)
|
|
goto LExit;
|
|
|
|
// determine the type of cleanup we're going to perform
|
|
|
|
if (g_ViperReqMgr.m_dwReqObjs >= dwF5AttackThreshold) {
|
|
|
|
// testing for a possible F5Attack. This will cause this loop
|
|
// to test the most recently received requests to check for a large
|
|
// number of disconnected requests. We'll test each 10% of the queue
|
|
// to see if at least 75% of the requests are disconnected. If 75% or
|
|
// more are disconnected, then we'll test the next 10%. If less than
|
|
// 75%, then we aren't under an F5Attack and will stop testing for an
|
|
// F5Attack.
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::WatchThread - testing for F5Attack\r\n"));
|
|
#endif
|
|
fTestForF5Attack = TRUE;
|
|
dwReqsDiscToCont = (dwReqsToTest * 75)/100;
|
|
dwReqsDisconnected = 0;
|
|
dwReqsTested = 0;
|
|
}
|
|
else {
|
|
|
|
// not testing for F5Attack. We will test all requests that have
|
|
// been on the queue for the minimum amount of time - 5 seconds if
|
|
// inproc, else 10 seconds.
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::WatchThread - NOT testing for F5Attack\r\n"));
|
|
#endif
|
|
fTestForF5Attack = FALSE;
|
|
}
|
|
|
|
// get the first request that is a candidate for testing
|
|
|
|
pNextViperReq = g_ViperReqMgr.GetNext(NULL, fTestForF5Attack);
|
|
|
|
// process all the available
|
|
|
|
while (pViperReq = pNextViperReq) {
|
|
|
|
// AddRef here to make sure the ViperReq object doesn't go away
|
|
// on the STA thread underneath of us.
|
|
|
|
pViperReq->AddRef();
|
|
|
|
// check again for shutdown. GetNext() sets the testing
|
|
// connection flag
|
|
|
|
if (g_ViperReqMgr.m_fShutdown) {
|
|
pViperReq->ClearTestingConnection();
|
|
pViperReq->Release();
|
|
goto LExit;
|
|
}
|
|
|
|
// if testing for F5Attack, check to see if we've tested the first
|
|
// 10% of the requests on the list. If so, see if we've found that
|
|
// 75% of them were disconnected. If not, then break. We're not under
|
|
// attack. Otherwise, continue testing.
|
|
|
|
if (fTestForF5Attack && (dwReqsTested == dwReqsToTest)) {
|
|
|
|
if (dwReqsDisconnected < dwReqsDiscToCont) {
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::WatchThread - 75% of requests NOT disconnected. Stopping.\r\n"));
|
|
#endif
|
|
// didn't see 75%, bail
|
|
pViperReq->ClearTestingConnection();
|
|
pViperReq->Release();
|
|
break;
|
|
}
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::WatchThread - 75% of requests disconnected. Continuing.\r\n"));
|
|
#endif
|
|
// at least 75% were disconnected. Reset running counters
|
|
// and continue
|
|
|
|
dwReqsDisconnected = 0;
|
|
dwReqsTested = 0;
|
|
}
|
|
|
|
dwReqsTested++;
|
|
|
|
BOOL fConnected = TRUE;
|
|
|
|
//
|
|
// test the connection but do so only after we are sure it has been queued to COM
|
|
//
|
|
g_ViperReqMgr.LockQueue();
|
|
|
|
if (!pViperReq->PHitObj()->PIReq()->TestConnection(&fConnected))
|
|
fConnected = TRUE;
|
|
|
|
g_ViperReqMgr.UnlockQueue();
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::WatchThread. Tested - %S (%s) (%p,%p)\n",
|
|
pViperReq->PHitObj()->PSzCurrTemplateVirtPath(),
|
|
fConnected ? "TRUE" : "FALSE",
|
|
pViperReq,
|
|
pViperReq->PHitObj()));
|
|
#endif
|
|
// get the next request. This will leave two requests locked for
|
|
// testing. We have to get the next request here because we may
|
|
// remove it below.
|
|
|
|
pNextViperReq = g_ViperReqMgr.GetNext(pViperReq,fTestForF5Attack);
|
|
|
|
// now check fConnected
|
|
|
|
if (!fConnected) {
|
|
|
|
g_ReqDisconnected++;
|
|
dwReqsDisconnected++;
|
|
|
|
// not connected. Forceably remove the request from the queue.
|
|
// Passing TRUE ignores the fTestingConnection test.
|
|
|
|
DBG_REQUIRE(g_ViperReqMgr.RemoveReqObj(pViperReq,TRUE) == TRUE);
|
|
|
|
// do the stuff we do in CHitObj::RejectBrowserRequestWhenNeeded().
|
|
|
|
pViperReq->PHitObj()->SetFExecuting(FALSE);
|
|
pViperReq->PHitObj()->ReportServerError(IDE_500_SERVER_ERROR);
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_REQCOMFAILED();
|
|
g_PerfData.Decr_REQCURRENT();
|
|
#endif
|
|
delete pViperReq->PHitObj();
|
|
}
|
|
else {
|
|
// still connected. Update the test stamp and release the testing
|
|
// bit
|
|
|
|
pViperReq->UpdateTestTimeStamp();
|
|
pViperReq->ClearTestingConnection();
|
|
}
|
|
pViperReq->Release();
|
|
}
|
|
// we'll exit the loop here as well if shutdown becomes true.
|
|
} while(g_ViperReqMgr.m_fShutdown == FALSE);
|
|
|
|
LExit:
|
|
|
|
//
|
|
// the thread is going away.
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperReqManager::AddReqObj
|
|
|
|
Parameters:
|
|
CViperAsyncRequest *pReqObj - request to add to queue
|
|
|
|
Returns:
|
|
HRESULT - always returns S_OK
|
|
|
|
===================================================================*/
|
|
HRESULT CViperReqManager::AddReqObj(CViperAsyncRequest *pReqObj)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DWORD curObjs;
|
|
#endif
|
|
|
|
if (m_fDisabled == TRUE)
|
|
return S_OK;
|
|
|
|
// don't track non-browser requests
|
|
|
|
if (pReqObj->FBrowserRequest() == FALSE)
|
|
return S_OK;
|
|
|
|
// lock the queue and add the object
|
|
|
|
Lock();
|
|
pReqObj->AppendTo(m_ReqObjList);
|
|
m_dwReqObjs++;
|
|
#if DEBUG_REQ_SCAVENGER
|
|
curObjs = m_dwReqObjs;
|
|
#endif
|
|
Unlock();
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::AddReqObj (%p). Total = %d\r\n", pReqObj, curObjs));
|
|
#endif
|
|
// for now, wake up the watch thread everytime.
|
|
|
|
WakeUp();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperReqManager::RemoveReqObj
|
|
|
|
Parameters:
|
|
CViperAsyncRequest *pReqObj - request to remove from the queue
|
|
BOOL fForce - remove regardless of testing state
|
|
|
|
Returns:
|
|
BOOL - TRUE if req was on list, else FALSE
|
|
|
|
===================================================================*/
|
|
BOOL CViperReqManager::RemoveReqObj(CViperAsyncRequest *pReqObj, BOOL fForce)
|
|
{
|
|
BOOL fOnList = FALSE;
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DWORD curObjs;
|
|
#endif
|
|
|
|
if (m_fDisabled == TRUE)
|
|
return TRUE;
|
|
|
|
// if not a browser request, then this queue isn't tracking it.
|
|
// It's safe to return TRUE
|
|
|
|
if (pReqObj->FBrowserRequest() == FALSE)
|
|
return TRUE;
|
|
|
|
// can't remove the object from the queue if it is currently
|
|
// being tested. The exception being if fForce is passed. This
|
|
// is passed only by the WatchThread.
|
|
|
|
sleepAgain:
|
|
while((fForce != TRUE) && pReqObj->FTestingConnection())
|
|
Sleep(100);
|
|
|
|
// lock and remove the request
|
|
|
|
Lock();
|
|
|
|
// check to see if after we got the lock, the request is now
|
|
// being tested. If so, unlock and return to sleeping.
|
|
|
|
if ((fForce != TRUE) && pReqObj->FTestingConnection()) {
|
|
Unlock();
|
|
goto sleepAgain;
|
|
}
|
|
|
|
// now see if it's on the queue. If FIsEmpty(), then it's not
|
|
// on the list.
|
|
if (pReqObj->FIsEmpty()) {
|
|
fOnList = FALSE;
|
|
}
|
|
else {
|
|
// if it is, Unlink it. Call ClearTestingConnection to release
|
|
// any other calls to RemoveReqObj that may be waiting. Of course
|
|
// they all will see that the item is no longer on the list.
|
|
|
|
fOnList = TRUE;
|
|
pReqObj->UnLink();
|
|
pReqObj->ClearTestingConnection();
|
|
m_dwReqObjs--;
|
|
}
|
|
#if DEBUG_REQ_SCAVENGER
|
|
curObjs = m_dwReqObjs;
|
|
#endif
|
|
Unlock();
|
|
|
|
#if DEBUG_REQ_SCAVENGER
|
|
DBGPRINTF((DBG_CONTEXT, "CViperReqManager::RemoveReqObj (%p). fOnList = %d; Total = %d\r\n", pReqObj, fOnList, curObjs));
|
|
#endif
|
|
|
|
// wakeup everytime for now...
|
|
|
|
WakeUp();
|
|
|
|
return fOnList;
|
|
}
|
|
|
|
/*===================================================================
|
|
CViperReqManager::GetNext
|
|
|
|
Parameters:
|
|
CViperAsyncRequest *pLastReq - position in queue.
|
|
|
|
Returns:
|
|
CViperAsyncRequest* - non-NULL if request to test, else end of list
|
|
|
|
===================================================================*/
|
|
CViperAsyncRequest *CViperReqManager::GetNext(CDblLink *pLastReq, BOOL fTestForF5Attack)
|
|
{
|
|
CViperAsyncRequest *pViperReq = NULL;
|
|
|
|
// if NULL was passed in, then start from the head of the queue.
|
|
|
|
if (pLastReq == NULL)
|
|
pLastReq = &m_ReqObjList;
|
|
|
|
// lock the queue while we find the next candidate
|
|
|
|
Lock();
|
|
|
|
if (fTestForF5Attack == TRUE) {
|
|
|
|
// get the next request on the list
|
|
|
|
pViperReq = static_cast<CViperAsyncRequest*>(pLastReq->PPrev());
|
|
|
|
// enter a while loop until we either get to the end of the list or we find
|
|
// a request has been posted to the viper queue
|
|
|
|
while((pViperReq != &m_ReqObjList)
|
|
&& (pViperReq->FAsyncCallPosted() == FALSE))
|
|
pViperReq = static_cast<CViperAsyncRequest*>(pViperReq->PPrev());
|
|
|
|
}
|
|
else {
|
|
|
|
// get the next request on the list
|
|
|
|
pViperReq = static_cast<CViperAsyncRequest*>(pLastReq->PNext());
|
|
|
|
// enter a while loop until we either get to the end of the list or we find
|
|
// a request that hasn't been tested in dwTimeout() and has been posted
|
|
// to the viper queue
|
|
|
|
while((pViperReq != &m_ReqObjList)
|
|
&& ((pViperReq->SecsSinceLastTest() < pViperReq->dwTimeout())
|
|
|| (pViperReq->FAsyncCallPosted() == FALSE)))
|
|
pViperReq = static_cast<CViperAsyncRequest*>(pViperReq->PNext());
|
|
}
|
|
|
|
// if we found a candidate, set the TestingConnection flag
|
|
|
|
if (pViperReq != &m_ReqObjList) {
|
|
pViperReq->SetTestingConnection();
|
|
}
|
|
else {
|
|
// ended up at the queue head. Return NULL.
|
|
pViperReq = NULL;
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return pViperReq;
|
|
}
|
|
|
|
/*===================================================================
|
|
G l o b a l F u n c t i o n s
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
ViperSetContextProperty
|
|
|
|
Static utility function.
|
|
|
|
Set Viper context property by BSTR and IDispatch*.
|
|
The real interface takes BSTR and VARIANT.
|
|
|
|
Parameters
|
|
IContextProperties *pContextProperties Context
|
|
BSTR bstrPropertyName Name
|
|
IDispatch *pdispPropertyValue Value
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
static HRESULT ViperSetContextProperty
|
|
(
|
|
IContextProperties *pContextProperties,
|
|
BSTR bstrPropertyName,
|
|
IDispatch *pdispPropertyValue
|
|
)
|
|
{
|
|
// Make VARIANT from IDispatch*
|
|
|
|
pdispPropertyValue->AddRef();
|
|
|
|
VARIANT Variant;
|
|
VariantInit(&Variant);
|
|
V_VT(&Variant) = VT_DISPATCH;
|
|
V_DISPATCH(&Variant) = pdispPropertyValue;
|
|
|
|
// Call Viper to set the property
|
|
|
|
HRESULT hr = pContextProperties->SetProperty
|
|
(
|
|
bstrPropertyName,
|
|
Variant
|
|
);
|
|
|
|
// Cleanup
|
|
|
|
VariantClear(&Variant);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
ViperAttachIntrinsicsToContext
|
|
|
|
Attach ASP intrinsic objects as Viper context properties
|
|
|
|
Parameters - Intrinsics as interface pointers
|
|
IApplicationObject *pAppln Application (required)
|
|
ISessionObject *pSession Session (optional)
|
|
IRequest *pRequest Request (optional)
|
|
IResponse *pResponse Response (optional)
|
|
IServer *pServer Server (optional)
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT ViperAttachIntrinsicsToContext
|
|
(
|
|
IApplicationObject *pAppln,
|
|
ISessionObject *pSession,
|
|
IRequest *pRequest,
|
|
IResponse *pResponse,
|
|
IServer *pServer
|
|
)
|
|
{
|
|
Assert(pAppln);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get Viper Context
|
|
|
|
IObjectContext *pViperContext = NULL;
|
|
hr = GetObjectContext(&pViperContext);
|
|
|
|
// Get IContextPoperties interface
|
|
|
|
IContextProperties *pContextProperties = NULL;
|
|
if (SUCCEEDED(hr))
|
|
hr = pViperContext->QueryInterface
|
|
(
|
|
IID_IContextProperties,
|
|
(void **)&pContextProperties
|
|
);
|
|
|
|
// Set properties
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = ViperSetContextProperty
|
|
(
|
|
pContextProperties,
|
|
BSTR_OBJ_APPLICATION,
|
|
pAppln
|
|
);
|
|
|
|
if (SUCCEEDED(hr) && pSession)
|
|
hr = ViperSetContextProperty
|
|
(
|
|
pContextProperties,
|
|
BSTR_OBJ_SESSION,
|
|
pSession
|
|
);
|
|
|
|
if (SUCCEEDED(hr) && pRequest)
|
|
hr = ViperSetContextProperty
|
|
(
|
|
pContextProperties,
|
|
BSTR_OBJ_REQUEST,
|
|
pRequest
|
|
);
|
|
|
|
if (SUCCEEDED(hr) && pResponse)
|
|
hr = ViperSetContextProperty
|
|
(
|
|
pContextProperties,
|
|
BSTR_OBJ_RESPONSE,
|
|
pResponse
|
|
);
|
|
|
|
if (SUCCEEDED(hr) && pServer)
|
|
hr = ViperSetContextProperty
|
|
(
|
|
pContextProperties,
|
|
BSTR_OBJ_SERVER,
|
|
pServer
|
|
);
|
|
|
|
// Cleanup
|
|
|
|
if (pContextProperties)
|
|
pContextProperties->Release();
|
|
|
|
if (pViperContext)
|
|
pViperContext->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
ViperGetObjectFromContext
|
|
|
|
Get Viper context property by LPWSTR and
|
|
return it as IDispatch*.
|
|
The real interface takes BSTR and VARIANT.
|
|
|
|
Parameters
|
|
BSTR bstrName Property Name
|
|
IDispatch **ppdisp [out] Object (Property Value)
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT ViperGetObjectFromContext
|
|
(
|
|
BSTR bstrName,
|
|
IDispatch **ppdisp
|
|
)
|
|
{
|
|
Assert(ppdisp);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get Viper Context
|
|
|
|
IObjectContext *pViperContext = NULL;
|
|
hr = GetObjectContext(&pViperContext);
|
|
|
|
// Get IContextPoperties interface
|
|
|
|
IContextProperties *pContextProperties = NULL;
|
|
if (SUCCEEDED(hr))
|
|
hr = pViperContext->QueryInterface
|
|
(
|
|
IID_IContextProperties,
|
|
(void **)&pContextProperties
|
|
);
|
|
|
|
// Get property Value as variant
|
|
|
|
VARIANT Variant;
|
|
VariantInit(&Variant);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pContextProperties->GetProperty(bstrName, &Variant);
|
|
|
|
// Convert Variant to IDispatch*
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IDispatch *pDisp = NULL;
|
|
if (V_VT(&Variant) == VT_DISPATCH)
|
|
pDisp = V_DISPATCH(&Variant);
|
|
|
|
if (pDisp)
|
|
{
|
|
pDisp->AddRef();
|
|
*ppdisp = pDisp;
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
// Cleanup
|
|
|
|
VariantClear(&Variant);
|
|
|
|
if (pContextProperties)
|
|
pContextProperties->Release();
|
|
|
|
if (pViperContext)
|
|
pViperContext->Release();
|
|
|
|
if (FAILED(hr))
|
|
*ppdisp = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
ViperGetHitObjFromContext
|
|
|
|
Get Server from Viper context property and get
|
|
it's current HitObj
|
|
|
|
Parameters
|
|
CHitObj **ppHitObj [out]
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT ViperGetHitObjFromContext
|
|
(
|
|
CHitObj **ppHitObj
|
|
)
|
|
{
|
|
*ppHitObj = NULL;
|
|
|
|
IDispatch *pdispServer = NULL;
|
|
HRESULT hr = ViperGetObjectFromContext(BSTR_OBJ_SERVER, &pdispServer);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (pdispServer)
|
|
{
|
|
CServer *pServer = static_cast<CServer *>(pdispServer);
|
|
*ppHitObj = pServer->PHitObj();
|
|
pdispServer->Release();
|
|
}
|
|
|
|
return *ppHitObj ? S_OK : S_FALSE;
|
|
}
|
|
|
|
/*===================================================================
|
|
ViperCreateInstance
|
|
|
|
Viper's implementation of CoCreateInstance
|
|
|
|
Parameters
|
|
REFCLSID rclsid class id
|
|
REFIID riid interface
|
|
void **ppv [out] pointer to interface
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT ViperCreateInstance
|
|
(
|
|
REFCLSID rclsid,
|
|
REFIID riid,
|
|
void **ppv
|
|
)
|
|
{
|
|
/*
|
|
DWORD dwClsContext = (Glob(fAllowOutOfProcCmpnts)) ?
|
|
CLSCTX_INPROC_SERVER | CLSCTX_SERVER :
|
|
CLSCTX_INPROC_SERVER;
|
|
*/
|
|
|
|
// The reasons for supporting ASPAllowOutOfProcComponents seem to have
|
|
// vanished. Because this only partially worked in II4 and we changed
|
|
// the default in IIS5 this was causing problems with upgrades. So
|
|
// we're going to ignore the fAllowOutOfProcCmpnts setting.
|
|
|
|
DWORD dwClsContext = CLSCTX_INPROC_SERVER | CLSCTX_SERVER;
|
|
return CoCreateInstance(rclsid, NULL, dwClsContext, riid, ppv);
|
|
}
|
|
|
|
/*===================================================================
|
|
ViperConfigure
|
|
|
|
Viper settings: # of threads, queue len,
|
|
in-proc failfast,
|
|
allow oop components
|
|
|
|
Parameters
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT ViperConfigure()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMTSPackage *pPackage = NULL;
|
|
|
|
//
|
|
// Get hold of the package
|
|
//
|
|
|
|
hr = CoCreateInstance(CLSID_MTSPackage,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IMTSPackage,
|
|
(void **)&pPackage);
|
|
if (SUCCEEDED(hr) && !pPackage)
|
|
hr = E_FAIL;
|
|
|
|
//
|
|
// Bug 111008: Tell Viper that we do impersonations
|
|
//
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IImpersonationControl *pImpControl = NULL;
|
|
hr = pPackage->QueryInterface(IID_IImpersonationControl, (void **)&pImpControl);
|
|
|
|
if (SUCCEEDED(hr) && pImpControl)
|
|
{
|
|
hr = pImpControl->ClientsImpersonate(TRUE);
|
|
pImpControl->Release();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disable FAILFAST for in-proc case
|
|
//
|
|
|
|
if (SUCCEEDED(hr) && !g_fOOP)
|
|
{
|
|
IFailfastControl *pFFControl = NULL;
|
|
hr = pPackage->QueryInterface(IID_IFailfastControl, (void **)&pFFControl);
|
|
|
|
if (SUCCEEDED(hr) && pFFControl)
|
|
{
|
|
pFFControl->SetApplFailfast(FALSE);
|
|
pFFControl->Release();
|
|
}
|
|
}
|
|
|
|
/*
|
|
//
|
|
// Set Allow OOP Components
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
INonMTSActivation *pNonMTSActivation = NULL;
|
|
hr = pPackage->QueryInterface(IID_INonMTSActivation, (void **)&pNonMTSActivation);
|
|
|
|
if (SUCCEEDED(hr) && pNonMTSActivation)
|
|
{
|
|
pNonMTSActivation->OutOfProcActivationAllowed(fAllowOopComponents);
|
|
pNonMTSActivation->Release();
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
|
|
if (pPackage)
|
|
pPackage->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ViperConfigureMTA()
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
IMTSPackage *pPackage = NULL;
|
|
|
|
if (g_fFirstMTAHit) {
|
|
|
|
EnterCriticalSection(&g_csFirstMTAHitLock);
|
|
|
|
if (g_fFirstMTAHit) {
|
|
|
|
//
|
|
// Get hold of the package
|
|
//
|
|
|
|
hr = CoCreateInstance(CLSID_MTSPackage,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IMTSPackage,
|
|
(void **)&pPackage);
|
|
|
|
if (SUCCEEDED(hr) && !pPackage)
|
|
hr = E_FAIL;
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
IComMtaThreadPoolKnobs *pMTAKnobs = NULL;
|
|
hr = pPackage->QueryInterface(IID_IComMtaThreadPoolKnobs, (void **)&pMTAKnobs);
|
|
|
|
if (SUCCEEDED(hr) && pMTAKnobs) {
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
|
|
DWORD dwThreadCount;
|
|
|
|
if (FAILED(g_AspRegistryParams.GetTotalThreadMax(&dwThreadCount))) {
|
|
dwThreadCount = DEFAULT_MAX_THREAD;
|
|
}
|
|
|
|
if (dwThreadCount > Glob(dwProcessorThreadMax)*si.dwNumberOfProcessors) {
|
|
dwThreadCount = Glob(dwProcessorThreadMax)*si.dwNumberOfProcessors;
|
|
}
|
|
|
|
hr = pMTAKnobs->MTASetMaxThreadCount(dwThreadCount);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pMTAKnobs->MTASetThrottleValue(8);
|
|
|
|
pMTAKnobs->Release();
|
|
}
|
|
}
|
|
|
|
g_fFirstMTAHit = FALSE;
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csFirstMTAHitLock);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT ViperConfigureSTA()
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
IMTSPackage *pPackage = NULL;
|
|
|
|
if (g_fFirstSTAHit) {
|
|
|
|
EnterCriticalSection(&g_csFirstSTAHitLock);
|
|
|
|
if (g_fFirstSTAHit) {
|
|
|
|
//
|
|
// Get hold of the package
|
|
//
|
|
|
|
hr = CoCreateInstance(CLSID_MTSPackage,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IMTSPackage,
|
|
(void **)&pPackage);
|
|
|
|
if (SUCCEEDED(hr) && !pPackage)
|
|
hr = E_FAIL;
|
|
|
|
|
|
//
|
|
// Set knobs
|
|
//
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IComStaThreadPoolKnobs *pKnobs = NULL;
|
|
hr = pPackage->QueryInterface(IID_IComStaThreadPoolKnobs, (void **)&pKnobs);
|
|
|
|
if (SUCCEEDED(hr) && pKnobs)
|
|
{
|
|
// number of threads
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
|
|
DWORD dwThreadCount;
|
|
DWORD dwMinThreads;
|
|
|
|
if (FAILED(g_AspRegistryParams.GetTotalThreadMax(&dwThreadCount))) {
|
|
dwThreadCount = DEFAULT_MAX_THREAD;
|
|
}
|
|
|
|
if (dwThreadCount > Glob(dwProcessorThreadMax)*si.dwNumberOfProcessors) {
|
|
dwThreadCount = Glob(dwProcessorThreadMax)*si.dwNumberOfProcessors;
|
|
}
|
|
|
|
dwMinThreads = (si.dwNumberOfProcessors > 4) ? si.dwNumberOfProcessors : 4;
|
|
|
|
if (dwThreadCount < dwMinThreads) {
|
|
dwThreadCount = dwMinThreads;
|
|
}
|
|
|
|
pKnobs->SetMaxThreadCount(dwThreadCount);
|
|
pKnobs->SetMinThreadCount(dwMinThreads);
|
|
|
|
// queue length
|
|
pKnobs->SetQueueDepth(30000);
|
|
|
|
pKnobs->SetActivityPerThread(1);
|
|
|
|
pKnobs->Release();
|
|
}
|
|
}
|
|
|
|
// set Knobs2
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IComStaThreadPoolKnobs2 *pKnobs2 = NULL;
|
|
hr = pPackage->QueryInterface(IID_IComStaThreadPoolKnobs2, (void **)&pKnobs2);
|
|
|
|
if (SUCCEEDED(hr) && pKnobs2)
|
|
{
|
|
DWORD dwMaxCSR;
|
|
DWORD dwMaxCPU;
|
|
DWORD dwDisableCpuMetric;
|
|
|
|
if (SUCCEEDED(g_AspRegistryParams.GetMaxCPU(&dwMaxCPU))) {
|
|
pKnobs2->SetMaxCPULoad(dwMaxCPU);
|
|
}
|
|
else {
|
|
pKnobs2->SetMaxCPULoad(100);
|
|
}
|
|
|
|
if (SUCCEEDED(g_AspRegistryParams.GetMaxCSR(&dwMaxCSR))) {
|
|
pKnobs2->SetMaxCSR(dwMaxCSR);
|
|
}
|
|
|
|
if (SUCCEEDED(g_AspRegistryParams.GetDisableComPlusCpuMetric(&dwDisableCpuMetric))) {
|
|
pKnobs2->SetCPUMetricEnabled(!dwDisableCpuMetric);
|
|
}
|
|
|
|
pKnobs2->Release();
|
|
}
|
|
}
|
|
|
|
g_fFirstSTAHit = FALSE;
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&g_csFirstSTAHitLock);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
C O M H e l p e r A P I
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
ViperCoObjectIsaProxy
|
|
|
|
Checks if the given IUnknown* points to a proxy
|
|
|
|
Parameters
|
|
IUnknown* pUnk pointer to check
|
|
|
|
Returns:
|
|
BOOL (TRUE if Proxy)
|
|
===================================================================*/
|
|
BOOL ViperCoObjectIsaProxy
|
|
(
|
|
IUnknown* pUnk
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IUnknown *pUnk2;
|
|
|
|
hr = pUnk->QueryInterface(IID_IProxyManager, (void**)&pUnk2);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
pUnk2->Release();
|
|
return TRUE;
|
|
}
|
|
|
|
/*===================================================================
|
|
ViperCoObjectAggregatesFTM
|
|
|
|
Checks if the given object agregates free threaded marshaller
|
|
(is agile)
|
|
|
|
Parameters
|
|
IUnknown* pUnk pointer to check
|
|
|
|
Returns:
|
|
BOOL (TRUE if Agile)
|
|
===================================================================*/
|
|
BOOL ViperCoObjectAggregatesFTM
|
|
(
|
|
IUnknown *pUnk
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IMarshal *pMarshal;
|
|
GUID guidClsid;
|
|
|
|
hr = pUnk->QueryInterface(IID_IMarshal, (void**)&pMarshal);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
hr = pMarshal->GetUnmarshalClass(IID_IUnknown, pUnk, MSHCTX_INPROC,
|
|
NULL, MSHLFLAGS_NORMAL, &guidClsid);
|
|
pMarshal->Release();
|
|
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
return (guidClsid == CLSID_InProcFreeMarshaler);
|
|
}
|