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.

1697 lines
52 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: destmsgq.cpp
  5. //
  6. // Description: Implementation of the CDestMsgQueue class
  7. //
  8. // Author: mikeswa
  9. //
  10. // Copyright (C) 1997 Microsoft Corporation
  11. //
  12. //-----------------------------------------------------------------------------
  13. #include "aqprecmp.h"
  14. #include "fifoqimp.h"
  15. #include "qwiklist.h"
  16. #include "aqutil.h"
  17. //---[ DEST_QUEUE_CONTEXT ]----------------------------------------------------
  18. //
  19. //
  20. // Description:
  21. // Context used for DSN generation. This is created by
  22. // GenerateDSNsIfNecessary() and passed to the DMQ iterator function
  23. // HrWalkDMQForDSN().
  24. // Hungarian:
  25. // dqcontext, pdqcontext
  26. //
  27. //-----------------------------------------------------------------------------
  28. class DEST_QUEUE_CONTEXT
  29. {
  30. private:
  31. friend HRESULT CDestMsgQueue::HrWalkDMQForDSN(IN CMsgRef *pmsgref, IN PVOID pvContext,
  32. OUT BOOL *pfContinue, OUT BOOL *pfDelete);
  33. CDestMsgQueue *m_pdmq;
  34. CQuickList *m_pql;
  35. HRESULT m_hrConnectionStatus;
  36. DWORD m_cMsgsSeenThisQueue;
  37. DWORD m_cDSNsGeneratedThisQueue;
  38. DWORD m_dwTickCountStart;
  39. BOOL m_bRetryQueue;
  40. public:
  41. //Contructor.... initializes and updates DWORD context
  42. DEST_QUEUE_CONTEXT(IN OUT DWORD *pdwContext, IN CDestMsgQueue *pdmq,
  43. IN CQuickList *pql, IN HRESULT hr)
  44. {
  45. _ASSERT(pdwContext);
  46. m_pdmq = pdmq;
  47. m_pql = pql;
  48. m_hrConnectionStatus = hr;
  49. m_cMsgsSeenThisQueue = 0;
  50. m_cDSNsGeneratedThisQueue = 0;
  51. m_bRetryQueue = FALSE;
  52. //Initialize/Update context if it has not been initialized
  53. if (!*pdwContext)
  54. *pdwContext = GetTickCount();
  55. m_dwTickCountStart = *pdwContext;
  56. }
  57. ~DEST_QUEUE_CONTEXT()
  58. {
  59. TraceFunctEnterEx((LPARAM) this, "DEST_QUEUE_CONTEXT::~DEST_QUEUE_CONTEXT");
  60. DWORD dwTickDiff = GetTickCount() - m_dwTickCountStart;
  61. DebugTrace((LPARAM) this,
  62. "DSN summary: %d milliseconds - %d msgs - %d DSNs",
  63. dwTickDiff, m_cMsgsSeenThisQueue, m_cDSNsGeneratedThisQueue);
  64. _ASSERT(m_cMsgsSeenThisQueue >= m_cDSNsGeneratedThisQueue);
  65. TraceFunctLeave();
  66. }
  67. void SetRetry(BOOL bRetry)
  68. {
  69. m_bRetryQueue = bRetry;
  70. }
  71. BOOL fPastTimeLimit()
  72. {
  73. DWORD dwTickCountDiff = GetTickCount() - m_dwTickCountStart;
  74. if (dwTickCountDiff >= g_cMaxSecondsPerDSNsGenerationPass*1000)
  75. return TRUE;
  76. else
  77. return FALSE;
  78. }
  79. };
  80. //---[ CDestMsgRetryQueue::CDestMsgRetryQueue ]--------------------------------
  81. //
  82. //
  83. // Description:
  84. // Constructor for CDestMsgRetryQueue.
  85. // Parameters:
  86. // -
  87. // Returns:
  88. // -
  89. // History:
  90. // 10/25/1999 - MikeSwa Created
  91. //
  92. //-----------------------------------------------------------------------------
  93. CDestMsgRetryQueue::CDestMsgRetryQueue()
  94. {
  95. m_dwSignature = DESTMSGRETRYQ_SIG;
  96. m_cRetryReferenceCount = 0;
  97. m_pdmq = NULL;
  98. }
  99. //---[ CDestMsgRetryQueue::HrRetryMsg ]----------------------------------------
  100. //
  101. //
  102. // Description:
  103. // Puts a message into the retry queue
  104. // Parameters:
  105. // pmsgref Message to put into retry queue
  106. // Returns:
  107. // S_OK on success
  108. // E_INVALIDARG if no refcount (asserts in DBG)
  109. // History:
  110. // 10/25/1999 - MikeSwa Created
  111. //
  112. //-----------------------------------------------------------------------------
  113. HRESULT CDestMsgRetryQueue::HrRetryMsg(IN CMsgRef *pmsgref)
  114. {
  115. _ASSERT(m_pdmq);
  116. _ASSERT(m_cRetryReferenceCount);
  117. _ASSERT(DESTMSGRETRYQ_SIG == m_dwSignature);
  118. if (!m_pdmq || !m_cRetryReferenceCount)
  119. {
  120. return E_INVALIDARG;
  121. }
  122. m_pdmq->AssertSignature();
  123. return (m_pdmq->HrRetryMsg(pmsgref));
  124. }
  125. //---[ CDestMsgQueueHrWalkDMQForDSN ]------------------------------------------
  126. //
  127. //
  128. // Description:
  129. // Example default function to use with HrMapFn... will always return TRUE
  130. // to continue and delete the current queued data
  131. // Parameters:
  132. // IN CMsgRef pmsgref, //ptr to data on queue
  133. // IN PVOID pvContext //list of queues to prepare for DSN
  134. // OUT BOOL *pfContinue, //TRUE if we should continue
  135. // OUT BOOL *pfDelete); //TRUE if item should be deleted
  136. // Returns:
  137. // S_OK
  138. // History:
  139. // 7/13/98 - MikeSwa Created
  140. //-----------------------------------------------------------------------------
  141. HRESULT CDestMsgQueue::HrWalkDMQForDSN(IN CMsgRef *pmsgref, IN PVOID pvContext,
  142. OUT BOOL *pfContinue, OUT BOOL *pfDelete)
  143. {
  144. TraceFunctEnterEx((LPARAM) pmsgref, "CDestMsgQueue::HrWalkDMQForDSN");
  145. Assert(pfContinue);
  146. Assert(pfDelete);
  147. HRESULT hr = S_OK;
  148. HRESULT hrReason = S_OK;
  149. DWORD dwDSNFlags = 0;
  150. DWORD dwMsgRefDSNOptions = CMsgRef::MSGREF_DSN_SEND_DELAY |
  151. CMsgRef::MSGREF_DSN_HAS_ROUTING_LOCK;
  152. DEST_QUEUE_CONTEXT *pdqcontext = (DEST_QUEUE_CONTEXT *) pvContext;
  153. CLinkMsgQueue *plmq = NULL;
  154. CQuickList quicklist;
  155. CQuickList *pql;
  156. DWORD dwIndex;
  157. _ASSERT(pdqcontext->m_pdmq);
  158. _ASSERT(pdqcontext->m_pql);
  159. *pfContinue = TRUE;
  160. *pfDelete = FALSE;
  161. //See if we got the shutdown hint... if so bail
  162. if (pdqcontext->m_pdmq->m_paqinst->fShutdownSignaled())
  163. {
  164. *pfContinue = FALSE;
  165. goto Exit;
  166. }
  167. //Check and make sure that a routing change is not pending
  168. if (!pdqcontext->m_pdmq->m_paqinst ||
  169. !pdqcontext->m_pdmq->m_paqinst->fTryRoutingShareLock())
  170. {
  171. *pfContinue = FALSE;
  172. goto Exit;
  173. }
  174. pdqcontext->m_pdmq->m_paqinst->RoutingShareUnlock();
  175. //Check and see if we have hit our limit. We force ourselves
  176. //to generate at least one DSN, so we make some forward progress
  177. //each pass.
  178. if (pdqcontext->m_cDSNsGeneratedThisQueue && pdqcontext->fPastTimeLimit())
  179. {
  180. *pfContinue = FALSE;
  181. hr = HRESULT_FROM_WIN32(E_PENDING);
  182. goto Exit;
  183. }
  184. //Avoid holding the lock while making external calls other than AddRef etc
  185. pdqcontext->m_pdmq->m_slPrivateData.ShareLock();
  186. plmq = pdqcontext->m_pdmq->m_plmq;
  187. if (plmq)
  188. plmq->AddRef();
  189. pdqcontext->m_pdmq->m_slPrivateData.ShareUnlock();
  190. //$$REVIEW: Holding the lock for such a short time is something of a moot
  191. //point here, since a Sharelock is held at the link level to assure that
  192. //the qwiklist passed in as part of our context does not change
  193. if (plmq)
  194. {
  195. //We should not send delay DSNs to TURN/ETRN domains
  196. if (plmq->dwGetLinkState() & LINK_STATE_PRIV_CONFIG_TURN_ETRN)
  197. dwMsgRefDSNOptions &= ~CMsgRef::MSGREF_DSN_SEND_DELAY;
  198. plmq->Release();
  199. }
  200. if(pdqcontext->m_pdmq->m_hrRoutingDiag != S_OK)
  201. {
  202. //
  203. // This message is being NDR'ed because routing had a problem with
  204. // it. Pass in the HRESULT from routing so that we can use it during
  205. // DSN generation. Pass in a quicklist with only the CDestMsgQueue
  206. // on which the routing error occured.
  207. //
  208. hrReason = pdqcontext->m_pdmq->m_hrRoutingDiag;
  209. DebugTrace((LPARAM)pmsgref, "Generating DSN due to routing, hr - %08x", hrReason);
  210. hr = quicklist.HrAppendItem(pdqcontext->m_pdmq, &dwIndex);
  211. if(FAILED(hr))
  212. {
  213. ErrorTrace((LPARAM)pmsgref, "Unable to generate DSN for msg");
  214. goto Exit;
  215. }
  216. pql = &quicklist;
  217. }
  218. else
  219. {
  220. hrReason = pdqcontext->m_hrConnectionStatus;
  221. pql = pdqcontext->m_pql;
  222. }
  223. if (pdqcontext->m_pdmq->m_dwFlags & DMQ_CHECK_FOR_STALE_MSGS)
  224. {
  225. DebugTrace((LPARAM) pmsgref, "Enabling checking for stale messages");
  226. dwMsgRefDSNOptions |= CMsgRef::MSGREF_DSN_CHECK_IF_STALE;
  227. }
  228. hr = pmsgref->HrSendDelayOrNDR(dwMsgRefDSNOptions, pql, hrReason, &dwDSNFlags);
  229. if (FAILED(hr))
  230. goto Exit;
  231. //NOTE: Although it would be tempting to return *pfContinue as FALSE if
  232. //MSGREF_HAS_NOT_EXPIRED was set, it would be wrong since queues may be
  233. //out of order on startup... and some sink may modify the expiration time
  234. //(for example... routing may want to expire low-priority messages earlier).
  235. //We need to remove this message from the queue
  236. if ((CMsgRef::MSGREF_DSN_SENT_NDR | CMsgRef::MSGREF_HANDLED) & dwDSNFlags)
  237. {
  238. *pfDelete = TRUE;
  239. if (pdqcontext->m_bRetryQueue)
  240. pdqcontext->m_pdmq->UpdateRetryStats(FALSE);
  241. else
  242. pdqcontext->m_pdmq->UpdateMsgStats(pmsgref, FALSE);
  243. }
  244. //Update counts in context
  245. pdqcontext->m_cMsgsSeenThisQueue++;
  246. if ((CMsgRef::MSGREF_DSN_SENT_NDR | CMsgRef::MSGREF_DSN_SENT_DELAY) & dwDSNFlags)
  247. {
  248. pdqcontext->m_cDSNsGeneratedThisQueue++;
  249. }
  250. Exit:
  251. if (AQUEUE_E_SHUTDOWN == hr)
  252. {
  253. *pfContinue = FALSE;
  254. hr = S_OK;
  255. }
  256. TraceFunctLeave();
  257. return hr;
  258. }
  259. //---[ CDestMsgQueue::HrWalkQueueForShutdown ]--------------------------------
  260. //
  261. //
  262. // Description:
  263. // Static function to walk a queue containing msgrefs at shutdown and
  264. // clear out all of the IMailMsgs
  265. // Parameters:
  266. // IN CMsgRef pmsgref, ptr to data on queue
  267. // IN PVOID pvContext Pointer to CDestMsgQueue we are walking for
  268. // shutdown.
  269. // OUT BOOL *pfContinue, TRUE if we should continue
  270. // OUT BOOL *pfDelete); TRUE if item should be deleted
  271. // Returns:
  272. // S_OK - *always*
  273. // History:
  274. // 11/18/98 - MikeSwa Created
  275. //-----------------------------------------------------------------------------
  276. HRESULT CDestMsgQueue::HrWalkQueueForShutdown(IN CMsgRef *pmsgref,
  277. IN PVOID pvContext, OUT BOOL *pfContinue,
  278. OUT BOOL *pfDelete)
  279. {
  280. TraceFunctEnterEx((LPARAM) pmsgref, "HrWalkMsgRefQueueForShutdown");
  281. Assert(pfContinue);
  282. Assert(pfDelete);
  283. CDestMsgQueue *pdmq = (CDestMsgQueue *) pvContext;
  284. _ASSERT(pmsgref);
  285. _ASSERT(pdmq);
  286. _ASSERT(DESTMSGQ_SIG == pdmq->m_dwSignature);
  287. *pfContinue = TRUE;
  288. *pfDelete = TRUE;
  289. //call server stop hint function
  290. if (pdmq->m_paqinst)
  291. pdmq->m_paqinst->ServerStopHintFunction();
  292. //Update stats
  293. pdmq->UpdateMsgStats(pmsgref, FALSE);
  294. pmsgref->AddRef();
  295. pdmq->m_paqinst->HrQueueWorkItem(pmsgref, fMsgRefShutdownCompletion);
  296. TraceFunctLeave();
  297. return S_OK;
  298. }
  299. //---[ CDestMsgQueue::HrWalkRetryQueueForShutdown ]--------------------------------
  300. //
  301. //
  302. // Description:
  303. // Static function to walk a retry queue containing msgrefs at shutdown and
  304. // clear out all of the IMailMsgs
  305. // Parameters:
  306. // IN CMsgRef pmsgref, ptr to data on queue
  307. // IN PVOID pvContext Pointer to CDestMsgQueue we are walking for
  308. // shutdown.
  309. // OUT BOOL *pfContinue, TRUE if we should continue
  310. // OUT BOOL *pfDelete); TRUE if item should be deleted
  311. // Returns:
  312. // S_OK - *always*
  313. //-----------------------------------------------------------------------------
  314. HRESULT CDestMsgQueue::HrWalkRetryQueueForShutdown(IN CMsgRef *pmsgref,
  315. IN PVOID pvContext, OUT BOOL *pfContinue,
  316. OUT BOOL *pfDelete)
  317. {
  318. TraceFunctEnterEx((LPARAM) pmsgref, "HrWalkMsgRefQueueForShutdown");
  319. Assert(pfContinue);
  320. Assert(pfDelete);
  321. CDestMsgQueue *pdmq = (CDestMsgQueue *) pvContext;
  322. _ASSERT(pmsgref);
  323. _ASSERT(pdmq);
  324. _ASSERT(DESTMSGQ_SIG == pdmq->m_dwSignature);
  325. *pfContinue = TRUE;
  326. *pfDelete = TRUE;
  327. //call server stop hint function
  328. if (pdmq->m_paqinst)
  329. pdmq->m_paqinst->ServerStopHintFunction();
  330. //Update retry stats
  331. pdmq->UpdateRetryStats(FALSE);
  332. pmsgref->AddRef();
  333. pdmq->m_paqinst->HrQueueWorkItem(pmsgref, fMsgRefShutdownCompletion);
  334. TraceFunctLeave();
  335. return S_OK;
  336. }
  337. //---[ CDestMsgQueue::CDestMsgQueue() ]----------------------------------------
  338. //
  339. //
  340. // Description:
  341. // Class constructor
  342. // Parameters:
  343. // IN paqinst AQ virtual server object
  344. // IN paqmtMessageType Message type for this queue
  345. // IN pIMessageRouter IMessageRouter interface for this queue
  346. // Returns:
  347. // -
  348. //-----------------------------------------------------------------------------
  349. CDestMsgQueue::CDestMsgQueue(CAQSvrInst *paqinst,
  350. CAQMessageType *paqmtMessageType,
  351. IMessageRouter *pIMessageRouter)
  352. : m_aqmt(paqmtMessageType)
  353. {
  354. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::CDestMsgQueue");
  355. _ASSERT(paqinst);
  356. _ASSERT(pIMessageRouter);
  357. m_dwSignature = DESTMSGQ_SIG;
  358. m_dwFlags = DMQ_EMPTY;
  359. m_pIMessageRouter = pIMessageRouter;
  360. m_plmq = NULL;
  361. m_paqinst = paqinst;
  362. m_cMessageTypeRefs = 0;
  363. m_pvLinkContext = NULL;
  364. m_cCurrentThreadsEnqueuing = 0;
  365. m_hrRoutingDiag = S_OK;
  366. m_pIMessageRouter->AddRef();
  367. m_paqinst->AddRef();
  368. m_paqinst->IncDestQueueCount();
  369. m_liDomainEntryDMQs.Flink = NULL;
  370. m_liDomainEntryDMQs.Blink = NULL;
  371. m_liEmptyDMQs.Flink = NULL;
  372. m_liEmptyDMQs.Blink = NULL;
  373. m_cRemovedFromEmptyList = 0;
  374. ZeroMemory(m_rgpfqQueues, NUM_PRIORITIES*sizeof(CFifoQueue<CMsgRef *> **));
  375. ZeroMemory(&m_ftOldest, sizeof (FILETIME));
  376. m_dmrq.m_pdmq = this;
  377. TraceFunctLeave();
  378. }
  379. //---[ CDestMsgQueue::~CDestMsgQueue() ]---------------------------------------
  380. //
  381. //
  382. // Description:
  383. // Default destructor
  384. // Parameters:
  385. // -
  386. // Returns:
  387. // -
  388. //-----------------------------------------------------------------------------
  389. CDestMsgQueue::~CDestMsgQueue()
  390. {
  391. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::~CDestMsgQueue");
  392. for (int i = 0; i < NUM_PRIORITIES; i++)
  393. {
  394. if (NULL != m_rgpfqQueues[i])
  395. delete m_rgpfqQueues[i];
  396. }
  397. //Make sure we clean up the link even if HrDeinitialize wasn't called
  398. if (m_plmq)
  399. {
  400. m_plmq->HrDeinitialize();
  401. m_plmq->Release();
  402. m_plmq = NULL;
  403. }
  404. if (m_pIMessageRouter)
  405. {
  406. _ASSERT((!m_cMessageTypeRefs) && "Message Type references in destructor");
  407. m_pIMessageRouter->Release();
  408. m_pIMessageRouter = NULL;
  409. }
  410. if (m_paqinst)
  411. {
  412. m_paqinst->DecDestQueueCount();
  413. m_paqinst->Release();
  414. m_paqinst = NULL;
  415. }
  416. _ASSERT(NULL == m_liDomainEntryDMQs.Flink);
  417. _ASSERT(NULL == m_liDomainEntryDMQs.Blink);
  418. _ASSERT(!m_cCurrentThreadsEnqueuing);
  419. MARK_SIG_AS_DELETED(m_dwSignature);
  420. TraceFunctLeave();
  421. }
  422. //---[ CDestMsgQueue::HrInitialize() ]-----------------------------------------
  423. //
  424. //
  425. // Description:
  426. // Performs initialization that may require allocation
  427. // Parameters:
  428. // IN CDomainMapping *pdmap //array of domain mappings to use
  429. // Returns:
  430. // S_OK on success
  431. // E_OUTOFMEMORY if allocations fail
  432. //-----------------------------------------------------------------------------
  433. HRESULT CDestMsgQueue::HrInitialize(IN CDomainMapping *pdmap)
  434. {
  435. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::HrInitialize");
  436. HRESULT hr = S_OK;
  437. DWORD i = 0; //loop counter
  438. _ASSERT(pdmap);
  439. if (!pdmap)
  440. {
  441. hr = E_INVALIDARG;
  442. goto Exit;
  443. }
  444. //If the queues are compressed (more than one domain name per destination),
  445. //then each queue will contain multiple domain mappings (1 for each domain)
  446. m_dmap.Clone(pdmap);
  447. Exit:
  448. TraceFunctLeave();
  449. return hr;
  450. }
  451. //---[ CDestMsgQueue::HrDeinitialize ]-----------------------------------------
  452. //
  453. //
  454. // Description:
  455. // Deinitialize object
  456. // Parameters:
  457. // -
  458. // Returns:
  459. // S_OK on success
  460. //
  461. //-----------------------------------------------------------------------------
  462. HRESULT CDestMsgQueue::HrDeinitialize()
  463. {
  464. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::HrDeinitialize");
  465. HRESULT hr = S_OK;
  466. HRESULT hrTmp = S_OK;
  467. DWORD cMsgsRemoved =0;
  468. dwInterlockedSetBits(&m_dwFlags, DMQ_SHUTDOWN_SIGNALED);
  469. for (int i = 0; i < NUM_PRIORITIES; i++)
  470. {
  471. if (NULL != m_rgpfqQueues[i])
  472. {
  473. hrTmp = m_rgpfqQueues[i]->HrMapFn(CDestMsgQueue::HrWalkQueueForShutdown,
  474. this, &cMsgsRemoved);
  475. //This should really never fail, since HrMapFn will only return errors from
  476. //the function walking the queues (which in this case never fails)
  477. _ASSERT(SUCCEEDED(hrTmp));
  478. //This *should* have removed all msgs
  479. _ASSERT(!m_aqstats.m_cMsgs && "Still msgs in queue after Deinit");
  480. }
  481. }
  482. m_fqRetryQueue.HrMapFn(CDestMsgQueue::HrWalkRetryQueueForShutdown, this, NULL);
  483. if (m_pIMessageRouter)
  484. {
  485. if (m_cMessageTypeRefs)
  486. {
  487. hr = m_pIMessageRouter->ReleaseMessageType(m_aqmt.dwGetMessageType(),
  488. m_cMessageTypeRefs);
  489. _ASSERT(SUCCEEDED(hr) && "Release Message Type Failed");
  490. m_cMessageTypeRefs = 0;
  491. }
  492. m_pIMessageRouter->Release();
  493. m_pIMessageRouter = NULL;
  494. }
  495. else
  496. {
  497. _ASSERT((!m_cMessageTypeRefs) && "We're leaking message type references");
  498. }
  499. if (m_paqinst)
  500. {
  501. m_paqinst->DecDestQueueCount();
  502. m_paqinst->Release();
  503. m_paqinst = NULL;
  504. }
  505. if (m_plmq)
  506. {
  507. m_plmq->Release();
  508. m_plmq = NULL;
  509. }
  510. TraceFunctLeave();
  511. return hr;
  512. }
  513. //---[ CDestMsgQueue::HrAddMsg ]----------------------------------------------
  514. //
  515. //
  516. // Description:
  517. // Enqueues or Requeues a message to the appropriate priority queue,
  518. // allocating queue if not present.
  519. //
  520. // A notification will be sent if needed (& requested) to the associated
  521. // link object. The fNotify argument was originally included to prevent
  522. // messages from the retry queue causing notifications.
  523. // Parameters:
  524. // IN CMsgRef *pmsgref - the message ref to enqueue
  525. // IN BOOL fEnqueue - TRUE => enqueue and FALSE => requeue
  526. // IN BOOL fNotify - TRUE => send notification if necessary.
  527. // Returns:
  528. // S_OK on success
  529. //
  530. //-----------------------------------------------------------------------------
  531. HRESULT CDestMsgQueue::HrAddMsg(IN CMsgRef *pmsgref, IN BOOL fEnqueue,
  532. IN BOOL fNotify)
  533. {
  534. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::HrAddMsg");
  535. HRESULT hr = S_OK;
  536. DWORD dwFlags = 0;
  537. DWORD iQueueIndex = (DWORD) eEffPriNormal;
  538. CFifoQueue<CMsgRef *> *pfqQueue = NULL;
  539. CFifoQueue<CMsgRef *> *pfqQueueNew= NULL;
  540. _ASSERT(pmsgref);
  541. _ASSERT(m_aqmt.fIsEqual(pmsgref->paqmtGetMessageType()));
  542. _ASSERT(!(m_dwFlags & (DMQ_INVALID | DMQ_SHUTDOWN_SIGNALED)));
  543. //get the priority from the message reference
  544. iQueueIndex = (DWORD) pmsgref->PriGetPriority();
  545. //
  546. // If the message is marked as problem... treat with lower priority.
  547. //
  548. if (iQueueIndex && pmsgref->fIsProblemMsg())
  549. iQueueIndex--;
  550. //use priority to get to get ptr to correct queue
  551. _ASSERT(iQueueIndex < NUM_PRIORITIES);
  552. pfqQueue = m_rgpfqQueues[iQueueIndex];
  553. if (NULL == pfqQueue) //we must allocate a queue
  554. {
  555. pfqQueueNew = new CFifoQueue<CMsgRef *>();
  556. if (NULL != pfqQueueNew)
  557. {
  558. pfqQueue = (CFifoQueue<CMsgRef *> *) InterlockedCompareExchangePointer(
  559. (VOID **) &(m_rgpfqQueues[iQueueIndex]),
  560. (VOID *) pfqQueueNew,
  561. NULL);
  562. if (NULL != pfqQueue)
  563. {
  564. //someone else updated first
  565. delete pfqQueueNew;
  566. }
  567. else
  568. {
  569. //Our updated worked
  570. pfqQueue = pfqQueueNew;
  571. }
  572. pfqQueueNew = NULL;
  573. }
  574. else //allocation failed
  575. {
  576. hr = E_OUTOFMEMORY;
  577. goto Exit;
  578. }
  579. }
  580. //at this point queue ptr should be good
  581. _ASSERT(pfqQueue);
  582. //Assume enqueue will work - optimize to avoid dealing with negative stats
  583. //Mark queue as non-empty
  584. dwFlags = dwInterlockedUnsetBits(&m_dwFlags, DMQ_EMPTY);
  585. if (DMQ_EMPTY & dwFlags)
  586. UpdateOldest(pmsgref->pftGetAge());
  587. //
  588. // Keep track of how many threads are enqueuing so that we know what
  589. // is the most we can expect to be off in our message count.
  590. //
  591. InterlockedIncrement((PLONG) &m_cCurrentThreadsEnqueuing);
  592. //Add the msg to the appropriate queue
  593. if (fEnqueue)
  594. hr = pfqQueue->HrEnqueue(pmsgref);
  595. else
  596. hr = pfqQueue->HrRequeue(pmsgref);
  597. //If the enqueue/requeue succeeded, update the stats. DO NOT update the
  598. //stats before the enqueue/requeue attempt. This will cause the link to
  599. //wake up and start spinning off connections before the msg is enqueued.
  600. //See bug 88931
  601. if (SUCCEEDED(hr))
  602. UpdateMsgStats(pmsgref, TRUE);
  603. InterlockedDecrement((PLONG) &m_cCurrentThreadsEnqueuing);
  604. Exit:
  605. TraceFunctLeave();
  606. return hr;
  607. }
  608. //---[ CDestMsgQueue::HrDequeueMsg ]-------------------------------------------
  609. //
  610. //
  611. // Description:
  612. // Finds and dequeues the next message. All OUT parameters are
  613. // ref-counted. The call is responsible for there release
  614. // Parameters:
  615. // IN priLowestPriority - Lowest priority message allowed
  616. // OUT ppmsgref - MsgRef dequeued
  617. // OUT ppdmrq - Ptr to retry interface (can be NULL)
  618. // Returns:
  619. // NO_ERROR if successful
  620. // AQUEUE_E_QUEUE_EMPTY if no messages in queue
  621. //-----------------------------------------------------------------------------
  622. HRESULT CDestMsgQueue::HrDequeueMsg(
  623. IN DWORD priLowestPriority,
  624. OUT CMsgRef **ppmsgref,
  625. OUT CDestMsgRetryQueue **ppdmrq)
  626. {
  627. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::HrDequeueMsg");
  628. Assert(ppmsgref);
  629. HRESULT hr = S_OK;
  630. DWORD priCurrent = eEffPriHigh; //start at highest priority
  631. *ppmsgref = NULL;
  632. Assert(priCurrent < NUM_PRIORITIES);
  633. hr = AQUEUE_E_QUEUE_EMPTY;
  634. while (TRUE)
  635. {
  636. if (NULL != m_rgpfqQueues[priCurrent])
  637. {
  638. hr = m_rgpfqQueues[priCurrent]->HrDequeue(ppmsgref);
  639. if (SUCCEEDED(hr))
  640. {
  641. if ((*ppmsgref)->fIsMsgFrozen())
  642. {
  643. //Msg is frozen, we need to put it in
  644. //the retry queue and get the next one
  645. //We must call UpdateMsgStats,because
  646. //MergeRetryQueue will re-add it.
  647. UpdateMsgStats(*ppmsgref, FALSE);
  648. hr = HrRetryMsg(*ppmsgref);
  649. if (FAILED(hr))
  650. goto Exit;
  651. (*ppmsgref)->Release();
  652. *ppmsgref = NULL;
  653. continue;
  654. }
  655. else
  656. {
  657. break;
  658. }
  659. }
  660. else if (hr != AQUEUE_E_QUEUE_EMPTY)
  661. {
  662. //some unexpected error has occured
  663. goto Exit;
  664. }
  665. }
  666. //otherwise decrement the priority
  667. if (priCurrent == eEffPriLow)
  668. break;
  669. //
  670. // If there are no messages at the current lowest priority,
  671. // then treat this queue as empty
  672. //
  673. if (priCurrent <= priLowestPriority)
  674. {
  675. hr = AQUEUE_E_QUEUE_EMPTY;
  676. goto Exit;
  677. }
  678. Assert(eEffPriLow < priCurrent);
  679. priCurrent--;
  680. }
  681. if (FAILED(hr))
  682. goto Exit;
  683. Assert(*ppmsgref);
  684. //Before we update stats. AddRef the retry interface so there is
  685. //no timing window where the queue is erroniously marked as empty
  686. if (ppdmrq)
  687. {
  688. *ppdmrq = &m_dmrq;
  689. m_dmrq.AddRef();
  690. }
  691. UpdateMsgStats(*ppmsgref, FALSE);
  692. //approximate oldest
  693. UpdateOldest((*ppmsgref)->pftGetAge());
  694. Exit:
  695. TraceFunctLeave();
  696. return hr;
  697. }
  698. //---[ CDestMsgQueue::UpdateMsgStats ]---------------------------------------
  699. //
  700. //
  701. // Description:
  702. // Updates stats. A shared lock must be aquired before calling into this.
  703. // Parameters:
  704. // IN pmsgref - message reference added or removed
  705. // IN fAdd - TRUE => msgref is being added the queue
  706. // FALSE => msgref is being removed from the queue
  707. // Returns:
  708. // -
  709. //
  710. //-----------------------------------------------------------------------------
  711. void CDestMsgQueue::UpdateMsgStats(IN CMsgRef *pmsgref, IN BOOL fAdd)
  712. {
  713. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::UpdateMsgStats");
  714. Assert(pmsgref);
  715. CAQStats aqstats;
  716. if (fAdd)
  717. {
  718. m_paqinst->IncQueueMsgInstances();
  719. }
  720. else
  721. {
  722. m_paqinst->DecQueueMsgInstances();
  723. }
  724. aqstats.m_cMsgs = 1;
  725. aqstats.m_rgcMsgPriorities[pmsgref->PriGetPriority()] = 1;
  726. aqstats.m_uliVolume.QuadPart = (ULONGLONG) pmsgref->dwGetMsgSize();
  727. aqstats.m_pdmq = this;
  728. aqstats.m_dwNotifyType = NotifyTypeDestMsgQueue;
  729. aqstats.m_dwHighestPri = pmsgref->PriGetPriority();
  730. //Keep track of the number of *other* domains this is being sent to, so
  731. //that we can make an accurate guess when to create connections
  732. aqstats.m_cOtherDomainsMsgSpread = pmsgref->cGetNumDomains()-1;
  733. //
  734. // Make sure that our stats are within reason. We expect to be negative
  735. // for short periods of time, but never more negative than the
  736. // number of threads currently enqueueing.
  737. //
  738. _ASSERT(m_aqstats.m_cMsgs+m_cCurrentThreadsEnqueuing < 0xFFFFFFF0);
  739. m_slPrivateData.ShareLock();
  740. m_aqstats.UpdateStats(&aqstats, fAdd);
  741. //send notification off to link
  742. if (m_plmq)
  743. {
  744. //Caller does not care about success of notification... only
  745. //about updating stats
  746. m_plmq->HrNotify(&aqstats, fAdd);
  747. }
  748. m_slPrivateData.ShareUnlock();
  749. TraceFunctLeave();
  750. }
  751. //---[ CDestMsgQueue::UpdateRetryStats ]---------------------------------------
  752. //
  753. //
  754. // Description:
  755. // Updates retry stats. A shared lock must be aquired before calling into this.
  756. // Parameters:
  757. // IN fAdd - TRUE => msgref is being added the queue
  758. // FALSE => msgref is being removed from the queue
  759. // Returns:
  760. // -
  761. //
  762. //-----------------------------------------------------------------------------
  763. void CDestMsgQueue::UpdateRetryStats(IN BOOL fAdd)
  764. {
  765. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::UpdateRetryStats");
  766. if (fAdd)
  767. {
  768. m_paqinst->IncQueueMsgInstances();
  769. }
  770. else
  771. {
  772. m_paqinst->DecQueueMsgInstances();
  773. }
  774. m_slPrivateData.ShareLock();
  775. m_aqstats.UpdateRetryStats(fAdd);
  776. if (m_plmq)
  777. {
  778. //Caller does not care about success of notification... only
  779. //about updating stats
  780. m_plmq->HrNotifyRetryStatChange(fAdd);
  781. }
  782. m_slPrivateData.ShareUnlock();
  783. TraceFunctLeave();
  784. }
  785. //---[ CDestMsgQueue::HrRetryMsg ]---------------------------------------------
  786. //
  787. //
  788. // Description:
  789. // Add an message to the queue for retry. This will put a message in
  790. // a retry queue (that is not usually checked during HrDequeueMessage)
  791. // Parameters:
  792. // IN pmsgref Message to add to the queue for retry
  793. // Returns:
  794. // S_OK on success
  795. //
  796. //-----------------------------------------------------------------------------
  797. HRESULT CDestMsgQueue::HrRetryMsg(IN CMsgRef *pmsgref)
  798. {
  799. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::HrRetryMsg");
  800. HRESULT hr = S_OK;
  801. _ASSERT(pmsgref);
  802. hr = m_fqRetryQueue.HrRequeue(pmsgref);
  803. //If we couldn't put it in retry queue... retry when all references
  804. //have been released
  805. if (FAILED(hr))
  806. pmsgref->RetryOnDelete();
  807. else
  808. UpdateRetryStats(true);
  809. hr = S_OK;
  810. TraceFunctLeave();
  811. return hr;
  812. }
  813. //---[ CDestMsgQueue::MarkQueueEmptyIfNecessary ]------------------------------
  814. //
  815. //
  816. // Description:
  817. // Checks and sees if it is OK to mark the queue as empty. Will
  818. // insert it in the empty list if needed.
  819. // If queue is now empty (and not tagged as empty), then we need to put
  820. // it in the empty queue list. If it is already tagged as empty, then
  821. // it is already in the empty queue list with the appropirate expire time.
  822. // Parameters:
  823. // -
  824. // Returns:
  825. // -
  826. // History:
  827. // 10/25/1999 - MikeSwa Created (separated from MergeRetryQueue())
  828. //
  829. //-----------------------------------------------------------------------------
  830. void CDestMsgQueue::MarkQueueEmptyIfNecessary()
  831. {
  832. //A queue cannot be considered empty if any of the following conditions
  833. // - There are messages queued up for delivery
  834. // - There are messages pending ack (someone has a reference to the
  835. // retry interface)
  836. // - There are messages pending retry
  837. // if we can't get the shutdown lock then there is no reason to mark the
  838. // queue as empty, since it will go away when we shutdown
  839. if (m_paqinst->fTryShutdownLock()) {
  840. //To be thread safe we should check in the opposite order that they
  841. //are set/unset. On dequeue, we add a ref count, then update stats. On
  842. //retry we update the retry stats, and then release.
  843. //
  844. if (!m_aqstats.m_cMsgs &&
  845. !m_dmrq.m_cRetryReferenceCount &&
  846. !m_fqRetryQueue.cGetCount() &&
  847. !(m_dwFlags & DMQ_EMPTY))
  848. {
  849. m_paqinst->pdmtGetDMT()->AddDMQToEmptyList(this);
  850. }
  851. m_paqinst->ShutdownUnlock();
  852. }
  853. }
  854. //---[ CDestMsgQueue::HrGenerateDSNsIfNecessary ]-----------------------------
  855. //
  856. //
  857. // Description:
  858. // Merge Messages from retry queue into main priority queues and
  859. // generates DSNs if neccessary.
  860. // Parameters:
  861. // IN pqlQueues List of queues to pass to DSN code
  862. // IN hrConnectionStatus HRESULT that should be passed to DSN generation
  863. // code.
  864. // IN OUT pdwContext Context that is used to throttle
  865. // DSN generation. Should be initialzed to
  866. // 0 on first call. Actually used to store
  867. // the tick count when we started DSN generation
  868. // Returns:
  869. // Failures will be handled internally
  870. // S_OK - success, and all messages processed
  871. // HRESULT_FROM_WIN32(E_PENDING) - more messages left to processes
  872. // History:
  873. // 11/10/1999 - MikeSwa Modified to return pending error
  874. //
  875. //-----------------------------------------------------------------------------
  876. HRESULT CDestMsgQueue::HrGenerateDSNsIfNecessary(IN CQuickList *pqlQueues,
  877. IN HRESULT hrConnectionStatus,
  878. IN OUT DWORD *pdwContext)
  879. {
  880. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::GenerateDSNsIfNecessary");
  881. HRESULT hr = S_OK;
  882. int i = 0;
  883. DEST_QUEUE_CONTEXT dqcontext(pdwContext, this, pqlQueues, hrConnectionStatus);
  884. //Re-merge retry queue
  885. MergeRetryQueue();
  886. //Check re-try queue as well since we may have frozen messages that need to
  887. //be NDR'd or DSN'd
  888. // this is on a retry queue, HrWalkDMQForDSN should call UpdateMsgStats
  889. dqcontext.SetRetry(TRUE);
  890. hr = m_fqRetryQueue.HrMapFn(CDestMsgQueue::HrWalkDMQForDSN, &dqcontext, NULL);
  891. if (FAILED(hr))
  892. {
  893. if (HRESULT_FROM_WIN32(E_PENDING) == hr)
  894. {
  895. DebugTrace((LPARAM) this,
  896. "Hit DSN generation limit, must continue DSN genration later");
  897. goto Exit;
  898. }
  899. ErrorTrace((LPARAM) this,
  900. "ERROR: Unable to Check Queues for DSNs - hr 0x%08X", hr);
  901. hr = S_OK;
  902. }
  903. dqcontext.SetRetry(FALSE);
  904. for (i = 0; i < NUM_PRIORITIES; i++)
  905. {
  906. if (NULL != m_rgpfqQueues[i])
  907. {
  908. hr = m_rgpfqQueues[i]->HrMapFn(CDestMsgQueue::HrWalkDMQForDSN,
  909. &dqcontext, NULL);
  910. if (FAILED(hr))
  911. {
  912. if (HRESULT_FROM_WIN32(E_PENDING) == hr)
  913. {
  914. DebugTrace((LPARAM) this,
  915. "Hit msg limit, must continue DSN genration later");
  916. goto Exit;
  917. }
  918. ErrorTrace((LPARAM) this,
  919. "ERROR: Unable to Check Queues for DSNs - hr 0x%08X", hr);
  920. hr = S_OK;
  921. }
  922. }
  923. }
  924. //
  925. // If we where checking for stale messages, we should stop until we
  926. // hit another stale message on a message ack
  927. //
  928. dwInterlockedUnsetBits(&m_dwFlags, DMQ_CHECK_FOR_STALE_MSGS);
  929. Exit:
  930. MarkQueueEmptyIfNecessary();
  931. TraceFunctLeave();
  932. return hr;
  933. }
  934. //---[ CDestMsgQueue::MergeRetryQueue ]------------------------------------------
  935. //
  936. //
  937. // Description:
  938. // Merges retry queues with normal queues. Will keep frozen msgs in
  939. // retry queue.
  940. // Parameters:
  941. // -
  942. // Returns:
  943. // -
  944. // History:
  945. // 12/13/98 - MikeSwa split from original MergeRetryQueue
  946. // (now called GenerateDSNsIfNecessary)
  947. //
  948. //-----------------------------------------------------------------------------
  949. void CDestMsgQueue::MergeRetryQueue()
  950. {
  951. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::MergeRetryQueue");
  952. HRESULT hr = S_OK;
  953. CMsgRef *pmsgref = NULL;
  954. CMsgRef *pmsgrefFirstFrozen = NULL;
  955. DWORD cMsgsInRetry = m_fqRetryQueue.cGetCount();
  956. DWORD cMsgsProcessed = 0;
  957. while (SUCCEEDED(hr))
  958. {
  959. //While we have a mechanism to loop through the queue only once by
  960. //checking the pmsgrefFirstFrozen pointer. It is possible that another
  961. //thread will remove that from the queue (unfreeze, NDR, etc), so it
  962. //is important that we have a failsafe mechanism. Worst case here, is
  963. //that we will see every message twice, but lets us handle extra
  964. //messages added to the retry queue.
  965. if (cMsgsProcessed++ > 2*cMsgsInRetry)
  966. break;
  967. hr = m_fqRetryQueue.HrDequeue(&pmsgref);
  968. if (FAILED(hr))
  969. break;
  970. UpdateRetryStats(false);
  971. //Handle frozen messages sitting in the retry queue
  972. if (pmsgref->fIsMsgFrozen())
  973. {
  974. //Message is frozen, we are keeping it in the retry queue
  975. hr = m_fqRetryQueue.HrEnqueue(pmsgref);
  976. if (FAILED(hr))
  977. {
  978. //Mark Msgref as retry
  979. pmsgref->RetryOnDelete();
  980. ErrorTrace((LPARAM) this,
  981. "ERROR: Unable to add frozen msg to retry queue - msg 0x%X", pmsgref);
  982. }
  983. else
  984. {
  985. UpdateRetryStats(true);
  986. }
  987. pmsgref->Release();
  988. //See if we've made it all the way through the retry queue
  989. if (!pmsgrefFirstFrozen)
  990. pmsgrefFirstFrozen = pmsgref;
  991. else if (pmsgref == pmsgrefFirstFrozen)
  992. break;
  993. }
  994. else
  995. {
  996. //Re-queue non-frozen message for delivery
  997. hr = HrAddMsg(pmsgref, FALSE, FALSE);
  998. if (FAILED(hr))
  999. {
  1000. pmsgref->RetryOnDelete();
  1001. ErrorTrace((LPARAM) this, "ERROR: Unable to merge retry queue - msg 0x%X", pmsgref);
  1002. }
  1003. pmsgref->Release();
  1004. }
  1005. }
  1006. MarkQueueEmptyIfNecessary();
  1007. TraceFunctLeave();
  1008. }
  1009. //---[ CDestMsgQueue::RemoveDMQFromLink ]--------------------------------------
  1010. //
  1011. //
  1012. // Description:
  1013. // Removes this DMQ from its associated link
  1014. // Parameters:
  1015. // fNotifyLink TRUE if not being called by owning link, and link needs
  1016. // to be notified
  1017. // Returns:
  1018. // -
  1019. // History:
  1020. // 9/14/98 - MikeSwa Created
  1021. // 11/6/98 - MikeSwa Modified to allow changes to routing info
  1022. //
  1023. //-----------------------------------------------------------------------------
  1024. void CDestMsgQueue::RemoveDMQFromLink(BOOL fNotifyLink)
  1025. {
  1026. _ASSERT(DESTMSGQ_SIG == m_dwSignature);
  1027. CLinkMsgQueue *plmq = NULL;
  1028. CAQStats aqstats;
  1029. m_slPrivateData.ExclusiveLock();
  1030. plmq = m_plmq;
  1031. m_plmq = NULL;
  1032. if (plmq && fNotifyLink)
  1033. memcpy(&aqstats, &m_aqstats, sizeof(CAQStats));
  1034. m_slPrivateData.ExclusiveUnlock();
  1035. if (plmq)
  1036. {
  1037. if (fNotifyLink)
  1038. plmq->RemoveQueue(this, &aqstats);
  1039. plmq->Release();
  1040. }
  1041. }
  1042. //---[ CDestMsgQueue::SetRouteInfo ]-------------------------------------------
  1043. //
  1044. //
  1045. // Description:
  1046. // Sets the routing information for this domain. Will blow away any
  1047. // previous routing info.
  1048. // Parameters:
  1049. // IN plmq Link to associate with this domain.
  1050. // Returns:
  1051. // -
  1052. // History:
  1053. // 11/6/98 - MikeSwa Created
  1054. //
  1055. //-----------------------------------------------------------------------------
  1056. void CDestMsgQueue::SetRouteInfo(CLinkMsgQueue *plmq)
  1057. {
  1058. TraceFunctEnterEx((LPARAM) this, "CDestMsgQueue::SetRouteInfo");
  1059. HRESULT hr = S_OK;
  1060. CAQStats aqstats;
  1061. //First blow-away old routing info
  1062. RemoveDMQFromLink(TRUE);
  1063. //Grab lock and update routing info
  1064. m_slPrivateData.ExclusiveLock();
  1065. m_plmq = plmq;
  1066. if (plmq)
  1067. {
  1068. plmq->AddRef();
  1069. memcpy(&aqstats, &m_aqstats, sizeof(CAQStats));
  1070. aqstats.m_dwNotifyType |= NotifyTypeDestMsgQueue;
  1071. hr = plmq->HrNotify(&aqstats, TRUE);
  1072. if (FAILED(hr))
  1073. {
  1074. //nothing really we can do
  1075. ErrorTrace((LPARAM) this,
  1076. "ERROR: Unable to update link stats - hr 0x%08X", hr);
  1077. }
  1078. }
  1079. m_slPrivateData.ExclusiveUnlock();
  1080. TraceFunctLeave();
  1081. }
  1082. //---[ CDestMsgQueue::plmqGetLink ]--------------------------------------------
  1083. //
  1084. //
  1085. // Description:
  1086. // Returns the Addref'd Link for the Queue.
  1087. // Parameters:
  1088. // -
  1089. // Returns:
  1090. // Ptr to CLinkMsgQueue
  1091. // History:
  1092. // 5/14/99 - MikeSwa Created
  1093. //
  1094. //-----------------------------------------------------------------------------
  1095. CLinkMsgQueue *CDestMsgQueue::plmqGetLink()
  1096. {
  1097. CLinkMsgQueue *plmq = NULL;
  1098. m_slPrivateData.ShareLock();
  1099. plmq = m_plmq;
  1100. if (plmq)
  1101. plmq->AddRef();
  1102. m_slPrivateData.ShareUnlock();
  1103. return plmq;
  1104. }
  1105. //---[ CDestMsgQueue::HrGetQueueID ]--------------------------------------------
  1106. //
  1107. //
  1108. // Description:
  1109. // Gets the QueueID for this DMQ. Used by Queue Admin
  1110. // Parameters:
  1111. // IN OUT pQueueID QUEUELINK_ID struct to fill in
  1112. // Returns:
  1113. // S_OK on success
  1114. // E_OUTOFMEMORY if unable to allocate memory for queue name.
  1115. // History:
  1116. // 12/3/98 - MikeSwa Created
  1117. // 2/23/99 - MikeSwa Modified to be part of IQueueAdminQueue interface
  1118. //
  1119. //-----------------------------------------------------------------------------
  1120. STDMETHODIMP CDestMsgQueue::HrGetQueueID(QUEUELINK_ID *pQueueID)
  1121. {
  1122. DWORD cbDomainName = m_dmap.pdentryGetQueueEntry()->cbGetDomainNameLength();
  1123. LPSTR szDomainName = m_dmap.pdentryGetQueueEntry()->szGetDomainName();
  1124. pQueueID->qltType = QLT_QUEUE;
  1125. pQueueID->dwId = m_aqmt.dwGetMessageType();
  1126. m_aqmt.GetGUID(&pQueueID->uuid);
  1127. pQueueID->szName = wszQueueAdminConvertToUnicode(szDomainName,
  1128. cbDomainName);
  1129. if (!pQueueID->szName)
  1130. return E_OUTOFMEMORY;
  1131. return S_OK;
  1132. }
  1133. //---[ CDestMsgQueue::HrGetQueueInfo ]------------------------------------------
  1134. //
  1135. //
  1136. // Description:
  1137. // Gets the Queue Admin infor for this Queue
  1138. // Parameters:
  1139. // IN OUT pqiQueueInfo Ptr to Queue Info Stucture to fill
  1140. // Returns:
  1141. // S_OK on success
  1142. // E_OUTOFMEMORY if unable to allocate memory for queue name.
  1143. // History:
  1144. // 12/5/98 - MikeSwa Created
  1145. // 2/22/99 - MikeSwa changed to COM function
  1146. //
  1147. //-----------------------------------------------------------------------------
  1148. STDMETHODIMP CDestMsgQueue::HrGetQueueInfo(QUEUE_INFO *pqiQueueInfo)
  1149. {
  1150. DWORD cbDomainName = m_dmap.pdentryGetQueueEntry()->cbGetDomainNameLength();
  1151. LPSTR szDomainName = m_dmap.pdentryGetQueueEntry()->szGetDomainName();
  1152. HRESULT hr = S_OK;
  1153. //Get # of messages = # in queue + failed msgs
  1154. pqiQueueInfo->cMessages = m_aqstats.m_cMsgs + cGetFailedMsgs();
  1155. //Get DMQ name
  1156. pqiQueueInfo->szQueueName = wszQueueAdminConvertToUnicode(szDomainName,
  1157. cbDomainName);
  1158. if (!pqiQueueInfo->szQueueName)
  1159. {
  1160. hr = E_OUTOFMEMORY;
  1161. goto Exit;
  1162. }
  1163. pqiQueueInfo->cbQueueVolume.QuadPart = m_aqstats.m_uliVolume.QuadPart;
  1164. pqiQueueInfo->dwMsgEnumFlagsSupported = AQUEUE_DEFAULT_SUPPORTED_ENUM_FILTERS;
  1165. //Get Link name
  1166. m_slPrivateData.ShareLock();
  1167. if (m_plmq && !m_plmq->fRPCCopyName(&pqiQueueInfo->szLinkName))
  1168. hr = E_OUTOFMEMORY;
  1169. m_slPrivateData.ShareUnlock();
  1170. Exit:
  1171. return hr;
  1172. }
  1173. //---[ CDestMsgQueue::UpdateOldest ]-------------------------------------------
  1174. //
  1175. //
  1176. // Description:
  1177. // Updates the age of the "oldest" message in the queue
  1178. // Parameters:
  1179. // pft Ptr to filetime of oldest nessage
  1180. // Returns:
  1181. // -
  1182. // History:
  1183. // 12/13/98 - MikeSwa Created
  1184. //
  1185. //-----------------------------------------------------------------------------
  1186. void CDestMsgQueue::UpdateOldest(FILETIME *pft)
  1187. {
  1188. DWORD dwFlags = dwInterlockedSetBits(&m_dwFlags, DMQ_UPDATING_OLDEST_TIME);
  1189. if (!(DMQ_UPDATING_OLDEST_TIME & dwFlags))
  1190. {
  1191. //we got the spin lock
  1192. memcpy(&m_ftOldest, pft, sizeof(FILETIME));
  1193. dwInterlockedUnsetBits(&m_dwFlags, DMQ_UPDATING_OLDEST_TIME);
  1194. }
  1195. }
  1196. //---[ CDestMsgQueue::QueryInterface ]-----------------------------------------
  1197. //
  1198. //
  1199. // Description:
  1200. // QueryInterface for CDestMsgQueue that supports:
  1201. // - IQueueAdminAction
  1202. // - IUnknown
  1203. // - IQueueAdminQueue
  1204. // Parameters:
  1205. //
  1206. // Returns:
  1207. //
  1208. // History:
  1209. // 2/21/99 - MikeSwa Created
  1210. //
  1211. //-----------------------------------------------------------------------------
  1212. STDMETHODIMP CDestMsgQueue::QueryInterface(REFIID riid, LPVOID *ppvObj)
  1213. {
  1214. HRESULT hr = S_OK;
  1215. if (!ppvObj)
  1216. {
  1217. hr = E_POINTER;
  1218. goto Exit;
  1219. }
  1220. if (IID_IUnknown == riid)
  1221. {
  1222. *ppvObj = static_cast<IQueueAdminAction *>(this);
  1223. }
  1224. else if (IID_IQueueAdminAction == riid)
  1225. {
  1226. *ppvObj = static_cast<IQueueAdminAction *>(this);
  1227. }
  1228. else if (IID_IQueueAdminQueue == riid)
  1229. {
  1230. *ppvObj = static_cast<IQueueAdminQueue *>(this);
  1231. }
  1232. else
  1233. {
  1234. *ppvObj = NULL;
  1235. hr = E_NOINTERFACE;
  1236. goto Exit;
  1237. }
  1238. static_cast<IUnknown *>(*ppvObj)->AddRef();
  1239. Exit:
  1240. return hr;
  1241. }
  1242. //---[ CDestMsgQueue::HrApplyQueueAdminFunction ]------------------------------
  1243. //
  1244. //
  1245. // Description:
  1246. // Will call the IQueueAdminMessageFilter::Process message for every
  1247. // message in this queue. If the message passes the filter, then
  1248. // HrApplyActionToMessage on this object will be called.
  1249. // Parameters:
  1250. // IN pIQueueAdminMessageFilter
  1251. // Returns:
  1252. // S_OK on success
  1253. // History:
  1254. // 2/21/99 - MikeSwa Created
  1255. // 4/1/99 - MikeSwa Merged implementations of ApplyQueueAdminFunction
  1256. //
  1257. //-----------------------------------------------------------------------------
  1258. STDMETHODIMP CDestMsgQueue::HrApplyQueueAdminFunction(
  1259. IQueueAdminMessageFilter *pIQueueAdminMessageFilter)
  1260. {
  1261. HRESULT hr = S_OK;
  1262. DWORD i = 0;
  1263. DWORD dwFlags = 0;
  1264. PVOID pvOldContext = NULL;
  1265. CQueueAdminContext qapictx(NULL, m_paqinst);
  1266. _ASSERT(pIQueueAdminMessageFilter);
  1267. hr = pIQueueAdminMessageFilter->HrSetQueueAdminAction(
  1268. (IQueueAdminAction *) this);
  1269. //This is an internal interface that should not fail
  1270. _ASSERT(SUCCEEDED(hr) && "HrSetQueueAdminAction");
  1271. if (FAILED(hr))
  1272. goto Exit;
  1273. pIQueueAdminMessageFilter->HrGetCurrentUserContext(&pvOldContext);
  1274. pIQueueAdminMessageFilter->HrSetCurrentUserContext(&qapictx);
  1275. //Apply action to every queue in DMQ
  1276. dwInterlockedSetBits(&m_dwFlags, DMQ_QUEUE_ADMIN_OP_PENDING);
  1277. //Map function on retry queue first, because that will make display
  1278. //order more consistant, since messages that where at the front of
  1279. //the queue, will be in the retry queue for retry errors.
  1280. qapictx.SetQueueState(LI_RETRY);
  1281. hr = m_fqRetryQueue.HrMapFn(QueueAdminApplyActionToMessages,
  1282. pIQueueAdminMessageFilter, NULL);
  1283. qapictx.SetQueueState(LI_READY);
  1284. if (FAILED(hr))
  1285. goto Exit;
  1286. for (i = 0; i < NUM_PRIORITIES; i++)
  1287. {
  1288. if (NULL != m_rgpfqQueues[i])
  1289. {
  1290. hr = m_rgpfqQueues[i]->HrMapFn(QueueAdminApplyActionToMessages,
  1291. pIQueueAdminMessageFilter, NULL);
  1292. if (FAILED(hr))
  1293. {
  1294. goto Exit;
  1295. }
  1296. }
  1297. }
  1298. Exit:
  1299. dwFlags = dwInterlockedUnsetBits(&m_dwFlags, DMQ_QUEUE_ADMIN_OP_PENDING);
  1300. //
  1301. // NOTE - By doing this here, we will only check DMQ's for which
  1302. // explicit queue admin actions have happened. Other DMQ's will wait
  1303. // for retry or another QAPI action.
  1304. //
  1305. if (qapictx.cGetNumThawedMsgs())
  1306. {
  1307. //We need to walk the retry queue for thawed msgs. We have to do
  1308. //it here, because otherwise we might deadlock if this thread called
  1309. //MergeRetryQueue() from within the HrMapFn
  1310. MergeRetryQueue();
  1311. }
  1312. //Restore inital context
  1313. pIQueueAdminMessageFilter->HrSetCurrentUserContext(pvOldContext);
  1314. return hr;
  1315. }
  1316. //---[ CDestMsgQueue::HrApplyActionToMessage ]---------------------------------
  1317. //
  1318. //
  1319. // Description:
  1320. // Applies an action to this message for this queue. This will be called
  1321. // by the IQueueAdminMessageFilter during a queue enumeration function.
  1322. // Parameters:
  1323. // IN *pIUnknownMsg ptr to message abstraction
  1324. // IN ma Message action to perform
  1325. // IN pvContext Context set on IQueueAdminFilter
  1326. // OUT pfShouldDelete TRUE if the message should be deleted
  1327. // Returns:
  1328. // S_OK on success
  1329. // History:
  1330. // 2/21/99 - MikeSwa Created
  1331. // 4/2/99 - MikeSwa Added context
  1332. //
  1333. //-----------------------------------------------------------------------------
  1334. STDMETHODIMP CDestMsgQueue::HrApplyActionToMessage(
  1335. IUnknown *pIUnknownMsg,
  1336. MESSAGE_ACTION ma,
  1337. PVOID pvContext,
  1338. BOOL *pfShouldDelete)
  1339. {
  1340. HRESULT hr = S_OK;
  1341. CMsgRef *pmsgref = (CMsgRef *)pIUnknownMsg;
  1342. CQueueAdminContext *pqapictx = (CQueueAdminContext *)pvContext;
  1343. BOOL fUpdateStats = TRUE;
  1344. _ASSERT(pmsgref);
  1345. _ASSERT(pfShouldDelete);
  1346. _ASSERT(pqapictx);
  1347. _ASSERT(pqapictx && pqapictx->fIsValid());
  1348. if (pqapictx && !pqapictx->fIsValid())
  1349. pqapictx = NULL;
  1350. if (pqapictx && (LI_RETRY == pqapictx->lfGetQueueState()))
  1351. {
  1352. //We should not update stats if we are working on the retry queue
  1353. //instead we should call UpdateRetryStats if this is a retry queue
  1354. fUpdateStats = FALSE;
  1355. }
  1356. *pfShouldDelete = FALSE;
  1357. switch (ma)
  1358. {
  1359. case MA_DELETE:
  1360. hr = pmsgref->HrQueueAdminNDRMessage((CDestMsgQueue *)this);
  1361. *pfShouldDelete = TRUE;
  1362. break;
  1363. case MA_DELETE_SILENT:
  1364. hr = pmsgref->HrRemoveMessageFromQueue((CDestMsgQueue *)this);
  1365. *pfShouldDelete = TRUE;
  1366. break;
  1367. case MA_FREEZE_GLOBAL:
  1368. pmsgref->GlobalFreezeMessage();
  1369. break;
  1370. case MA_THAW_GLOBAL:
  1371. pmsgref->GlobalThawMessage();
  1372. //
  1373. // Mark this queue as one to check for thawed messages.
  1374. //
  1375. if (pqapictx)
  1376. pqapictx->IncThawedMsgs();
  1377. break;
  1378. case MA_COUNT:
  1379. default:
  1380. //do nothing for counting and default
  1381. break;
  1382. }
  1383. if (*pfShouldDelete && SUCCEEDED(hr) ) {
  1384. if (fUpdateStats)
  1385. {
  1386. UpdateMsgStats(pmsgref, FALSE);
  1387. MarkQueueEmptyIfNecessary();
  1388. }
  1389. else
  1390. {
  1391. UpdateRetryStats(FALSE);
  1392. }
  1393. }
  1394. return hr;
  1395. }
  1396. //---[ CDestMsgQueue::fMatchesID ]---------------------------------------------
  1397. //
  1398. //
  1399. // Description:
  1400. // Used to determine if this link matches a given scheduleID/link pair
  1401. // Parameters:
  1402. // IN QueueLinkID ID to match against
  1403. // Returns:
  1404. // TRUE if it matches
  1405. // FALSE if it does not
  1406. // History:
  1407. // 2/23/99 - MikeSwa Created
  1408. //
  1409. //-----------------------------------------------------------------------------
  1410. BOOL STDMETHODCALLTYPE CDestMsgQueue::fMatchesID(QUEUELINK_ID *pQueueLinkID)
  1411. {
  1412. //This is not used at the DMQ level
  1413. _ASSERT(0 && "Not implemented");
  1414. return E_NOTIMPL;
  1415. }
  1416. //---[ CDestMsgQueue::SendLinkStateNotification ]------------------------------
  1417. //
  1418. //
  1419. // Description:
  1420. // Sends link state notification saying that the link was created.
  1421. // Parameters:
  1422. // -
  1423. // Returns:
  1424. // -
  1425. // History:
  1426. // 8/18/99 - AWetmore Created
  1427. //
  1428. //-----------------------------------------------------------------------------
  1429. void CDestMsgQueue::SendLinkStateNotification(void) {
  1430. CLinkMsgQueue *plmq = plmqGetLink();
  1431. if (plmq) {
  1432. plmq->SendLinkStateNotificationIfNew();
  1433. plmq->Release();
  1434. }
  1435. }
  1436. //---[ CDestMsgQueue::fIsRemote ]----------------------------------------------
  1437. //
  1438. //
  1439. // Description:
  1440. // Determines if queue is routed remotely. Caller should have routing
  1441. // share lock.
  1442. // Parameters:
  1443. // -
  1444. // Returns:
  1445. // TRUE if link is routed remotely
  1446. // FALSE otherwise
  1447. // History:
  1448. // 11/29/1999 - MikeSwa Created
  1449. //
  1450. //-----------------------------------------------------------------------------
  1451. BOOL CDestMsgQueue::fIsRemote()
  1452. {
  1453. CLinkMsgQueue *plmq = plmqGetLink();
  1454. BOOL fIsRemote = FALSE;
  1455. if (plmq) {
  1456. fIsRemote = plmq->fIsRemote();
  1457. plmq->Release();
  1458. }
  1459. return fIsRemote;
  1460. }
  1461. //---[ CDestMsgRetryQueue::CheckForStaleMsgsNextDSNGenerationPass ]------------
  1462. //
  1463. //
  1464. // Description:
  1465. // Marks the queue as so that we will do the (expensive) check for
  1466. // stale messages during the next DSN generation pass.
  1467. // Parameters:
  1468. // -
  1469. // Returns:
  1470. // -
  1471. // History:
  1472. // 4/18/2000 - MikeSwa Created
  1473. //
  1474. //-----------------------------------------------------------------------------
  1475. VOID CDestMsgRetryQueue::CheckForStaleMsgsNextDSNGenerationPass()
  1476. {
  1477. _ASSERT(m_pdmq);
  1478. dwInterlockedSetBits(&(m_pdmq->m_dwFlags),
  1479. CDestMsgQueue::DMQ_CHECK_FOR_STALE_MSGS);
  1480. }