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.

746 lines
25 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: destmsgq.h
  5. //
  6. // Description:
  7. // Header file for CDestMsgQueue class.
  8. //
  9. // Author: mikeswa
  10. //
  11. // Copyright (C) 1997 Microsoft Corporation
  12. //
  13. //-----------------------------------------------------------------------------
  14. #ifndef _DESTMSGQ_H_
  15. #define _DESTMSGQ_H_
  16. #include "cmt.h"
  17. #include <fifoq.h>
  18. #include <rwnew.h>
  19. #include "domain.h"
  20. #include "aqroute.h"
  21. #include <listmacr.h>
  22. #include "aqutil.h"
  23. #include "aqinst.h"
  24. #include "aqstats.h"
  25. #include "aqadmsvr.h"
  26. class CLinkMsgQueue;
  27. class CMsgRef;
  28. class CAQSvrInst;
  29. class CQuickList;
  30. #define DESTMSGQ_SIG ' QMD'
  31. #define DESTMSGRETRYQ_SIG 'QRMD'
  32. #define EMPTY_DMQ_EXPIRE_TIME_MINUTES 1
  33. class CDestMsgQueue;
  34. //---[ CDestMsgRetryQueue ]----------------------------------------------------
  35. //
  36. //
  37. // Hungarian: dmrq, pmdrq
  38. //
  39. // Provides a retry interface for requeuing messages to DMQ. If there are
  40. // any outstanding messages for a queue, then someone must hold a reference
  41. // to this intertace to requeue it.
  42. //
  43. // This class can only be created as as member of a CDestMsgQueue
  44. //
  45. //-----------------------------------------------------------------------------
  46. class CDestMsgRetryQueue
  47. {
  48. protected:
  49. DWORD m_dwSignature;
  50. //Reference count for retry interface.
  51. //Count is used to determine if it is safe to remove this DMQ from the
  52. //DMT. This queue will only be removed when it has no messages and this
  53. //count is zero. The count represents the total number of
  54. //messages pending Ack on this queue. This is held while the message
  55. //is sent over the wire, and we determine if the message needs
  56. //to be retried.
  57. DWORD m_cRetryReferenceCount;
  58. CDestMsgQueue *m_pdmq;
  59. friend class CDestMsgQueue;
  60. CDestMsgRetryQueue();
  61. ~CDestMsgRetryQueue() {_ASSERT(!m_cRetryReferenceCount);};
  62. public:
  63. DWORD AddRef()
  64. {return InterlockedIncrement((PLONG) &m_cRetryReferenceCount);};
  65. DWORD Release()
  66. {return InterlockedDecrement((PLONG) &m_cRetryReferenceCount);};
  67. HRESULT HrRetryMsg(IN CMsgRef *pmsgref); //put message on retry queue
  68. VOID CheckForStaleMsgsNextDSNGenerationPass();
  69. };
  70. //---[ CDestMsgQueue ]---------------------------------------------------------
  71. //
  72. //
  73. // Hungarian: dmq, pmdq
  74. //
  75. // Provides a priority queue of MsgRef's for the CMT
  76. //-----------------------------------------------------------------------------
  77. class CDestMsgQueue :
  78. public IQueueAdminAction,
  79. public IQueueAdminQueue,
  80. public CBaseObject
  81. {
  82. public:
  83. CDestMsgQueue(CAQSvrInst *paqinst,
  84. CAQMessageType *paqmtMessageType, IMessageRouter *pIMessageRouter);
  85. ~CDestMsgQueue();
  86. HRESULT HrInitialize(IN CDomainMapping *pdmap);
  87. HRESULT HrDeinitialize();
  88. //Set the routing information for this domain
  89. void SetRouteInfo(CLinkMsgQueue *plmq);
  90. //Queue operations
  91. inline HRESULT HrEnqueueMsg(IN CMsgRef *pmsgref, BOOL fOwnsTypeRef);
  92. //Dequeue a message for delivery. All OUT params are ref-counted, and
  93. //caller is responsable for releasing
  94. HRESULT HrDequeueMsg(
  95. IN DWORD dwLowestPriority, //Lowest priority message that will be dequeued
  96. OUT CMsgRef **ppmsgref, //MsgRef dequeued
  97. OUT CDestMsgRetryQueue **ppdmrq); //retry interface (optional)
  98. inline void GetDomainMapping(OUT CDomainMapping **ppdmap);
  99. //Remerge the retry queue with queues & generate DSNs if required
  100. HRESULT HrGenerateDSNsIfNecessary(IN CQuickList *pqlQueues,
  101. IN HRESULT hrConnectionStatus,
  102. IN OUT DWORD *pdwContext);
  103. //functions used to manipulate lists of queues
  104. inline CAQMessageType *paqmtGetMessageType();
  105. inline IMessageRouter *pIMessageRouterGetRouter();
  106. inline BOOL fIsSameMessageType(CAQMessageType *paqmt);
  107. static inline CDestMsgQueue *pdmqIsSameMessageType(
  108. CAQMessageType *paqmt,
  109. PLIST_ENTRY pli);
  110. static inline CDestMsgQueue *pdmqGetDMQFromDomainListEntry(PLIST_ENTRY pli);
  111. //Accessor functions for DomainEntry list
  112. inline void InsertQueueInDomainList(PLIST_ENTRY pliHead);
  113. inline void RemoveQueueFromDomainList();
  114. inline PLIST_ENTRY pliGetNextDomainListEntry();
  115. //Accessor functions for "empty-queue" list
  116. void MarkQueueEmptyIfNecessary();
  117. inline void InsertQueueInEmptyQueueList(PLIST_ENTRY pliHead);
  118. inline void RemoveQueueFromEmptyQueueList();
  119. inline PLIST_ENTRY pliGetNextEmptyQueueListEntry();
  120. inline DWORD dwGetDMQState();
  121. inline void MarkDMQInvalid();
  122. void RemoveDMQFromLink(BOOL fNotifyLink);
  123. //Addref and get link (returns NULL if not routed)
  124. CLinkMsgQueue *plmqGetLink();
  125. static inline CDestMsgQueue *pdmqGetDMQFromEmptyListEntry(PLIST_ENTRY pli);
  126. //Method that external users can use to verify the signature for
  127. //DMQ's passed as contexts or LIST_ENTRY's
  128. inline void AssertSignature() {_ASSERT(DESTMSGQ_SIG == m_dwSignature);};
  129. static HRESULT HrWalkDMQForDSN(IN CMsgRef *pmsgref, IN PVOID pvContext,
  130. OUT BOOL *pfContinue, OUT BOOL *pfDelete);
  131. static HRESULT HrWalkQueueForShutdown(IN CMsgRef *pmsgref,
  132. IN PVOID pvContext, OUT BOOL *pfContinue,
  133. OUT BOOL *pfDelete);
  134. static HRESULT HrWalkRetryQueueForShutdown(IN CMsgRef *pmsgref,
  135. IN PVOID pvContext, OUT BOOL *pfContinue,
  136. OUT BOOL *pfDelete);
  137. //Called by link to get & set link context
  138. inline PVOID pvGetLinkContext() {return m_pvLinkContext;};
  139. inline void SetLinkContext(IN PVOID pvLinkContext) {m_pvLinkContext = pvLinkContext;};
  140. inline BOOL fIsRouted() {return (m_plmq ? TRUE : FALSE);};
  141. //update stats after adding or removing a message
  142. //This should only be called by member functions and queue iterators
  143. void UpdateMsgStats(
  144. IN CMsgRef *pmsgref, //Msg that was added/removed
  145. IN BOOL fAdd); //TRUE => message was added
  146. //update stats after adding or removing a message on retry queue
  147. void UpdateRetryStats(
  148. IN BOOL fAdd); //TRUE => message was added
  149. //Returns an approximation of the age of the oldest message in the queue
  150. inline void GetOldestMsg(FILETIME *pft);
  151. //Walk retry queue and remerge messages into normal queues
  152. void MergeRetryQueue();
  153. void SendLinkStateNotification(void);
  154. //Returns TRUE if queue is routed remotely.
  155. BOOL fIsRemote();
  156. //Describes DMQ state. Returned by dwGetDMQState and cached in m_dwFlags
  157. enum
  158. {
  159. DMQ_INVALID = 0x00000001, //This DMQ is no longer valid
  160. DMQ_IN_EMPTY_QUEUE_LIST = 0x00000002, //This DMQ is in empty list
  161. DMQ_SHUTDOWN_SIGNALED = 0x00000004, //Shutdown has been signaled
  162. DMQ_EMPTY = 0x00000010, //DMQ has no messages
  163. DMQ_EXPIRED = 0x00000020, //DMQ has expired in empty list
  164. DMQ_QUEUE_ADMIN_OP_PENDING = 0x00000040, //A queue admin operation is pending
  165. DMQ_UPDATING_OLDEST_TIME = 0x00000100, //Spinlock for updating oldest time
  166. DMQ_CHECK_FOR_STALE_MSGS = 0x00000200, //Do check filehandles during DSN gen
  167. };
  168. //
  169. // Since queues start out empty... there some error paths that can cause a queue
  170. // to be marked as empty, but not actually put in the empty list. We should
  171. // clean these up during reset routes. This tells us if it is safe to do so.
  172. //
  173. BOOL fIsEmptyAndAbandoned()
  174. {
  175. return (!m_aqstats.m_cMsgs &&
  176. !m_dmrq.m_cRetryReferenceCount &&
  177. !m_fqRetryQueue.cGetCount() &&
  178. (m_dwFlags & DMQ_EMPTY) && !(m_dwFlags & DMQ_IN_EMPTY_QUEUE_LIST));
  179. }
  180. public: //IUnknown
  181. STDMETHOD(QueryInterface)(REFIID riid, LPVOID * ppvObj);
  182. STDMETHOD_(ULONG, AddRef)(void) {return CBaseObject::AddRef();};
  183. STDMETHOD_(ULONG, Release)(void) {return CBaseObject::Release();};
  184. public: //IQueueAdminAction
  185. STDMETHOD(HrApplyQueueAdminFunction)(
  186. IQueueAdminMessageFilter *pIQueueAdminMessageFilter);
  187. STDMETHOD(HrApplyActionToMessage)(
  188. IUnknown *pIUnknownMsg,
  189. MESSAGE_ACTION ma,
  190. PVOID pvContext,
  191. BOOL *pfShouldDelete);
  192. STDMETHOD_(BOOL, fMatchesID)
  193. (QUEUELINK_ID *QueueLinkID);
  194. STDMETHOD(QuerySupportedActions)(DWORD *pdwSupportedActions,
  195. DWORD *pdwSupportedFilterFlags)
  196. {
  197. return QueryDefaultSupportedActions(pdwSupportedActions,
  198. pdwSupportedFilterFlags);
  199. };
  200. public: //IQueueAdminQueue
  201. STDMETHOD(HrGetQueueInfo)(
  202. QUEUE_INFO *pliQueueInfo);
  203. STDMETHOD(HrGetQueueID)(
  204. QUEUELINK_ID *pQueueID);
  205. public:
  206. // Return # of failed messages: They are not counted in the m_aqstats of the DMQ
  207. DWORD cGetFailedMsgs() { return m_fqRetryQueue.cGetCount(); }
  208. // Set error code from routing
  209. void SetRoutingDiagnostic(HRESULT hr) { m_hrRoutingDiag = hr; }
  210. protected:
  211. DWORD m_dwSignature;
  212. DWORD m_dwFlags;
  213. LIST_ENTRY m_liDomainEntryDMQs;
  214. //Type of message (as returned by routing) that is on this queue.
  215. CAQMessageType m_aqmt;
  216. DWORD m_cMessageTypeRefs;
  217. IMessageRouter *m_pIMessageRouter;
  218. //Errorcode from routing. This is set to S_OK if there's no errorcode.
  219. //Currently this indicates the reason why a destination is unreachable.
  220. HRESULT m_hrRoutingDiag;
  221. //Members used for DMQ deletion (maintaining a list of empty queues)
  222. LIST_ENTRY m_liEmptyDMQs;
  223. FILETIME m_ftEmptyExpireTime; //expiration time of empty DMQ
  224. DWORD m_cRemovedFromEmptyList; //# of times on list w/o
  225. //being deleted.
  226. CShareLockNH m_slPrivateData; //Share lock to protect access to m_rgpfqQueues
  227. //The following three fields encapsulate all of the routing data
  228. //for this DMQ. The actual routing data is the pointer to the link,
  229. //and the context is used by the link to optimize access
  230. CLinkMsgQueue *m_plmq;
  231. PVOID m_pvLinkContext;
  232. CAQSvrInst *m_paqinst;
  233. //Array of FIFO queues (used to make a priority queue
  234. CFifoQueue<CMsgRef *> *m_rgpfqQueues[NUM_PRIORITIES];
  235. //Retry Qeueue for failed messages
  236. CFifoQueue<CMsgRef *> m_fqRetryQueue;
  237. //class used to store stats
  238. CAQStats m_aqstats;
  239. //which domain is represented in this destination
  240. CDomainMapping m_dmap;
  241. FILETIME m_ftOldest;
  242. CDestMsgRetryQueue m_dmrq;
  243. DWORD m_cCurrentThreadsEnqueuing;
  244. protected: //internal interfaces
  245. //Add Message to front or back of priority queues
  246. HRESULT HrAddMsg(
  247. IN CMsgRef *pmsgref, //Msg to add
  248. IN BOOL fEnqueue, //TRUE => enqueue,FALSE => requeue
  249. IN BOOL fNotify); //TRUE => send notification if needed
  250. void UpdateOldest(FILETIME *pft);
  251. //Callers must use CDestMsgRetryQueueClass
  252. HRESULT HrRetryMsg(IN CMsgRef *pmsgref); //put message on retry queue
  253. friend class CDestMsgRetryQueue;
  254. };
  255. //---[ CDestMsgQueue::HrEnqueueMsg ]-------------------------------------------
  256. //
  257. //
  258. // Description:
  259. // Enqueues a message for remote delivery for a given final destination
  260. // and message type
  261. // Parameters:
  262. // pmsgref AQ Message Reference to enqueue
  263. // fOwnsTypeRef TRUE if this queue is responsible for calling
  264. // IMessageRouter::ReleaseMessageType
  265. // Returns:
  266. // S_OK on success
  267. // Error code from HrAddMsg
  268. // History:
  269. // 5/21/98 - MikeSwa added fOwnsTypeRef
  270. //
  271. //-----------------------------------------------------------------------------
  272. HRESULT CDestMsgQueue::HrEnqueueMsg(IN CMsgRef *pmsgref, BOOL fOwnsTypeRef)
  273. {
  274. HRESULT hr = S_OK;
  275. hr = HrAddMsg(pmsgref, TRUE, TRUE);
  276. if (fOwnsTypeRef && SUCCEEDED(hr))
  277. InterlockedIncrement((PLONG) &m_cMessageTypeRefs);
  278. //Callers should have shutdown lock
  279. _ASSERT(!(m_dwFlags & (DMQ_INVALID | DMQ_SHUTDOWN_SIGNALED)));
  280. return hr;
  281. }
  282. //---[ CDestMsgQueue::paqmtGetMessageType ]------------------------------------
  283. //
  284. //
  285. // Description:
  286. // Get the message type for this queue
  287. // Parameters:
  288. // -
  289. // Returns:
  290. // CAQMessageType * of this queue's message type
  291. // History:
  292. // 5/28/98 - MikeSwa Created
  293. //
  294. //-----------------------------------------------------------------------------
  295. CAQMessageType *CDestMsgQueue::paqmtGetMessageType()
  296. {
  297. return (&m_aqmt);
  298. }
  299. //---[ CDestMsgQueue::fIsSameMessageType ]-------------------------------------
  300. //
  301. //
  302. // Description:
  303. // Tells if the message type of this queue is the same as the given
  304. // message type.
  305. // Parameters:
  306. // paqmt - ptr to CAQMessageType to test
  307. // Returns:
  308. // TRUE if they match, FALSE if they do not
  309. // History:
  310. // 5/26/98 - MikeSwa Created
  311. //
  312. //-----------------------------------------------------------------------------
  313. BOOL CDestMsgQueue::fIsSameMessageType(CAQMessageType *paqmt)
  314. {
  315. _ASSERT(paqmt);
  316. return m_aqmt.fIsEqual(paqmt);
  317. }
  318. //---[ CDestMsgQueue::pdmqIsSameMessageType ]----------------------------------
  319. //
  320. //
  321. // Description:
  322. // STATIC function used to determine if a LIST_ENTRY refers to a
  323. // CDestMsgQueue with a given message type.
  324. // Parameters:
  325. // paqmt - ptr to CAQMessageType to check against
  326. // pli - ptr to list entry to check (must refer to a CDestMsgQueue)
  327. // Returns:
  328. // Ptr to CDestMsgQueue if LIST_ENTRY refers to a CDestMsgQueue with
  329. // the given message type.
  330. // NULL if no match is not found
  331. // History:
  332. // 5/27/98 - MikeSwa Created
  333. //
  334. //-----------------------------------------------------------------------------
  335. CDestMsgQueue *CDestMsgQueue::pdmqIsSameMessageType(
  336. CAQMessageType *paqmt,
  337. PLIST_ENTRY pli)
  338. {
  339. CDestMsgQueue *pdmq = NULL;
  340. pdmq = CONTAINING_RECORD(pli, CDestMsgQueue, m_liDomainEntryDMQs);
  341. _ASSERT(DESTMSGQ_SIG == pdmq->m_dwSignature);
  342. //if not the same message type return NULL
  343. if (!pdmq->fIsSameMessageType(paqmt))
  344. pdmq = NULL;
  345. return pdmq;
  346. }
  347. //---[ CDestMsgQueue::pdmqGetDMQFromDomainListEntry ]--------------------------
  348. //
  349. //
  350. // Description:
  351. // Returns the CDestMsgQueue associated with a list entry
  352. // Parameters:
  353. // IN pli ptr to list entry to get CDestMsgQueue from
  354. // Returns:
  355. // ptr to CDestMsgQueue
  356. // History:
  357. // 5/28/98 - MikeSwa Created
  358. //
  359. //-----------------------------------------------------------------------------
  360. CDestMsgQueue *CDestMsgQueue::pdmqGetDMQFromDomainListEntry(PLIST_ENTRY pli)
  361. {
  362. _ASSERT(DESTMSGQ_SIG == (CONTAINING_RECORD(pli, CDestMsgQueue, m_liDomainEntryDMQs))->m_dwSignature);
  363. return (CONTAINING_RECORD(pli, CDestMsgQueue, m_liDomainEntryDMQs));
  364. }
  365. //---[ CDestMsgQueue::InsertQueueInDomainList ]---------------------------------
  366. //
  367. //
  368. // Description:
  369. // Inserts this CDestMsgQueue into the given linked list of queues
  370. // Parameters:
  371. // pliHead - PLIST_ENTRY for list head
  372. // Returns:
  373. // -
  374. // History:
  375. // 5/27/98 - MikeSwa Created
  376. //
  377. //-----------------------------------------------------------------------------
  378. void CDestMsgQueue::InsertQueueInDomainList(PLIST_ENTRY pliHead)
  379. {
  380. _ASSERT(NULL == m_liDomainEntryDMQs.Flink);
  381. _ASSERT(NULL == m_liDomainEntryDMQs.Blink);
  382. InsertHeadList(pliHead, &m_liDomainEntryDMQs);
  383. }
  384. //---[ CDestMsgQueue::RemoveQueueFromDomainList ]-------------------------------
  385. //
  386. //
  387. // Description:
  388. // Removes this queue from a list of queues
  389. // Parameters:
  390. // -
  391. // Returns:
  392. // -
  393. // History:
  394. // 5/27/98 - MikeSwa Created
  395. //
  396. //-----------------------------------------------------------------------------
  397. void CDestMsgQueue::RemoveQueueFromDomainList()
  398. {
  399. RemoveEntryList(&m_liDomainEntryDMQs);
  400. m_liDomainEntryDMQs.Flink = NULL;
  401. m_liDomainEntryDMQs.Blink = NULL;
  402. }
  403. //---[ CDestMsgQueue::pliGetNextDomainListEntry ]-------------------------------
  404. //
  405. //
  406. // Description:
  407. // Gets the pointer to the next list entry for this queue.
  408. // Parameters:
  409. // -
  410. // Returns:
  411. // The Flink of the queues LIST_ENTRY
  412. // History:
  413. // 6/16/98 - MikeSwa Created
  414. //
  415. //-----------------------------------------------------------------------------
  416. PLIST_ENTRY CDestMsgQueue::pliGetNextDomainListEntry()
  417. {
  418. return m_liDomainEntryDMQs.Flink;
  419. }
  420. //---[ CDestMsgQueue::InsertQueueInEmptyQueueList ]----------------------------
  421. //
  422. //
  423. // Description:
  424. // Inserts queue at *tail* of DMT empty queue list. The queue that has
  425. // been empty the longest should be at the As with the other EmptyQueue
  426. // list functions this is called by the DMT, when it has the appropriate
  427. // lock for the head of the list.
  428. //
  429. // Upon insertion, an "expire time" is stamped on the queue. If the queue
  430. // is still in the list, then it is a candidate for deletion, and will be
  431. // delete the next time the DMT looks at the queue (everytime HrMapDomain
  432. // is called).
  433. //
  434. // NOTE"We need to make sure this function is thread-safe. Since the
  435. // DMQ lock is aquired exclusively before this is called, we know that
  436. // no one will ENQUEUE a messsage. This function call is tiggered after
  437. // the retry queues are emptied when a connection finished, so we can
  438. // also ensure that no one will call this while there are messages to
  439. // retry.
  440. // It is however (remotely) possible for 2 threads to finish connections
  441. // for this queue and thus cause 2 threads to be in this function.
  442. // The thread that successfully modified the EMPTY bit will be allowed
  443. // to add the queue to the list.
  444. // Parameters:
  445. // IN pliHead The head of the list to insert into
  446. // Returns:
  447. // -
  448. // History:
  449. // 9/11/98 - MikeSwa Created
  450. //
  451. //-----------------------------------------------------------------------------
  452. void CDestMsgQueue::InsertQueueInEmptyQueueList(PLIST_ENTRY pliHead)
  453. {
  454. _ASSERT(m_paqinst);
  455. //Now that we have the exclusive lock recheck to make sure there are no messages
  456. if (m_aqstats.m_cMsgs || m_fqRetryQueue.cGetCount())
  457. return;
  458. //Attempt to set the DMQ_EMPTY bit
  459. if (DMQ_EMPTY & dwInterlockedSetBits(&m_dwFlags, DMQ_EMPTY))
  460. {
  461. //Another thread has set it, we cannot modify the LIST_ENTRY
  462. return;
  463. }
  464. //If it is already in queue, that means that the queue has gone
  465. //from empty to non-empty to empty. Insert at tail of list with new time
  466. if (m_dwFlags & DMQ_IN_EMPTY_QUEUE_LIST)
  467. {
  468. _ASSERT(NULL != m_liEmptyDMQs.Flink);
  469. _ASSERT(NULL != m_liEmptyDMQs.Blink);
  470. RemoveEntryList(&m_liEmptyDMQs);
  471. m_cRemovedFromEmptyList++;
  472. }
  473. else
  474. {
  475. _ASSERT(NULL == m_liEmptyDMQs.Flink);
  476. _ASSERT(NULL == m_liEmptyDMQs.Blink);
  477. }
  478. //Get expire time for this queue
  479. m_paqinst->GetExpireTime(EMPTY_DMQ_EXPIRE_TIME_MINUTES,
  480. &m_ftEmptyExpireTime, NULL);
  481. //Mark queue as in empty queue
  482. dwInterlockedSetBits(&m_dwFlags, DMQ_IN_EMPTY_QUEUE_LIST);
  483. //Insert into queue
  484. InsertTailList(pliHead, &m_liEmptyDMQs);
  485. _ASSERT(pliHead->Blink == &m_liEmptyDMQs);
  486. _ASSERT(!m_aqstats.m_cMsgs); //No other thread should be able to add msgs
  487. }
  488. //---[ DestMsgQueue::RemoveQueueFromEmptyQueueList ]---------------------------
  489. //
  490. //
  491. // Description:
  492. // Removed the queue from the empty list. Caller *must* have DMT lock
  493. // to call this. DMQ will not call this directly, but will call into
  494. // DMT .
  495. // Parameters:
  496. // -
  497. // Returns:
  498. // -
  499. // History:
  500. // 9/11/98 - MikeSwa Created
  501. //
  502. //-----------------------------------------------------------------------------
  503. void CDestMsgQueue::RemoveQueueFromEmptyQueueList()
  504. {
  505. RemoveEntryList(&m_liEmptyDMQs);
  506. //Increment count now that queue is being removed from empty list
  507. m_cRemovedFromEmptyList++;
  508. //Mark queue as not in empty queue
  509. dwInterlockedUnsetBits(&m_dwFlags, DMQ_IN_EMPTY_QUEUE_LIST);
  510. m_liEmptyDMQs.Flink = NULL;
  511. m_liEmptyDMQs.Blink = NULL;
  512. }
  513. //---[ CDestMsgQueue::pliGetNextEmptyQueueListEntry ]--------------------------
  514. //
  515. //
  516. // Description:
  517. // Gets next queue entry in empty list.
  518. // Parameters:
  519. // -
  520. // Returns:
  521. // Next entry pointed to by list entry
  522. // History:
  523. // 9/11/98 - MikeSwa Created
  524. //
  525. //-----------------------------------------------------------------------------
  526. PLIST_ENTRY CDestMsgQueue::pliGetNextEmptyQueueListEntry()
  527. {
  528. return m_liEmptyDMQs.Flink;
  529. }
  530. //---[ CDestMsgQueue::dwGetDMQState ]------------------------------------------
  531. //
  532. //
  533. // Description:
  534. // Returns the state of the DMQ and caches that state in m_dwFlags. May
  535. // update DMQ_EXPIRED if DMQ is in empty list and it has expired
  536. // Parameters:
  537. // -
  538. // Returns:
  539. // Current DMQ state
  540. // History:
  541. // 9/12/98 - MikeSwa Created
  542. //
  543. //-----------------------------------------------------------------------------
  544. DWORD CDestMsgQueue::dwGetDMQState()
  545. {
  546. _ASSERT(DESTMSGQ_SIG == m_dwSignature);
  547. _ASSERT(m_paqinst);
  548. if (DMQ_IN_EMPTY_QUEUE_LIST & m_dwFlags)
  549. {
  550. //If it is empty and not expired..check if expired
  551. if ((DMQ_EMPTY & m_dwFlags) && !(DMQ_EXPIRED & m_dwFlags))
  552. {
  553. if (m_paqinst->fInPast(&m_ftEmptyExpireTime, NULL))
  554. dwInterlockedSetBits(&m_dwFlags, DMQ_EXPIRED);
  555. }
  556. }
  557. return m_dwFlags;
  558. }
  559. //---[ CDestMsgQueue::MarkDMQInvalid ]------------------------------------------
  560. //
  561. //
  562. // Description:
  563. // Marks this queue as invalid. Queue *must* be empty for this to happen
  564. // Parameters:
  565. // -
  566. // Returns:
  567. // -
  568. // History:
  569. // 9/12/98 - MikeSwa Created
  570. //
  571. //-----------------------------------------------------------------------------
  572. void CDestMsgQueue::MarkDMQInvalid()
  573. {
  574. _ASSERT(DESTMSGQ_SIG == m_dwSignature);
  575. _ASSERT(DMQ_EMPTY & m_dwFlags);
  576. dwInterlockedSetBits(&m_dwFlags, DMQ_INVALID);
  577. }
  578. //---[ CDestMsgQueue::pdmqGetDMQFromEmptyListEntry ]---------------------------
  579. //
  580. //
  581. // Description:
  582. // Returns the DMQ corresponding to a given Empty Queue LIST_ENTRY.
  583. //
  584. // Will assert that DMQ signature is valid
  585. // Parameters:
  586. // IN pli Pointer to LIST_ENTRY for queue
  587. // Returns:
  588. //
  589. // History:
  590. // 9/12/98 - MikeSwa Created
  591. //
  592. //-----------------------------------------------------------------------------
  593. CDestMsgQueue *CDestMsgQueue::pdmqGetDMQFromEmptyListEntry(PLIST_ENTRY pli)
  594. {
  595. _ASSERT(DESTMSGQ_SIG == (CONTAINING_RECORD(pli, CDestMsgQueue, m_liEmptyDMQs))->m_dwSignature);
  596. return (CONTAINING_RECORD(pli, CDestMsgQueue, m_liEmptyDMQs));
  597. }
  598. //---[ CDestMsgQueue::GetDomainMapping ]---------------------------------------
  599. //
  600. //
  601. // Description:
  602. // Returns the domain mapping for this queue.
  603. // Parameters:
  604. // OUT ppdmap Returned domain mapping
  605. // Returns:
  606. // -
  607. // History:
  608. // 9/14/98 - MikeSwa Modified to not have a return value
  609. //
  610. //-----------------------------------------------------------------------------
  611. void CDestMsgQueue::GetDomainMapping(OUT CDomainMapping **ppdmap)
  612. {
  613. _ASSERT(ppdmap);
  614. *ppdmap = &m_dmap;
  615. }
  616. IMessageRouter *CDestMsgQueue::pIMessageRouterGetRouter()
  617. {
  618. return m_pIMessageRouter;
  619. }
  620. //---[ CDestMsgQueue::GetOldestMsg ]-------------------------------------------
  621. //
  622. //
  623. // Description:
  624. // Retruns an approximation of the oldest message in the queue
  625. // Parameters:
  626. // OUT pft FILTIME of "oldest" Messate
  627. // Returns:
  628. // -
  629. // History:
  630. // 12/13/98 - MikeSwa Created
  631. //
  632. //-----------------------------------------------------------------------------
  633. void CDestMsgQueue::GetOldestMsg(FILETIME *pft)
  634. {
  635. _ASSERT(pft);
  636. if (m_aqstats.m_cMsgs)
  637. memcpy(pft, &m_ftOldest, sizeof(FILETIME));
  638. else
  639. ZeroMemory(pft, sizeof (FILETIME));
  640. }
  641. #endif //_DESTMSGQ_H_