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.
746 lines
25 KiB
746 lines
25 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// File: destmsgq.h
|
|
//
|
|
// Description:
|
|
// Header file for CDestMsgQueue class.
|
|
//
|
|
// Author: mikeswa
|
|
//
|
|
// Copyright (C) 1997 Microsoft Corporation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef _DESTMSGQ_H_
|
|
#define _DESTMSGQ_H_
|
|
|
|
#include "cmt.h"
|
|
#include <fifoq.h>
|
|
#include <rwnew.h>
|
|
#include "domain.h"
|
|
#include "aqroute.h"
|
|
#include <listmacr.h>
|
|
#include "aqutil.h"
|
|
#include "aqinst.h"
|
|
#include "aqstats.h"
|
|
#include "aqadmsvr.h"
|
|
|
|
class CLinkMsgQueue;
|
|
class CMsgRef;
|
|
class CAQSvrInst;
|
|
class CQuickList;
|
|
|
|
#define DESTMSGQ_SIG ' QMD'
|
|
#define DESTMSGRETRYQ_SIG 'QRMD'
|
|
#define EMPTY_DMQ_EXPIRE_TIME_MINUTES 1
|
|
|
|
class CDestMsgQueue;
|
|
|
|
//---[ CDestMsgRetryQueue ]----------------------------------------------------
|
|
//
|
|
//
|
|
// Hungarian: dmrq, pmdrq
|
|
//
|
|
// Provides a retry interface for requeuing messages to DMQ. If there are
|
|
// any outstanding messages for a queue, then someone must hold a reference
|
|
// to this intertace to requeue it.
|
|
//
|
|
// This class can only be created as as member of a CDestMsgQueue
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
class CDestMsgRetryQueue
|
|
{
|
|
protected:
|
|
DWORD m_dwSignature;
|
|
//Reference count for retry interface.
|
|
//Count is used to determine if it is safe to remove this DMQ from the
|
|
//DMT. This queue will only be removed when it has no messages and this
|
|
//count is zero. The count represents the total number of
|
|
//messages pending Ack on this queue. This is held while the message
|
|
//is sent over the wire, and we determine if the message needs
|
|
//to be retried.
|
|
DWORD m_cRetryReferenceCount;
|
|
CDestMsgQueue *m_pdmq;
|
|
|
|
friend class CDestMsgQueue;
|
|
|
|
CDestMsgRetryQueue();
|
|
~CDestMsgRetryQueue() {_ASSERT(!m_cRetryReferenceCount);};
|
|
public:
|
|
|
|
DWORD AddRef()
|
|
{return InterlockedIncrement((PLONG) &m_cRetryReferenceCount);};
|
|
DWORD Release()
|
|
{return InterlockedDecrement((PLONG) &m_cRetryReferenceCount);};
|
|
|
|
HRESULT HrRetryMsg(IN CMsgRef *pmsgref); //put message on retry queue
|
|
|
|
VOID CheckForStaleMsgsNextDSNGenerationPass();
|
|
|
|
};
|
|
|
|
//---[ CDestMsgQueue ]---------------------------------------------------------
|
|
//
|
|
//
|
|
// Hungarian: dmq, pmdq
|
|
//
|
|
// Provides a priority queue of MsgRef's for the CMT
|
|
//-----------------------------------------------------------------------------
|
|
class CDestMsgQueue :
|
|
public IQueueAdminAction,
|
|
public IQueueAdminQueue,
|
|
public CBaseObject
|
|
{
|
|
public:
|
|
CDestMsgQueue(CAQSvrInst *paqinst,
|
|
CAQMessageType *paqmtMessageType, IMessageRouter *pIMessageRouter);
|
|
~CDestMsgQueue();
|
|
|
|
HRESULT HrInitialize(IN CDomainMapping *pdmap);
|
|
|
|
HRESULT HrDeinitialize();
|
|
|
|
//Set the routing information for this domain
|
|
void SetRouteInfo(CLinkMsgQueue *plmq);
|
|
|
|
//Queue operations
|
|
inline HRESULT HrEnqueueMsg(IN CMsgRef *pmsgref, BOOL fOwnsTypeRef);
|
|
|
|
//Dequeue a message for delivery. All OUT params are ref-counted, and
|
|
//caller is responsable for releasing
|
|
HRESULT HrDequeueMsg(
|
|
IN DWORD dwLowestPriority, //Lowest priority message that will be dequeued
|
|
OUT CMsgRef **ppmsgref, //MsgRef dequeued
|
|
OUT CDestMsgRetryQueue **ppdmrq); //retry interface (optional)
|
|
|
|
inline void GetDomainMapping(OUT CDomainMapping **ppdmap);
|
|
|
|
//Remerge the retry queue with queues & generate DSNs if required
|
|
HRESULT HrGenerateDSNsIfNecessary(IN CQuickList *pqlQueues,
|
|
IN HRESULT hrConnectionStatus,
|
|
IN OUT DWORD *pdwContext);
|
|
|
|
//functions used to manipulate lists of queues
|
|
inline CAQMessageType *paqmtGetMessageType();
|
|
inline IMessageRouter *pIMessageRouterGetRouter();
|
|
inline BOOL fIsSameMessageType(CAQMessageType *paqmt);
|
|
static inline CDestMsgQueue *pdmqIsSameMessageType(
|
|
CAQMessageType *paqmt,
|
|
PLIST_ENTRY pli);
|
|
|
|
static inline CDestMsgQueue *pdmqGetDMQFromDomainListEntry(PLIST_ENTRY pli);
|
|
|
|
//Accessor functions for DomainEntry list
|
|
inline void InsertQueueInDomainList(PLIST_ENTRY pliHead);
|
|
inline void RemoveQueueFromDomainList();
|
|
inline PLIST_ENTRY pliGetNextDomainListEntry();
|
|
|
|
//Accessor functions for "empty-queue" list
|
|
void MarkQueueEmptyIfNecessary();
|
|
inline void InsertQueueInEmptyQueueList(PLIST_ENTRY pliHead);
|
|
inline void RemoveQueueFromEmptyQueueList();
|
|
inline PLIST_ENTRY pliGetNextEmptyQueueListEntry();
|
|
inline DWORD dwGetDMQState();
|
|
inline void MarkDMQInvalid();
|
|
void RemoveDMQFromLink(BOOL fNotifyLink);
|
|
|
|
//Addref and get link (returns NULL if not routed)
|
|
CLinkMsgQueue *plmqGetLink();
|
|
|
|
static inline CDestMsgQueue *pdmqGetDMQFromEmptyListEntry(PLIST_ENTRY pli);
|
|
|
|
//Method that external users can use to verify the signature for
|
|
//DMQ's passed as contexts or LIST_ENTRY's
|
|
inline void AssertSignature() {_ASSERT(DESTMSGQ_SIG == m_dwSignature);};
|
|
|
|
static HRESULT HrWalkDMQForDSN(IN CMsgRef *pmsgref, IN PVOID pvContext,
|
|
OUT BOOL *pfContinue, OUT BOOL *pfDelete);
|
|
|
|
static HRESULT HrWalkQueueForShutdown(IN CMsgRef *pmsgref,
|
|
IN PVOID pvContext, OUT BOOL *pfContinue,
|
|
OUT BOOL *pfDelete);
|
|
static HRESULT HrWalkRetryQueueForShutdown(IN CMsgRef *pmsgref,
|
|
IN PVOID pvContext, OUT BOOL *pfContinue,
|
|
OUT BOOL *pfDelete);
|
|
|
|
//Called by link to get & set link context
|
|
inline PVOID pvGetLinkContext() {return m_pvLinkContext;};
|
|
inline void SetLinkContext(IN PVOID pvLinkContext) {m_pvLinkContext = pvLinkContext;};
|
|
|
|
inline BOOL fIsRouted() {return (m_plmq ? TRUE : FALSE);};
|
|
|
|
//update stats after adding or removing a message
|
|
//This should only be called by member functions and queue iterators
|
|
void UpdateMsgStats(
|
|
IN CMsgRef *pmsgref, //Msg that was added/removed
|
|
IN BOOL fAdd); //TRUE => message was added
|
|
|
|
//update stats after adding or removing a message on retry queue
|
|
void UpdateRetryStats(
|
|
IN BOOL fAdd); //TRUE => message was added
|
|
|
|
|
|
|
|
//Returns an approximation of the age of the oldest message in the queue
|
|
inline void GetOldestMsg(FILETIME *pft);
|
|
|
|
//Walk retry queue and remerge messages into normal queues
|
|
void MergeRetryQueue();
|
|
|
|
void SendLinkStateNotification(void);
|
|
|
|
//Returns TRUE if queue is routed remotely.
|
|
BOOL fIsRemote();
|
|
|
|
//Describes DMQ state. Returned by dwGetDMQState and cached in m_dwFlags
|
|
enum
|
|
{
|
|
DMQ_INVALID = 0x00000001, //This DMQ is no longer valid
|
|
DMQ_IN_EMPTY_QUEUE_LIST = 0x00000002, //This DMQ is in empty list
|
|
DMQ_SHUTDOWN_SIGNALED = 0x00000004, //Shutdown has been signaled
|
|
DMQ_EMPTY = 0x00000010, //DMQ has no messages
|
|
DMQ_EXPIRED = 0x00000020, //DMQ has expired in empty list
|
|
DMQ_QUEUE_ADMIN_OP_PENDING = 0x00000040, //A queue admin operation is pending
|
|
DMQ_UPDATING_OLDEST_TIME = 0x00000100, //Spinlock for updating oldest time
|
|
DMQ_CHECK_FOR_STALE_MSGS = 0x00000200, //Do check filehandles during DSN gen
|
|
};
|
|
|
|
//
|
|
// Since queues start out empty... there some error paths that can cause a queue
|
|
// to be marked as empty, but not actually put in the empty list. We should
|
|
// clean these up during reset routes. This tells us if it is safe to do so.
|
|
//
|
|
BOOL fIsEmptyAndAbandoned()
|
|
{
|
|
return (!m_aqstats.m_cMsgs &&
|
|
!m_dmrq.m_cRetryReferenceCount &&
|
|
!m_fqRetryQueue.cGetCount() &&
|
|
(m_dwFlags & DMQ_EMPTY) && !(m_dwFlags & DMQ_IN_EMPTY_QUEUE_LIST));
|
|
}
|
|
|
|
public: //IUnknown
|
|
STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj);
|
|
STDMETHOD_(ULONG, AddRef)(void) {return CBaseObject::AddRef();};
|
|
STDMETHOD_(ULONG, Release)(void) {return CBaseObject::Release();};
|
|
|
|
public: //IQueueAdminAction
|
|
STDMETHOD(HrApplyQueueAdminFunction)(
|
|
IQueueAdminMessageFilter *pIQueueAdminMessageFilter);
|
|
|
|
STDMETHOD(HrApplyActionToMessage)(
|
|
IUnknown *pIUnknownMsg,
|
|
MESSAGE_ACTION ma,
|
|
PVOID pvContext,
|
|
BOOL *pfShouldDelete);
|
|
|
|
STDMETHOD_(BOOL, fMatchesID)
|
|
(QUEUELINK_ID *QueueLinkID);
|
|
|
|
STDMETHOD(QuerySupportedActions)(DWORD *pdwSupportedActions,
|
|
DWORD *pdwSupportedFilterFlags)
|
|
{
|
|
return QueryDefaultSupportedActions(pdwSupportedActions,
|
|
pdwSupportedFilterFlags);
|
|
};
|
|
|
|
public: //IQueueAdminQueue
|
|
STDMETHOD(HrGetQueueInfo)(
|
|
QUEUE_INFO *pliQueueInfo);
|
|
|
|
STDMETHOD(HrGetQueueID)(
|
|
QUEUELINK_ID *pQueueID);
|
|
|
|
public:
|
|
// Return # of failed messages: They are not counted in the m_aqstats of the DMQ
|
|
DWORD cGetFailedMsgs() { return m_fqRetryQueue.cGetCount(); }
|
|
|
|
// Set error code from routing
|
|
void SetRoutingDiagnostic(HRESULT hr) { m_hrRoutingDiag = hr; }
|
|
|
|
protected:
|
|
DWORD m_dwSignature;
|
|
DWORD m_dwFlags;
|
|
LIST_ENTRY m_liDomainEntryDMQs;
|
|
|
|
//Type of message (as returned by routing) that is on this queue.
|
|
CAQMessageType m_aqmt;
|
|
DWORD m_cMessageTypeRefs;
|
|
|
|
IMessageRouter *m_pIMessageRouter;
|
|
|
|
//Errorcode from routing. This is set to S_OK if there's no errorcode.
|
|
//Currently this indicates the reason why a destination is unreachable.
|
|
HRESULT m_hrRoutingDiag;
|
|
|
|
//Members used for DMQ deletion (maintaining a list of empty queues)
|
|
LIST_ENTRY m_liEmptyDMQs;
|
|
FILETIME m_ftEmptyExpireTime; //expiration time of empty DMQ
|
|
DWORD m_cRemovedFromEmptyList; //# of times on list w/o
|
|
//being deleted.
|
|
|
|
CShareLockNH m_slPrivateData; //Share lock to protect access to m_rgpfqQueues
|
|
|
|
//The following three fields encapsulate all of the routing data
|
|
//for this DMQ. The actual routing data is the pointer to the link,
|
|
//and the context is used by the link to optimize access
|
|
CLinkMsgQueue *m_plmq;
|
|
PVOID m_pvLinkContext;
|
|
|
|
CAQSvrInst *m_paqinst;
|
|
|
|
//Array of FIFO queues (used to make a priority queue
|
|
CFifoQueue<CMsgRef *> *m_rgpfqQueues[NUM_PRIORITIES];
|
|
|
|
//Retry Qeueue for failed messages
|
|
CFifoQueue<CMsgRef *> m_fqRetryQueue;
|
|
|
|
//class used to store stats
|
|
CAQStats m_aqstats;
|
|
|
|
//which domain is represented in this destination
|
|
CDomainMapping m_dmap;
|
|
|
|
FILETIME m_ftOldest;
|
|
|
|
CDestMsgRetryQueue m_dmrq;
|
|
|
|
DWORD m_cCurrentThreadsEnqueuing;
|
|
protected: //internal interfaces
|
|
|
|
//Add Message to front or back of priority queues
|
|
HRESULT HrAddMsg(
|
|
IN CMsgRef *pmsgref, //Msg to add
|
|
IN BOOL fEnqueue, //TRUE => enqueue,FALSE => requeue
|
|
IN BOOL fNotify); //TRUE => send notification if needed
|
|
|
|
void UpdateOldest(FILETIME *pft);
|
|
|
|
//Callers must use CDestMsgRetryQueueClass
|
|
HRESULT HrRetryMsg(IN CMsgRef *pmsgref); //put message on retry queue
|
|
|
|
friend class CDestMsgRetryQueue;
|
|
};
|
|
|
|
//---[ CDestMsgQueue::HrEnqueueMsg ]-------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Enqueues a message for remote delivery for a given final destination
|
|
// and message type
|
|
// Parameters:
|
|
// pmsgref AQ Message Reference to enqueue
|
|
// fOwnsTypeRef TRUE if this queue is responsible for calling
|
|
// IMessageRouter::ReleaseMessageType
|
|
// Returns:
|
|
// S_OK on success
|
|
// Error code from HrAddMsg
|
|
// History:
|
|
// 5/21/98 - MikeSwa added fOwnsTypeRef
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CDestMsgQueue::HrEnqueueMsg(IN CMsgRef *pmsgref, BOOL fOwnsTypeRef)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = HrAddMsg(pmsgref, TRUE, TRUE);
|
|
|
|
if (fOwnsTypeRef && SUCCEEDED(hr))
|
|
InterlockedIncrement((PLONG) &m_cMessageTypeRefs);
|
|
|
|
//Callers should have shutdown lock
|
|
_ASSERT(!(m_dwFlags & (DMQ_INVALID | DMQ_SHUTDOWN_SIGNALED)));
|
|
return hr;
|
|
}
|
|
|
|
|
|
//---[ CDestMsgQueue::paqmtGetMessageType ]------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Get the message type for this queue
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// CAQMessageType * of this queue's message type
|
|
// History:
|
|
// 5/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CAQMessageType *CDestMsgQueue::paqmtGetMessageType()
|
|
{
|
|
return (&m_aqmt);
|
|
}
|
|
|
|
//---[ CDestMsgQueue::fIsSameMessageType ]-------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Tells if the message type of this queue is the same as the given
|
|
// message type.
|
|
// Parameters:
|
|
// paqmt - ptr to CAQMessageType to test
|
|
// Returns:
|
|
// TRUE if they match, FALSE if they do not
|
|
// History:
|
|
// 5/26/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CDestMsgQueue::fIsSameMessageType(CAQMessageType *paqmt)
|
|
{
|
|
_ASSERT(paqmt);
|
|
return m_aqmt.fIsEqual(paqmt);
|
|
}
|
|
|
|
//---[ CDestMsgQueue::pdmqIsSameMessageType ]----------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// STATIC function used to determine if a LIST_ENTRY refers to a
|
|
// CDestMsgQueue with a given message type.
|
|
// Parameters:
|
|
// paqmt - ptr to CAQMessageType to check against
|
|
// pli - ptr to list entry to check (must refer to a CDestMsgQueue)
|
|
// Returns:
|
|
// Ptr to CDestMsgQueue if LIST_ENTRY refers to a CDestMsgQueue with
|
|
// the given message type.
|
|
// NULL if no match is not found
|
|
// History:
|
|
// 5/27/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDestMsgQueue *CDestMsgQueue::pdmqIsSameMessageType(
|
|
CAQMessageType *paqmt,
|
|
PLIST_ENTRY pli)
|
|
{
|
|
CDestMsgQueue *pdmq = NULL;
|
|
pdmq = CONTAINING_RECORD(pli, CDestMsgQueue, m_liDomainEntryDMQs);
|
|
_ASSERT(DESTMSGQ_SIG == pdmq->m_dwSignature);
|
|
|
|
//if not the same message type return NULL
|
|
if (!pdmq->fIsSameMessageType(paqmt))
|
|
pdmq = NULL;
|
|
|
|
return pdmq;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::pdmqGetDMQFromDomainListEntry ]--------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns the CDestMsgQueue associated with a list entry
|
|
// Parameters:
|
|
// IN pli ptr to list entry to get CDestMsgQueue from
|
|
// Returns:
|
|
// ptr to CDestMsgQueue
|
|
// History:
|
|
// 5/28/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDestMsgQueue *CDestMsgQueue::pdmqGetDMQFromDomainListEntry(PLIST_ENTRY pli)
|
|
{
|
|
_ASSERT(DESTMSGQ_SIG == (CONTAINING_RECORD(pli, CDestMsgQueue, m_liDomainEntryDMQs))->m_dwSignature);
|
|
return (CONTAINING_RECORD(pli, CDestMsgQueue, m_liDomainEntryDMQs));
|
|
}
|
|
|
|
//---[ CDestMsgQueue::InsertQueueInDomainList ]---------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Inserts this CDestMsgQueue into the given linked list of queues
|
|
// Parameters:
|
|
// pliHead - PLIST_ENTRY for list head
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 5/27/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::InsertQueueInDomainList(PLIST_ENTRY pliHead)
|
|
{
|
|
_ASSERT(NULL == m_liDomainEntryDMQs.Flink);
|
|
_ASSERT(NULL == m_liDomainEntryDMQs.Blink);
|
|
InsertHeadList(pliHead, &m_liDomainEntryDMQs);
|
|
}
|
|
|
|
//---[ CDestMsgQueue::RemoveQueueFromDomainList ]-------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Removes this queue from a list of queues
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 5/27/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::RemoveQueueFromDomainList()
|
|
{
|
|
RemoveEntryList(&m_liDomainEntryDMQs);
|
|
m_liDomainEntryDMQs.Flink = NULL;
|
|
m_liDomainEntryDMQs.Blink = NULL;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::pliGetNextDomainListEntry ]-------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Gets the pointer to the next list entry for this queue.
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// The Flink of the queues LIST_ENTRY
|
|
// History:
|
|
// 6/16/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
PLIST_ENTRY CDestMsgQueue::pliGetNextDomainListEntry()
|
|
{
|
|
return m_liDomainEntryDMQs.Flink;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::InsertQueueInEmptyQueueList ]----------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Inserts queue at *tail* of DMT empty queue list. The queue that has
|
|
// been empty the longest should be at the As with the other EmptyQueue
|
|
// list functions this is called by the DMT, when it has the appropriate
|
|
// lock for the head of the list.
|
|
//
|
|
// Upon insertion, an "expire time" is stamped on the queue. If the queue
|
|
// is still in the list, then it is a candidate for deletion, and will be
|
|
// delete the next time the DMT looks at the queue (everytime HrMapDomain
|
|
// is called).
|
|
//
|
|
// NOTE"We need to make sure this function is thread-safe. Since the
|
|
// DMQ lock is aquired exclusively before this is called, we know that
|
|
// no one will ENQUEUE a messsage. This function call is tiggered after
|
|
// the retry queues are emptied when a connection finished, so we can
|
|
// also ensure that no one will call this while there are messages to
|
|
// retry.
|
|
|
|
// It is however (remotely) possible for 2 threads to finish connections
|
|
// for this queue and thus cause 2 threads to be in this function.
|
|
// The thread that successfully modified the EMPTY bit will be allowed
|
|
// to add the queue to the list.
|
|
// Parameters:
|
|
// IN pliHead The head of the list to insert into
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/11/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::InsertQueueInEmptyQueueList(PLIST_ENTRY pliHead)
|
|
{
|
|
_ASSERT(m_paqinst);
|
|
|
|
//Now that we have the exclusive lock recheck to make sure there are no messages
|
|
if (m_aqstats.m_cMsgs || m_fqRetryQueue.cGetCount())
|
|
return;
|
|
|
|
//Attempt to set the DMQ_EMPTY bit
|
|
if (DMQ_EMPTY & dwInterlockedSetBits(&m_dwFlags, DMQ_EMPTY))
|
|
{
|
|
//Another thread has set it, we cannot modify the LIST_ENTRY
|
|
return;
|
|
}
|
|
|
|
//If it is already in queue, that means that the queue has gone
|
|
//from empty to non-empty to empty. Insert at tail of list with new time
|
|
if (m_dwFlags & DMQ_IN_EMPTY_QUEUE_LIST)
|
|
{
|
|
_ASSERT(NULL != m_liEmptyDMQs.Flink);
|
|
_ASSERT(NULL != m_liEmptyDMQs.Blink);
|
|
RemoveEntryList(&m_liEmptyDMQs);
|
|
m_cRemovedFromEmptyList++;
|
|
}
|
|
else
|
|
{
|
|
_ASSERT(NULL == m_liEmptyDMQs.Flink);
|
|
_ASSERT(NULL == m_liEmptyDMQs.Blink);
|
|
}
|
|
|
|
//Get expire time for this queue
|
|
m_paqinst->GetExpireTime(EMPTY_DMQ_EXPIRE_TIME_MINUTES,
|
|
&m_ftEmptyExpireTime, NULL);
|
|
|
|
//Mark queue as in empty queue
|
|
dwInterlockedSetBits(&m_dwFlags, DMQ_IN_EMPTY_QUEUE_LIST);
|
|
|
|
//Insert into queue
|
|
InsertTailList(pliHead, &m_liEmptyDMQs);
|
|
_ASSERT(pliHead->Blink == &m_liEmptyDMQs);
|
|
_ASSERT(!m_aqstats.m_cMsgs); //No other thread should be able to add msgs
|
|
}
|
|
|
|
//---[ DestMsgQueue::RemoveQueueFromEmptyQueueList ]---------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Removed the queue from the empty list. Caller *must* have DMT lock
|
|
// to call this. DMQ will not call this directly, but will call into
|
|
// DMT .
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/11/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::RemoveQueueFromEmptyQueueList()
|
|
{
|
|
RemoveEntryList(&m_liEmptyDMQs);
|
|
|
|
//Increment count now that queue is being removed from empty list
|
|
m_cRemovedFromEmptyList++;
|
|
|
|
//Mark queue as not in empty queue
|
|
dwInterlockedUnsetBits(&m_dwFlags, DMQ_IN_EMPTY_QUEUE_LIST);
|
|
|
|
m_liEmptyDMQs.Flink = NULL;
|
|
m_liEmptyDMQs.Blink = NULL;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::pliGetNextEmptyQueueListEntry ]--------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Gets next queue entry in empty list.
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// Next entry pointed to by list entry
|
|
// History:
|
|
// 9/11/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
PLIST_ENTRY CDestMsgQueue::pliGetNextEmptyQueueListEntry()
|
|
{
|
|
return m_liEmptyDMQs.Flink;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::dwGetDMQState ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns the state of the DMQ and caches that state in m_dwFlags. May
|
|
// update DMQ_EXPIRED if DMQ is in empty list and it has expired
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// Current DMQ state
|
|
// History:
|
|
// 9/12/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DWORD CDestMsgQueue::dwGetDMQState()
|
|
{
|
|
_ASSERT(DESTMSGQ_SIG == m_dwSignature);
|
|
_ASSERT(m_paqinst);
|
|
|
|
if (DMQ_IN_EMPTY_QUEUE_LIST & m_dwFlags)
|
|
{
|
|
//If it is empty and not expired..check if expired
|
|
if ((DMQ_EMPTY & m_dwFlags) && !(DMQ_EXPIRED & m_dwFlags))
|
|
{
|
|
if (m_paqinst->fInPast(&m_ftEmptyExpireTime, NULL))
|
|
dwInterlockedSetBits(&m_dwFlags, DMQ_EXPIRED);
|
|
}
|
|
}
|
|
|
|
return m_dwFlags;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::MarkDMQInvalid ]------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Marks this queue as invalid. Queue *must* be empty for this to happen
|
|
// Parameters:
|
|
// -
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/12/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::MarkDMQInvalid()
|
|
{
|
|
_ASSERT(DESTMSGQ_SIG == m_dwSignature);
|
|
_ASSERT(DMQ_EMPTY & m_dwFlags);
|
|
dwInterlockedSetBits(&m_dwFlags, DMQ_INVALID);
|
|
}
|
|
|
|
//---[ CDestMsgQueue::pdmqGetDMQFromEmptyListEntry ]---------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns the DMQ corresponding to a given Empty Queue LIST_ENTRY.
|
|
//
|
|
// Will assert that DMQ signature is valid
|
|
// Parameters:
|
|
// IN pli Pointer to LIST_ENTRY for queue
|
|
// Returns:
|
|
//
|
|
// History:
|
|
// 9/12/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDestMsgQueue *CDestMsgQueue::pdmqGetDMQFromEmptyListEntry(PLIST_ENTRY pli)
|
|
{
|
|
_ASSERT(DESTMSGQ_SIG == (CONTAINING_RECORD(pli, CDestMsgQueue, m_liEmptyDMQs))->m_dwSignature);
|
|
return (CONTAINING_RECORD(pli, CDestMsgQueue, m_liEmptyDMQs));
|
|
}
|
|
|
|
//---[ CDestMsgQueue::GetDomainMapping ]---------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Returns the domain mapping for this queue.
|
|
// Parameters:
|
|
// OUT ppdmap Returned domain mapping
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 9/14/98 - MikeSwa Modified to not have a return value
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::GetDomainMapping(OUT CDomainMapping **ppdmap)
|
|
{
|
|
_ASSERT(ppdmap);
|
|
*ppdmap = &m_dmap;
|
|
}
|
|
|
|
IMessageRouter *CDestMsgQueue::pIMessageRouterGetRouter()
|
|
{
|
|
return m_pIMessageRouter;
|
|
}
|
|
|
|
//---[ CDestMsgQueue::GetOldestMsg ]-------------------------------------------
|
|
//
|
|
//
|
|
// Description:
|
|
// Retruns an approximation of the oldest message in the queue
|
|
// Parameters:
|
|
// OUT pft FILTIME of "oldest" Messate
|
|
// Returns:
|
|
// -
|
|
// History:
|
|
// 12/13/98 - MikeSwa Created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CDestMsgQueue::GetOldestMsg(FILETIME *pft)
|
|
{
|
|
_ASSERT(pft);
|
|
if (m_aqstats.m_cMsgs)
|
|
memcpy(pft, &m_ftOldest, sizeof(FILETIME));
|
|
else
|
|
ZeroMemory(pft, sizeof (FILETIME));
|
|
}
|
|
|
|
#endif //_DESTMSGQ_H_
|