2025-04-27 07:49:33 -04:00

583 lines
15 KiB
C++

//-----------------------------------------------------------------------------
//
//
// File: ConnSched.cpp
//
// Description: Implementation of CConnScheduler class, which provides a stub
// implementation of IConnectionScheduler for aqueue.dll.
//
// Author: mikeswa
//
// Copyright (C) 1998 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <transmem.h>
#include "consched.h"
#include "catmsgq.h"
#define DEFAULT_DOMAIN_LENGTH 256 //initial
#define ACTION_SIG 'ntcA'
//---[ eScheduleAction ]-------------------------------------------------------
//
//
// Description: Enum describing the possible actions that can happen
//
//
//-----------------------------------------------------------------------------
typedef enum eScheduleAction
{
SCHEDULE_ACTION_RETRY = 0x000000001,
SCHEDULE_ACTION_ENABLE = 0x000000002,
SCHEDULE_ACTION_DISABLE = 0x000000003,
};
eScheduleAction g_eScheduleAction = SCHEDULE_ACTION_RETRY; //encourage symbols
//---[ CScheduledAction ]------------------------------------------------------
//
//
// Hungarian: pSchedAct
//
// Description: Class that represent a single queued action for the scheduler
//
//
//-----------------------------------------------------------------------------
class CScheduledAction
{
public:
CScheduledAction(DWORD dwAction);
~CScheduledAction();
HRESULT HrInit(DWORD cbDomain, LPSTR szDomain);
DWORD m_dwSig; //signature used when to make verify dequeued object
DWORD m_dwAction; //Action to start
DWORD m_cbDomain; //# bytes in domain name
LPSTR m_szDomain; //domain name string
CHAR m_szBuffer[DEFAULT_DOMAIN_LENGTH]; //default buffer to use
};
//---[ CScheduledAction::CScheduledAction ]------------------------------------
//
//
// Description:
// Class constructor for CScheduledAction
// Parameters:
// Action to create object for
// Returns:
// -
//
//-----------------------------------------------------------------------------
inline CScheduledAction::CScheduledAction(DWORD dwAction)
{
m_dwSig = ACTION_SIG;
m_dwAction = dwAction;
m_cbDomain = 0;
m_szDomain = NULL;
m_szBuffer[0] = '\0';
}
//---[ CScheduledAction::~CScheduledAction ]-----------------------------------
//
//
// Description:
// Class destructor
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
inline CScheduledAction::~CScheduledAction()
{
if (m_szDomain && (m_szDomain != m_szBuffer))
{
_ASSERT( m_cbDomain >= DEFAULT_DOMAIN_LENGTH);
FreePv(m_szDomain);
}
}
//---[ CScheduledAction::HrInit ]----------------------------------------------
//
//
// Description:
// Initializes a CScheduleAction object... will allocate a buffer for the
// string name if neccessary
// Parameters:
// cbDomain # bytes in domain name
// szDomain Domain Name string
// Returns:
// -
//
//-----------------------------------------------------------------------------
HRESULT inline CScheduledAction::HrInit(DWORD cbDomain, LPSTR szDomain)
{
HRESULT hr = S_OK;
if (cbDomain >= DEFAULT_DOMAIN_LENGTH)
{
m_szDomain = (LPSTR) pvMalloc(cbDomain+sizeof(CHAR));
if (!m_szDomain)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
}
else
{
m_szDomain = m_szBuffer;
}
m_cbDomain = cbDomain;
memcpy(m_szDomain, szDomain, cbDomain+sizeof(CHAR));
Exit:
return hr;
}
//---[ SCHED_THREAD_PARAM ]----------------------------------------------------
//
//
// Description: Struct used to pass thread parameters to threads created to
// handle scheduled actions/
//
//
//-----------------------------------------------------------------------------
typedef struct _SCHED_THREAD_PARAM
{
CScheduledAction *pSchedAct; //scheduled action
CConnScheduler *pConnSched; //object that can process action
DWORD dwDelay; //time to sleep before action
} SCHED_THREAD_PARAM, *PSCHED_THREAD_PARAM;
//---[ StartConnectionSchedulerThread ]----------------------------------------
//
//
// Description:
// Function used to start a Connection Scheduler thread
// Parameters:
// lpThreadParam - actually a this pointer for a CMsgConversion Obj.
// Returns:
// HRESULT indicating success or failure.
//
//-----------------------------------------------------------------------------
DWORD WINAPI StartConnectionSchedulerThread(LPVOID lpThreadParam)
{
HRESULT hr = S_OK;
bool fCOMInit = false;
PSCHED_THREAD_PARAM pSchedThreadParam = (PSCHED_THREAD_PARAM) lpThreadParam;
CScheduledAction *pSchedAct = pSchedThreadParam->pSchedAct;
CConnScheduler *pConnSched = pSchedThreadParam->pConnSched;
_ASSERT(pSchedAct && pConnSched);
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
goto Exit;
fCOMInit = true;
if (ACTION_SIG != pSchedAct->m_dwSig)
{
hr = E_INVALIDARG;
goto Exit;
}
hr = pConnSched->HrProcessAction(pSchedAct, pSchedThreadParam->dwDelay);
if (FAILED(hr))
goto Exit;
Exit:
if (pConnSched)
pConnSched->Release();
if (pSchedThreadParam)
delete pSchedThreadParam;
if (fCOMInit)
CoUninitialize();
return ((DWORD) hr);
}
//---[ CConnScheduler::CConnScheduler ]----------------------------------------
//
//
// Description:
// Class constructor for CConnScheduler
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
CConnScheduler::CConnScheduler(CCatMsgQueue *pcmq, IScheduleManager *pISchedMgr)
{
_ASSERT(pcmq);
_ASSERT(pISchedMgr);
m_pcmq = pcmq;
m_pISchedMgr = pISchedMgr;
m_pcmq->AddRef();
m_pISchedMgr->AddRef();
m_hShutdown = NULL;
}
//---[ CConnScheduler::~CConnScheduler ]---------------------------------------
//
//
// Description:
// Class destructor for CConnScheduler
// Parameters:
//
// Returns:
//
//
//-----------------------------------------------------------------------------
CConnScheduler::~CConnScheduler()
{
if (m_pcmq)
m_pcmq->Release();
if (m_pISchedMgr)
m_pISchedMgr->Release();
if (m_hShutdown)
{
CloseHandle(m_hShutdown);
}
}
//---[ CConnScheduler::HrInit ]------------------------------------------------
//
//
// Description:
//
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
HRESULT CConnScheduler::HrInit()
{
HRESULT hr = S_OK;
hr = HrInitSyncShutdown();
m_hShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!m_hShutdown)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
Exit:
return hr;
}
//---[ CConnScheduler::HrDeinit ]----------------------------------------------
//
//
// Description:
// Signals an orderly shutdown
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
HRESULT CConnScheduler::HrDeinit()
{
HRESULT hr = S_OK;
if (!SetEvent(m_hShutdown))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
SignalShutdown();
if (m_pcmq)
{
m_pcmq->Release();
m_pcmq = NULL;
}
if (m_pISchedMgr)
{
m_pISchedMgr->Release();
m_pISchedMgr = NULL;
}
Sleep(1000); //given retry threads a chance to shutdown.
Exit:
return hr;
}
//---[ CConnScheduler::HrProcessAction ]--------------------------------------
//
//
// Description:
// Processes a single schedule action
// Parameters:
// pSchedAct object that encapsulates scheduled action
// cDelayMilliseconds Time in millseconds to delay processing of action
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
HRESULT CConnScheduler::HrProcessAction(CScheduledAction *pSchedAct,
DWORD dwDelayMilliseconds)
{
HRESULT hr = S_OK;
DWORD dwWaitResult = 0;
BOOL fLocked = FALSE;
_ASSERT(m_pISchedMgr);
_ASSERT(m_hShutdown);
_ASSERT(pSchedAct);
_ASSERT(pSchedAct->m_szDomain);
_ASSERT(pSchedAct->m_cbDomain);
hr = HrLock();
if (FAILED(hr))
goto Exit;
fLocked = TRUE;
dwWaitResult = WaitForSingleObject(m_hShutdown, dwDelayMilliseconds);
if (WAIT_OBJECT_0 == dwWaitResult) //shutdown
{
hr = AQUEUE_E_SHUTDOWN;
goto Exit;
}
_ASSERT(WAIT_TIMEOUT == dwWaitResult);
if (pSchedAct->m_dwAction & SCHEDULE_ACTION_RETRY)
{
hr = m_pISchedMgr->EnableOutboundConnections(pSchedAct->m_cbDomain,
pSchedAct->m_szDomain, 0);
if (FAILED(hr))
goto Exit;
}
else
{
_ASSERT(0 || "Invalid Arg");
}
Exit:
if (fLocked)
HrUnlock();
return hr;
}
//---[ CConnScheduler::QueryInterface ]----------------------------------------
//
//
// Description:
// QueryInterface for IConnectionScheduler
// Parameters:
//
// Returns:
// S_OK on success
//
//
//-----------------------------------------------------------------------------
STDMETHODIMP CConnScheduler::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
HRESULT hr = S_OK;
if (!ppvObj)
{
hr = E_INVALIDARG;
goto Exit;
}
if (IID_IUnknown == riid)
{
*ppvObj = static_cast<IConnectionScheduler *>(this);
}
else if (IID_IConnectionScheduler == riid)
{
*ppvObj = static_cast<IConnectionScheduler *>(this);
}
else
{
*ppvObj = NULL;
hr = E_NOINTERFACE;
goto Exit;
}
static_cast<IUnknown *>(*ppvObj)->AddRef();
Exit:
return hr;
}
//---[ CConnScheduler::ConnectionReleased ]------------------------------------
//
//
// Description:
// Implements IConnectionScheduler::ConnectionRelease(). Called when
// SMTP releases a connection and is used to handle retry logic
// Parameters:
// IN cbDomainName String length of domain name
// IN szDomainName Domain name of connection that was released
// IN dwScheduleID Schedule ID of this connection
// IN cFailedMessages Number of messages that were attempted and failed
// by the particular connection instance being released
// IN cUntiredMessages The number of messages that have not been attempted
// For this <domain, schedule>
// IN cOutstandingConnections The number of other outstanding connections
// for this <domain, schedule>
// OUT pfAllowImmediateRetry Should we allow any further connections to
// be created for this <domain, schedule>
// Returns:
// S_OK on success
//
// REVIEW:
// Will we really want to expose this functionality externally? It might
// make sense to have another internal object that makes calls to ISMTP
//-----------------------------------------------------------------------------
STDMETHODIMP CConnScheduler::ConnectionReleased(
IN DWORD cbDomainName,
IN char szDomainName[],
IN DWORD dwScheduleID,
IN DWORD dwConnectionStatus, //eConnectionStatus
IN DWORD cFailedMessages, //# of failed message for *this* connection
IN DWORD cUntriedMessages, //# of untried messages in queue
IN DWORD cOutstandingConnections,//# of other active connections for this domain
OUT BOOL *pfAllowImmediateRetry)
{
TraceFunctEnterEx((LPARAM) this, "CConnScheduler::ConnectionReleased");
HRESULT hr = S_OK;
CScheduledAction *pSchedAct = NULL;
PSCHED_THREAD_PARAM pThreadParam = NULL;
HANDLE hThread = NULL;
DWORD dwThreadID = 0;
BOOL fLocked = FALSE;
_ASSERT(pfAllowImmediateRetry);
hr = HrLock();
if (FAILED(hr))
goto Exit;
fLocked = TRUE;
if (!cFailedMessages && (CONNECTION_STATUS_OK == dwConnectionStatus))
{
*pfAllowImmediateRetry = TRUE;
}
else
{
DebugTrace((LPARAM) dwConnectionStatus, "INFO: Remote Domain %s scheduled for retry", szDomainName);
pSchedAct = new CScheduledAction(SCHEDULE_ACTION_RETRY);
hr = pSchedAct->HrInit(cbDomainName, szDomainName);
if (FAILED(hr))
goto Exit;
pThreadParam = (PSCHED_THREAD_PARAM) pvMalloc(sizeof(SCHED_THREAD_PARAM));
if (!pThreadParam)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
pThreadParam->pSchedAct = pSchedAct;
pThreadParam->pConnSched = this;
if (FAILED(m_pcmq->HrGetConnectionRetry(&(pThreadParam->dwDelay))))
{
pThreadParam->dwDelay = 60000;
}
AddRef();
hThread = CreateThread(
NULL,
0,
StartConnectionSchedulerThread,
(LPVOID) pThreadParam,
0,
&dwThreadID);
if (NULL == hThread)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if (!CloseHandle(hThread))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
hThread = NULL;
pThreadParam = NULL;
pSchedAct = NULL;
*pfAllowImmediateRetry = FALSE;
}
Exit:
if (pSchedAct)
delete pSchedAct;
if (pThreadParam)
{
Release();
FreePv(pThreadParam);
}
if (fLocked)
HrUnlock();
TraceFunctLeave();
return hr;
}
//---[ CConnScheduler::NewRemoteDomain ]---------------------------------------
//
//
// Description:
// Implements IConnectionScheduler::NewRemoteDomain(). For MM1, this is
// just a stub implementation with no actual scheduling happening (all
// domains are approved for immediate connections.
// Parameters:
// IN cbDomainName String length of domain name
// IN szDomainName Domain name of next hop link that was created
// IN dwScheduleID Schedule ID of this connection
// OUT pfAllowImmediateConnection Should we allow any further connections to
// be created for this <domain, schedule> befote
// IScheduleManager::EnableOutboundConnections is called.
// Returns:
// S_OK on success
//
//-----------------------------------------------------------------------------
STDMETHODIMP CConnScheduler::NewRemoteDomain(
IN DWORD cbDomainName,
IN char szDomainName[],
IN DWORD dwScheduleID,
OUT BOOL *pfAllowImmediateConnection)
{
HRESULT hr = S_OK;
_ASSERT(pfAllowImmediateConnection);
*pfAllowImmediateConnection = TRUE;
return hr;
}