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.

487 lines
14 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: defdlvrq.cpp
  5. //
  6. // Description: Implementation of CAQDeferredDeliveryQueue &
  7. // CAQDeferredDeliveryQueueEntry.
  8. //
  9. // Author: Mike Swafford (MikeSwa)
  10. //
  11. // History:
  12. // 12/23/98 - MikeSwa Created
  13. //
  14. // Copyright (C) 1998 Microsoft Corporation
  15. //
  16. //-----------------------------------------------------------------------------
  17. #include "aqprecmp.h"
  18. #include "defdlvrq.h"
  19. #include "aqutil.h"
  20. //---[ CAQDeferredDeliveryQueueEntry::CAQDeferredDeliveryQueueEntry ]----------
  21. //
  22. //
  23. // Description:
  24. // Constructor for CAQDeferredDeliveryQueueEntry class
  25. // Parameters:
  26. // IN pIMailMsgProperties MailMsg to queue
  27. // IN pft FILTIME (UT) to defer deliver until
  28. // Returns:
  29. // -
  30. // History:
  31. // 12/28/98 - MikeSwa Created
  32. //
  33. //-----------------------------------------------------------------------------
  34. CAQDeferredDeliveryQueueEntry::CAQDeferredDeliveryQueueEntry(
  35. IMailMsgProperties *pIMailMsgProperties,
  36. FILETIME *pft)
  37. {
  38. _ASSERT(pIMailMsgProperties);
  39. _ASSERT(pft);
  40. m_liQueueEntry.Flink = NULL;
  41. m_liQueueEntry.Blink = NULL;
  42. memcpy(&m_ftDeferredDeilveryTime, pft, sizeof(FILETIME));
  43. m_pIMailMsgProperties = pIMailMsgProperties;
  44. if (m_pIMailMsgProperties)
  45. {
  46. m_pIMailMsgProperties->AddRef();
  47. //Release usage count while this message is pending delivery
  48. HrReleaseIMailMsgUsageCount(m_pIMailMsgProperties);
  49. }
  50. m_fCallbackSet = FALSE;
  51. m_dwSignature = DEFERRED_DELIVERY_QUEUE_ENTRY_SIG;
  52. }
  53. //---[ CAQDeferredDeliveryQueueEntry::~CAQDeferredDeliveryQueueEntry ]---------
  54. //
  55. //
  56. // Description:
  57. // Descructor for CAQDeferredDeliveryQueueEntry class
  58. // Parameters:
  59. // -
  60. // Returns:
  61. // -
  62. // History:
  63. // 12/28/98 - MikeSwa Created
  64. //
  65. //-----------------------------------------------------------------------------
  66. CAQDeferredDeliveryQueueEntry::~CAQDeferredDeliveryQueueEntry()
  67. {
  68. //Release mailmsg properties
  69. if (m_pIMailMsgProperties)
  70. {
  71. m_pIMailMsgProperties->Release();
  72. m_pIMailMsgProperties = NULL;
  73. }
  74. //Remove from list (if in list)
  75. if (m_liQueueEntry.Flink)
  76. {
  77. _ASSERT(m_liQueueEntry.Blink);
  78. RemoveEntryList(&m_liQueueEntry);
  79. }
  80. MARK_SIG_AS_DELETED(m_dwSignature);
  81. }
  82. //---[ CAQDeferredDeliveryQueueEntry::SetCallback ]----------------------------
  83. //
  84. //
  85. // Description:
  86. // Sets callback for queue. Per Entry state is maintained so we know we
  87. // have 1 and only 1 callback per head of queue.
  88. //
  89. // Queue private lock should be exclusive when this is called
  90. // Parameters:
  91. // pvContext Context for callback function
  92. // paqinst Server Instance object
  93. // Returns:
  94. // TRUE if a callback is set
  95. // History:
  96. // 1/13/99 - MikeSwa Created
  97. //
  98. //-----------------------------------------------------------------------------
  99. BOOL CAQDeferredDeliveryQueueEntry::fSetCallback(PVOID pvContext,
  100. CAQSvrInst *paqinst)
  101. {
  102. if (!m_fCallbackSet && paqinst)
  103. {
  104. m_fCallbackSet = TRUE;
  105. paqinst->SetCallbackTime(CAQDeferredDeliveryQueue::TimerCallback,
  106. pvContext, &m_ftDeferredDeilveryTime);
  107. return TRUE;
  108. }
  109. else
  110. {
  111. return FALSE;
  112. }
  113. }
  114. //---[ CAQDeferredDeliveryQueueEntry::pmsgGetMsg ]-----------------------------
  115. //
  116. //
  117. // Description:
  118. // Get AddRef'd message for this entry
  119. // Parameters:
  120. // -
  121. // Returns:
  122. // pIMailMsgProperties.
  123. // History:
  124. // 12/28/98 - MikeSwa Created
  125. //
  126. //-----------------------------------------------------------------------------
  127. IMailMsgProperties *CAQDeferredDeliveryQueueEntry::pmsgGetMsg()
  128. {
  129. _ASSERT(m_pIMailMsgProperties);
  130. IMailMsgProperties *pIMailMsgProperties = m_pIMailMsgProperties;
  131. if (pIMailMsgProperties)
  132. {
  133. //Add the usage count the we released earlier on
  134. HrIncrementIMailMsgUsageCount(m_pIMailMsgProperties);
  135. //Set to NULL, so caller "owns" this entry's reference count (and
  136. //usage count).
  137. m_pIMailMsgProperties = NULL;
  138. }
  139. return pIMailMsgProperties;
  140. }
  141. //---[ CAQDeferredDeliveryQueue::CAQDeferredDeliveryQueue ]--------------------
  142. //
  143. //
  144. // Description:
  145. // Constructor for CAQDeferredDeliveryQueue class
  146. // Parameters:
  147. // -
  148. // Returns:
  149. // -
  150. // History:
  151. // 12/28/98 - MikeSwa Created
  152. //
  153. //-----------------------------------------------------------------------------
  154. CAQDeferredDeliveryQueue::CAQDeferredDeliveryQueue()
  155. {
  156. m_dwSignature = DEFERRED_DELIVERY_QUEUE_SIG;
  157. InitializeListHead(&m_liQueueHead);
  158. m_paqinst = NULL;
  159. m_cCallbacksPending = 0;
  160. }
  161. //---[ CAQDeferredDeliveryQueue::~CAQDeferredDeliveryQueue ]-------------------
  162. //
  163. //
  164. // Description:
  165. // Default destructor for CAQDeferredDeliveryQueue.
  166. // Parameters:
  167. //
  168. // Returns:
  169. //
  170. // History:
  171. // 12/28/98 - MikeSwa Created
  172. //
  173. //-----------------------------------------------------------------------------
  174. CAQDeferredDeliveryQueue::~CAQDeferredDeliveryQueue()
  175. {
  176. Deinitialize();
  177. }
  178. //---[ CAQDeferredDeliveryQueue::Initialize ]----------------------------------
  179. //
  180. //
  181. // Description:
  182. // Initialization for CAQDeferredDeliveryQueue
  183. // Parameters:
  184. // IN paqinst Ptr to virtual server instance object
  185. // Returns:
  186. // -
  187. // History:
  188. // 12/29/98 - MikeSwa Created
  189. //
  190. //-----------------------------------------------------------------------------
  191. void CAQDeferredDeliveryQueue::Initialize(CAQSvrInst *paqinst)
  192. {
  193. _ASSERT(paqinst);
  194. m_paqinst = paqinst;
  195. m_paqinst->AddRef();
  196. }
  197. //---[ CAQDeferredDeliveryQueue::Deinitialize ]--------------------------------
  198. //
  199. //
  200. // Description:
  201. // Performs first-pass shutdown for CAQDeferredDeliveryQueue
  202. // Parameters:
  203. // -
  204. // Returns:
  205. // -
  206. // History:
  207. // 12/28/98 - MikeSwa Created
  208. //
  209. //-----------------------------------------------------------------------------
  210. void CAQDeferredDeliveryQueue::Deinitialize()
  211. {
  212. CAQDeferredDeliveryQueueEntry *pdefqe = NULL;
  213. LIST_ENTRY *pli = NULL;
  214. //$$REVIEW - It may be adventagious to remove this lock and rely on the
  215. //private data lock of the virtual server instance. This will require
  216. //fixing fTryRoutingLock. Also having a single lock leads to single-thread
  217. //deadlock issues while we have it exclusively and call to submit.
  218. m_slPrivateData.ExclusiveLock();
  219. pli = m_liQueueHead.Flink;
  220. //Walk queue and delete remaining entries
  221. while (pli != &m_liQueueHead)
  222. {
  223. pdefqe = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(pli);
  224. if (m_paqinst)
  225. m_paqinst->ServerStopHintFunction();
  226. //Make sure we get the next before deleting the entry :)
  227. pli = pli->Flink;
  228. _ASSERT(pdefqe);
  229. delete pdefqe;
  230. }
  231. if (m_paqinst)
  232. {
  233. m_paqinst->Release();
  234. m_paqinst = NULL;
  235. }
  236. m_slPrivateData.ExclusiveUnlock();
  237. }
  238. //---[ CAQDeferredDeliveryQueue::Enqueue ]-------------------------------------
  239. //
  240. //
  241. // Description:
  242. // Enqueues a message for deferred delivery
  243. // Parameters:
  244. // IN pIMailMsgProperties message to defer
  245. // IN pft FILETIME to defer delivery too
  246. // Returns:
  247. // - Failures are handled internally
  248. // History:
  249. // 12/28/98 - MikeSwa Created
  250. //
  251. //-----------------------------------------------------------------------------
  252. void CAQDeferredDeliveryQueue::Enqueue(IMailMsgProperties *pIMailMsgProperties,
  253. FILETIME *pft)
  254. {
  255. CAQDeferredDeliveryQueueEntry *pdefqeCurrent = NULL;
  256. CAQDeferredDeliveryQueueEntry *pdefqeNew = NULL;
  257. LIST_ENTRY *pli = NULL;
  258. LARGE_INTEGER *pLargeIntCurrentDeferredTime = NULL;
  259. LARGE_INTEGER *pLargeIntNewDeferredTime = (LARGE_INTEGER *)pft;
  260. _ASSERT(pIMailMsgProperties);
  261. _ASSERT(pft);
  262. pdefqeNew = new CAQDeferredDeliveryQueueEntry(pIMailMsgProperties, pft);
  263. m_slPrivateData.ExclusiveLock();
  264. if (!pdefqeNew)
  265. {
  266. //Handle Out of memory situation
  267. _ASSERT(m_paqinst); //if we don't have a virtual server we're toast
  268. if (m_paqinst)
  269. {
  270. m_paqinst->DecPendingDeferred();
  271. //pass off to virtual server object for general failure handling
  272. m_paqinst->HandleAQFailure(AQ_FAILURE_PENDING_DEFERRED_DELIVERY,
  273. E_OUTOFMEMORY, pIMailMsgProperties);
  274. }
  275. goto Exit;
  276. }
  277. pli = m_liQueueHead.Flink;
  278. //Walk queue and look for entries with a later deferred delivery time.
  279. while (pli != &m_liQueueHead)
  280. {
  281. pdefqeCurrent = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(pli);
  282. _ASSERT(pdefqeCurrent);
  283. pLargeIntCurrentDeferredTime = (LARGE_INTEGER *)
  284. pdefqeCurrent->pftGetDeferredDeliveryTime();
  285. //If we have found an entry with a later time, we're done and will insert
  286. //in front of this entry
  287. if (pLargeIntCurrentDeferredTime->QuadPart > pLargeIntNewDeferredTime->QuadPart)
  288. {
  289. //back up so insert will happen between current and previous entry
  290. pli = pli->Blink;
  291. break;
  292. }
  293. //continue searching forward (same direction as dequeue)
  294. pli = pli->Flink;
  295. _ASSERT(pli);
  296. }
  297. _ASSERT(pli);
  298. pdefqeNew->InsertBefore(pli);
  299. SetCallback();
  300. Exit:
  301. m_slPrivateData.ExclusiveUnlock();
  302. }
  303. //---[ CAQDeferredDeliveryQueue::ProcessEntries ]------------------------------
  304. //
  305. //
  306. // Description:
  307. // Processes entries from the front of the queue until there are no
  308. // more entries with deferred delivery times in the past.
  309. // Parameters:
  310. // -
  311. // Returns:
  312. // -
  313. // History:
  314. // 12/28/98 - MikeSwa Created
  315. //
  316. //-----------------------------------------------------------------------------
  317. void CAQDeferredDeliveryQueue::ProcessEntries()
  318. {
  319. CAQDeferredDeliveryQueueEntry *pdefqe = NULL;
  320. LIST_ENTRY *pli = NULL;
  321. FILETIME *pftDeferredTime = NULL;
  322. DWORD dwTimeContext = 0;
  323. IMailMsgProperties *pIMailMsgProperties = NULL;
  324. HRESULT hr = S_OK;
  325. DWORD cEntriesProcessed = 0;
  326. m_slPrivateData.ExclusiveLock();
  327. pli = m_liQueueHead.Flink;
  328. //if we do not have a virtual server pointer... then we have nothing
  329. //to do with the processed messages
  330. if (!m_paqinst)
  331. goto Exit;
  332. //Walk queue and delete remaining entries
  333. while (pli != &m_liQueueHead)
  334. {
  335. pdefqe = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(pli);
  336. _ASSERT(pdefqe);
  337. pftDeferredTime = pdefqe->pftGetDeferredDeliveryTime();
  338. //Check if the deferred delivery time is in the past... if not, we are done
  339. if (!m_paqinst->fInPast(pftDeferredTime, &dwTimeContext))
  340. {
  341. if (!cEntriesProcessed)
  342. {
  343. //we have processed no entries... and wasted a callback
  344. //force another callback so messages don't get stranded
  345. pdefqe->ResetCallbackFlag();
  346. }
  347. break;
  348. }
  349. cEntriesProcessed++;
  350. pIMailMsgProperties = pdefqe->pmsgGetMsg();
  351. delete pdefqe; //we remove from list
  352. //Release lock, so we do not hold it for external calls to submit
  353. //the message
  354. m_slPrivateData.ExclusiveUnlock();
  355. m_paqinst->DecPendingDeferred();
  356. //This is the external verions of AQ's submit API which should
  357. //always succeed... (unless shutdown is happening).
  358. hr = m_paqinst->HrInternalSubmitMessage(pIMailMsgProperties);
  359. if (FAILED(hr))
  360. m_paqinst->HandleAQFailure(AQ_FAILURE_PROCESSING_DEFERRED_DELIVERY, hr,
  361. pIMailMsgProperties);
  362. pIMailMsgProperties->Release();
  363. pIMailMsgProperties = NULL;
  364. //Since we gave up the lock, we need to start from the front of
  365. //the queue
  366. m_slPrivateData.ExclusiveLock();
  367. pli = m_liQueueHead.Flink;
  368. }
  369. //see if there are any other entries an set a new callback time
  370. SetCallback();
  371. Exit:
  372. m_slPrivateData.ExclusiveUnlock();
  373. }
  374. //---[ CAQDeferredDeliveryQueue::TimerCallback ]-------------------------------
  375. //
  376. //
  377. // Description:
  378. // Callback function that is triggered by the retry-callback code.
  379. // Parameters:
  380. // IN pvContext A this ptr for the CAQDeferredDeliveryQueue
  381. // object.
  382. // Returns:
  383. // -
  384. // History:
  385. // 12/28/98 - MikeSwa Created
  386. //
  387. //-----------------------------------------------------------------------------
  388. void CAQDeferredDeliveryQueue::TimerCallback(PVOID pvContext)
  389. {
  390. CAQDeferredDeliveryQueue *pdefq = (CAQDeferredDeliveryQueue *) pvContext;
  391. _ASSERT(DEFERRED_DELIVERY_QUEUE_SIG == pdefq->m_dwSignature);
  392. InterlockedDecrement((PLONG) &pdefq->m_cCallbacksPending);
  393. pdefq->ProcessEntries();
  394. }
  395. //---[ CAQDeferredDeliveryQueue::SetCallback ]---------------------------------
  396. //
  397. //
  398. // Description:
  399. // Sets the retry callback if the queue is non-empty... Exclusive lock
  400. // on queue should be held at this point.
  401. // Parameters:
  402. // -
  403. // Returns:
  404. // -
  405. // History:
  406. // 1/13/99 - MikeSwa Created
  407. //
  408. //-----------------------------------------------------------------------------
  409. void CAQDeferredDeliveryQueue::SetCallback()
  410. {
  411. CAQDeferredDeliveryQueueEntry *pdefqe = NULL;
  412. if (!IsListEmpty(&m_liQueueHead))
  413. {
  414. //Set the callback time.
  415. pdefqe = CAQDeferredDeliveryQueueEntry::pdefqeGetEntry(m_liQueueHead.Flink);
  416. _ASSERT(pdefqe);
  417. if (pdefqe->fSetCallback(this, m_paqinst))
  418. InterlockedIncrement((PLONG) &m_cCallbacksPending);
  419. }
  420. }