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.
487 lines
14 KiB
487 lines
14 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// File: defdlvrq.cpp
|
|
//
|
|
// Description: Implementation of CAQDeferredDeliveryQueue &
|
|
// CAQDeferredDeliveryQueueEntry.
|
|
//
|
|
// Author: Mike Swafford (MikeSwa)
|
|
//
|
|
// History:
|
|
// 12/23/98 - MikeSwa Created
|
|
//
|
|
// Copyright (C) 1998 Microsoft Corporation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "aqprecmp.h"
|
|
#include "defdlvrq.h"
|
|
#include "aqutil.h"
|
|
|
|
//---[ CAQDeferredDeliveryQueueEntry::CAQDeferredDeliveryQueueEntry ]----------
|
|
//
|
|
//
|
|
// Description:
|
|
// Constructor for CAQDeferredDeliveryQueueEntry class
|
|
// Parameters:
|
|
// IN pIMailMsgProperties MailMsg to queue
|
|
// IN pft FILTIME (UT) to defer deliver until
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CAQDeferredDeliveryQueueEntry::CAQDeferredDeliveryQueueEntry(
|
|
IMailMsgProperties *pIMailMsgProperties,
|
|
FILETIME *pft)
|
|
{
|
|
_ASSERT(pIMailMsgProperties);
|
|
_ASSERT(pft);
|
|
|
|
m_liQueueEntry.Flink = NULL;
|
|
m_liQueueEntry.Blink = NULL;
|
|
|
|
memcpy(&m_ftDeferredDeilveryTime, pft, sizeof(FILETIME));
|
|
|
|
m_pIMailMsgProperties = pIMailMsgProperties;
|
|
|
|
if (m_pIMailMsgProperties)
|
|
{
|
|
m_pIMailMsgProperties->AddRef();
|
|
|
|
//Release usage count while this message is pending delivery
|
|
HrReleaseIMailMsgUsageCount(m_pIMailMsgProperties);
|
|
|
|
}
|
|
|
|
m_fCallbackSet = FALSE;
|
|
m_dwSignature = DEFERRED_DELIVERY_QUEUE_ENTRY_SIG;
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueueEntry::~CAQDeferredDeliveryQueueEntry ]---------
|
|
//
|
|
//
|
|
// Description:
|
|
// Descructor for CAQDeferredDeliveryQueueEntry class
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CAQDeferredDeliveryQueueEntry::~CAQDeferredDeliveryQueueEntry()
|
|
{
|
|
//Release mailmsg properties
|
|
if (m_pIMailMsgProperties)
|
|
{
|
|
m_pIMailMsgProperties->Release();
|
|
m_pIMailMsgProperties = NULL;
|
|
}
|
|
|
|
//Remove from list (if in list)
|
|
if (m_liQueueEntry.Flink)
|
|
{
|
|
_ASSERT(m_liQueueEntry.Blink);
|
|
RemoveEntryList(&m_liQueueEntry);
|
|
}
|
|
MARK_SIG_AS_DELETED(m_dwSignature);
|
|
}
|
|
|
|
|
|
//---[ CAQDeferredDeliveryQueueEntry::SetCallback ]----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Sets callback for queue. Per Entry state is maintained so we know we
|
|
// have 1 and only 1 callback per head of queue.
|
|
//
|
|
// Queue private lock should be exclusive when this is called
|
|
// Parameters:
|
|
// pvContext Context for callback function
|
|
// paqinst Server Instance object
|
|
// Returns:
|
|
// TRUE if a callback is set
|
|
// History:
|
|
// 1/13/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CAQDeferredDeliveryQueueEntry::fSetCallback(PVOID pvContext,
|
|
CAQSvrInst *paqinst)
|
|
{
|
|
if (!m_fCallbackSet && paqinst)
|
|
{
|
|
m_fCallbackSet = TRUE;
|
|
paqinst->SetCallbackTime(CAQDeferredDeliveryQueue::TimerCallback,
|
|
pvContext, &m_ftDeferredDeilveryTime);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueueEntry::pmsgGetMsg ]-----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Get AddRef'd message for this entry
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// pIMailMsgProperties.
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
IMailMsgProperties *CAQDeferredDeliveryQueueEntry::pmsgGetMsg()
|
|
{
|
|
_ASSERT(m_pIMailMsgProperties);
|
|
IMailMsgProperties *pIMailMsgProperties = m_pIMailMsgProperties;
|
|
|
|
if (pIMailMsgProperties)
|
|
{
|
|
//Add the usage count the we released earlier on
|
|
HrIncrementIMailMsgUsageCount(m_pIMailMsgProperties);
|
|
|
|
//Set to NULL, so caller "owns" this entry's reference count (and
|
|
//usage count).
|
|
m_pIMailMsgProperties = NULL;
|
|
}
|
|
|
|
return pIMailMsgProperties;
|
|
}
|
|
|
|
|
|
//---[ CAQDeferredDeliveryQueue::CAQDeferredDeliveryQueue ]--------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Constructor for CAQDeferredDeliveryQueue class
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CAQDeferredDeliveryQueue::CAQDeferredDeliveryQueue()
|
|
{
|
|
m_dwSignature = DEFERRED_DELIVERY_QUEUE_SIG;
|
|
InitializeListHead(&m_liQueueHead);
|
|
m_paqinst = NULL;
|
|
m_cCallbacksPending = 0;
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueue::~CAQDeferredDeliveryQueue ]-------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Default destructor for CAQDeferredDeliveryQueue.
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CAQDeferredDeliveryQueue::~CAQDeferredDeliveryQueue()
|
|
{
|
|
Deinitialize();
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueue::Initialize ]----------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Initialization for CAQDeferredDeliveryQueue
|
|
// Parameters:
|
|
// IN paqinst Ptr to virtual server instance object
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/29/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CAQDeferredDeliveryQueue::Initialize(CAQSvrInst *paqinst)
|
|
{
|
|
_ASSERT(paqinst);
|
|
m_paqinst = paqinst;
|
|
m_paqinst->AddRef();
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueue::Deinitialize ]--------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Performs first-pass shutdown for CAQDeferredDeliveryQueue
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CAQDeferredDeliveryQueue::Deinitialize()
|
|
{
|
|
CAQDeferredDeliveryQueueEntry *pdefqe = NULL;
|
|
LIST_ENTRY *pli = NULL;
|
|
|
|
//$$REVIEW - It may be adventagious to remove this lock and rely on the
|
|
//private data lock of the virtual server instance. This will require
|
|
//fixing fTryRoutingLock. Also having a single lock leads to single-thread
|
|
//deadlock issues while we have it exclusively and call to submit.
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
pli = m_liQueueHead.Flink;
|
|
|
|
//Walk queue and delete remaining entries
|
|
while (pli != &m_liQueueHead)
|
|
{
|
|
pdefqe = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(pli);
|
|
|
|
if (m_paqinst)
|
|
m_paqinst->ServerStopHintFunction();
|
|
|
|
//Make sure we get the next before deleting the entry :)
|
|
pli = pli->Flink;
|
|
|
|
_ASSERT(pdefqe);
|
|
delete pdefqe;
|
|
}
|
|
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->Release();
|
|
m_paqinst = NULL;
|
|
}
|
|
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueue::Enqueue ]-------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Enqueues a message for deferred delivery
|
|
// Parameters:
|
|
// IN pIMailMsgProperties message to defer
|
|
// IN pft FILETIME to defer delivery too
|
|
// Returns:
|
|
// - Failures are handled internally
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CAQDeferredDeliveryQueue::Enqueue(IMailMsgProperties *pIMailMsgProperties,
|
|
FILETIME *pft)
|
|
{
|
|
CAQDeferredDeliveryQueueEntry *pdefqeCurrent = NULL;
|
|
CAQDeferredDeliveryQueueEntry *pdefqeNew = NULL;
|
|
LIST_ENTRY *pli = NULL;
|
|
LARGE_INTEGER *pLargeIntCurrentDeferredTime = NULL;
|
|
LARGE_INTEGER *pLargeIntNewDeferredTime = (LARGE_INTEGER *)pft;
|
|
|
|
_ASSERT(pIMailMsgProperties);
|
|
_ASSERT(pft);
|
|
|
|
pdefqeNew = new CAQDeferredDeliveryQueueEntry(pIMailMsgProperties, pft);
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
|
|
if (!pdefqeNew)
|
|
{
|
|
//Handle Out of memory situation
|
|
_ASSERT(m_paqinst); //if we don't have a virtual server we're toast
|
|
if (m_paqinst)
|
|
{
|
|
m_paqinst->DecPendingDeferred();
|
|
//pass off to virtual server object for general failure handling
|
|
m_paqinst->HandleAQFailure(AQ_FAILURE_PENDING_DEFERRED_DELIVERY,
|
|
E_OUTOFMEMORY, pIMailMsgProperties);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
pli = m_liQueueHead.Flink;
|
|
|
|
//Walk queue and look for entries with a later deferred delivery time.
|
|
while (pli != &m_liQueueHead)
|
|
{
|
|
pdefqeCurrent = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(pli);
|
|
_ASSERT(pdefqeCurrent);
|
|
pLargeIntCurrentDeferredTime = (LARGE_INTEGER *)
|
|
pdefqeCurrent->pftGetDeferredDeliveryTime();
|
|
|
|
//If we have found an entry with a later time, we're done and will insert
|
|
//in front of this entry
|
|
if (pLargeIntCurrentDeferredTime->QuadPart > pLargeIntNewDeferredTime->QuadPart)
|
|
{
|
|
//back up so insert will happen between current and previous entry
|
|
pli = pli->Blink;
|
|
break;
|
|
}
|
|
|
|
//continue searching forward (same direction as dequeue)
|
|
pli = pli->Flink;
|
|
_ASSERT(pli);
|
|
}
|
|
|
|
_ASSERT(pli);
|
|
pdefqeNew->InsertBefore(pli);
|
|
|
|
SetCallback();
|
|
|
|
Exit:
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueue::ProcessEntries ]------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Processes entries from the front of the queue until there are no
|
|
// more entries with deferred delivery times in the past.
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CAQDeferredDeliveryQueue::ProcessEntries()
|
|
{
|
|
CAQDeferredDeliveryQueueEntry *pdefqe = NULL;
|
|
LIST_ENTRY *pli = NULL;
|
|
FILETIME *pftDeferredTime = NULL;
|
|
DWORD dwTimeContext = 0;
|
|
IMailMsgProperties *pIMailMsgProperties = NULL;
|
|
HRESULT hr = S_OK;
|
|
DWORD cEntriesProcessed = 0;
|
|
|
|
m_slPrivateData.ExclusiveLock();
|
|
pli = m_liQueueHead.Flink;
|
|
|
|
//if we do not have a virtual server pointer... then we have nothing
|
|
//to do with the processed messages
|
|
if (!m_paqinst)
|
|
goto Exit;
|
|
|
|
//Walk queue and delete remaining entries
|
|
while (pli != &m_liQueueHead)
|
|
{
|
|
pdefqe = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(pli);
|
|
_ASSERT(pdefqe);
|
|
pftDeferredTime = pdefqe->pftGetDeferredDeliveryTime();
|
|
|
|
//Check if the deferred delivery time is in the past... if not, we are done
|
|
if (!m_paqinst->fInPast(pftDeferredTime, &dwTimeContext))
|
|
{
|
|
if (!cEntriesProcessed)
|
|
{
|
|
//we have processed no entries... and wasted a callback
|
|
//force another callback so messages don't get stranded
|
|
pdefqe->ResetCallbackFlag();
|
|
}
|
|
break;
|
|
}
|
|
|
|
cEntriesProcessed++;
|
|
|
|
pIMailMsgProperties = pdefqe->pmsgGetMsg();
|
|
delete pdefqe; //we remove from list
|
|
|
|
//Release lock, so we do not hold it for external calls to submit
|
|
//the message
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
|
|
m_paqinst->DecPendingDeferred();
|
|
|
|
//This is the external verions of AQ's submit API which should
|
|
//always succeed... (unless shutdown is happening).
|
|
hr = m_paqinst->HrInternalSubmitMessage(pIMailMsgProperties);
|
|
|
|
if (FAILED(hr))
|
|
m_paqinst->HandleAQFailure(AQ_FAILURE_PROCESSING_DEFERRED_DELIVERY, hr,
|
|
pIMailMsgProperties);
|
|
|
|
pIMailMsgProperties->Release();
|
|
pIMailMsgProperties = NULL;
|
|
|
|
//Since we gave up the lock, we need to start from the front of
|
|
//the queue
|
|
m_slPrivateData.ExclusiveLock();
|
|
pli = m_liQueueHead.Flink;
|
|
|
|
}
|
|
|
|
//see if there are any other entries an set a new callback time
|
|
SetCallback();
|
|
|
|
Exit:
|
|
m_slPrivateData.ExclusiveUnlock();
|
|
}
|
|
|
|
//---[ CAQDeferredDeliveryQueue::TimerCallback ]-------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Callback function that is triggered by the retry-callback code.
|
|
// Parameters:
|
|
// IN pvContext A this ptr for the CAQDeferredDeliveryQueue
|
|
// object.
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CAQDeferredDeliveryQueue::TimerCallback(PVOID pvContext)
|
|
{
|
|
CAQDeferredDeliveryQueue *pdefq = (CAQDeferredDeliveryQueue *) pvContext;
|
|
|
|
_ASSERT(DEFERRED_DELIVERY_QUEUE_SIG == pdefq->m_dwSignature);
|
|
|
|
InterlockedDecrement((PLONG) &pdefq->m_cCallbacksPending);
|
|
pdefq->ProcessEntries();
|
|
}
|
|
|
|
|
|
//---[ CAQDeferredDeliveryQueue::SetCallback ]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Sets the retry callback if the queue is non-empty... Exclusive lock
|
|
// on queue should be held at this point.
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 1/13/99 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CAQDeferredDeliveryQueue::SetCallback()
|
|
{
|
|
CAQDeferredDeliveryQueueEntry *pdefqe = NULL;
|
|
if (!IsListEmpty(&m_liQueueHead))
|
|
{
|
|
//Set the callback time.
|
|
pdefqe = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(m_liQueueHead.Flink);
|
|
_ASSERT(pdefqe);
|
|
|
|
if (pdefqe->fSetCallback(this, m_paqinst))
|
|
InterlockedIncrement((PLONG) &m_cCallbacksPending);
|
|
|
|
}
|
|
|
|
}
|