Leaked source code of windows server 2003
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.
|
|
//-----------------------------------------------------------------------------
//
//
// 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);
}
}
|