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.

431 lines
12 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: failmsgq.cpp
  5. //
  6. // Description:
  7. // Implementation of CFailedMsgQueue class.
  8. //
  9. // Author: Mike Swafford (MikeSwa)
  10. //
  11. // History:
  12. // 1/18/99 - MikeSwa Created
  13. //
  14. // Copyright (C) 1999 Microsoft Corporation
  15. //
  16. //-----------------------------------------------------------------------------
  17. #include "aqprecmp.h"
  18. #include "failmsgq.h"
  19. #include "aqutil.h"
  20. #include <mailmsgi_i.c>
  21. //---[ IMailMsgAQueueListEntry ]----------------------------------------------
  22. //
  23. //
  24. // Description:
  25. // Helper function that gets list entry for message.
  26. // Parameters:
  27. // IN pIMailMsgPropertes Msg to get list entry for
  28. // Returns:
  29. // Pointer to list entry
  30. // History:
  31. // 1/19/99 - MikeSwa Created
  32. //
  33. //-----------------------------------------------------------------------------
  34. AQueueFailedListEntry *pfliGetListEntryForMsg(
  35. IMailMsgProperties *pIMailMsgProperties)
  36. {
  37. HRESULT hr = S_OK;
  38. AQueueFailedListEntry *pfli = NULL;
  39. IMailMsgAQueueListEntry *pIMailMsgAQueueListEntry = NULL;
  40. _ASSERT(pIMailMsgProperties);
  41. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgAQueueListEntry,
  42. (void **) &pIMailMsgAQueueListEntry);
  43. //This are spec'd to never fail
  44. _ASSERT(SUCCEEDED(hr));
  45. _ASSERT(pIMailMsgAQueueListEntry);
  46. if (pIMailMsgAQueueListEntry)
  47. {
  48. hr = pIMailMsgAQueueListEntry->GetListEntry((void **) &pfli);
  49. _ASSERT(SUCCEEDED(hr));
  50. _ASSERT(pfli);
  51. pIMailMsgAQueueListEntry->Release();
  52. pfli->m_pIMailMsgProperties = pIMailMsgProperties;
  53. pIMailMsgProperties->AddRef();
  54. }
  55. return pfli;
  56. }
  57. //---[ ValidateListEntry ]-----------------------------------------------------
  58. //
  59. //
  60. // Description:
  61. // Debug code to do some validation on the list entry pulled off the list
  62. // Parameters:
  63. // IN pfli list entry struct pulled off of
  64. // Returns:
  65. //
  66. // History:
  67. // 1/19/99 - MikeSwa Created
  68. //
  69. //-----------------------------------------------------------------------------
  70. #ifndef DEBUG
  71. #define AQValidateListEntry(x)
  72. #else //is DEBUG
  73. void AQValidateListEntry(AQueueFailedListEntry *pfli)
  74. {
  75. HRESULT hr = S_OK;
  76. AQueueFailedListEntry *pfliNew = NULL;
  77. IMailMsgAQueueListEntry *pIMailMsgAQueueListEntry = NULL;
  78. _ASSERT(pfli);
  79. _ASSERT(pfli->m_pIMailMsgProperties);
  80. hr = pfli->m_pIMailMsgProperties->QueryInterface(IID_IMailMsgAQueueListEntry,
  81. (void **) &pIMailMsgAQueueListEntry);
  82. //This are spec'd to never fail
  83. _ASSERT(SUCCEEDED(hr));
  84. _ASSERT(pIMailMsgAQueueListEntry);
  85. hr = pIMailMsgAQueueListEntry->GetListEntry((void **) &pfliNew);
  86. _ASSERT(SUCCEEDED(hr));
  87. _ASSERT(pfliNew);
  88. //The list entry returned should be the same one pass into this function
  89. _ASSERT(pfli == pfliNew);
  90. pIMailMsgAQueueListEntry->Release();
  91. }
  92. #endif //DEBUG
  93. //---[ CFailedMsgQueue::CFailedMsgQueue ]--------------------------------------
  94. //
  95. //
  96. // Description:
  97. // Constuctor for CFailedMsgQueue
  98. // Parameters:
  99. // -
  100. // Returns:
  101. // -
  102. // History:
  103. // 1/18/99 - MikeSwa Created
  104. //
  105. //-----------------------------------------------------------------------------
  106. CFailedMsgQueue::CFailedMsgQueue()
  107. {
  108. m_dwSignature = FAILEDMSGQUEUE_SIG;
  109. m_cMsgs = 0;
  110. m_paqinst = NULL;
  111. m_dwFlags = 0;
  112. InitializeListHead(&m_liHead);
  113. }
  114. //---[ CFailedMsgQueue::~CFailedMsgQueue ]-------------------------------------
  115. //
  116. //
  117. // Description:
  118. // Default destructor for CFailedMsgQueue
  119. // Parameters:
  120. // -
  121. // Returns:
  122. // -
  123. // History:
  124. // 1/18/99 - MikeSwa Created
  125. //
  126. //-----------------------------------------------------------------------------
  127. CFailedMsgQueue::~CFailedMsgQueue()
  128. {
  129. Deinitialize();
  130. }
  131. //---[ CFailedMsgQueue::Initialize ]-------------------------------------------
  132. //
  133. //
  134. // Description:
  135. // Initialization routine for CFailedMsgQueue
  136. // Parameters:
  137. // IN paqinst Ptr to the server instance object
  138. // Returns:
  139. // -
  140. // History:
  141. // 1/18/99 - MikeSwa Created
  142. //
  143. //-----------------------------------------------------------------------------
  144. void CFailedMsgQueue::Initialize(CAQSvrInst *paqinst)
  145. {
  146. _ASSERT(paqinst);
  147. m_paqinst = paqinst;
  148. if (m_paqinst)
  149. m_paqinst->AddRef();
  150. }
  151. //---[ CFailedMsgQueue::Deinitialize ]-----------------------------------------
  152. //
  153. //
  154. // Description:
  155. // Deinitialization code for CFailedMsgQueue. Release server instance
  156. // object.
  157. // Parameters:
  158. // -
  159. // Returns:
  160. // -
  161. // History:
  162. // 1/18/99 - MikeSwa Created
  163. //
  164. //-----------------------------------------------------------------------------
  165. void CFailedMsgQueue::Deinitialize()
  166. {
  167. CAQSvrInst *paqinst = NULL;
  168. AQueueFailedListEntry *pfli = NULL;
  169. m_slPrivateData.ExclusiveLock();
  170. paqinst = m_paqinst;
  171. m_paqinst = NULL;
  172. //Loop through list & release messages
  173. while (!IsListEmpty(&m_liHead))
  174. {
  175. pfli = (AQueueFailedListEntry *) m_liHead.Flink;
  176. _ASSERT(&m_liHead != ((PLIST_ENTRY) pfli));
  177. _ASSERT(pfli->m_pIMailMsgProperties);
  178. if (paqinst)
  179. paqinst->ServerStopHintFunction();
  180. RemoveEntryList((PLIST_ENTRY)pfli);
  181. pfli->m_pIMailMsgProperties->Release();
  182. }
  183. m_slPrivateData.ExclusiveUnlock();
  184. if (paqinst)
  185. paqinst->Release();
  186. }
  187. //---[ CFailedMsgQueue::HandleFailedMailMsg ]----------------------------------
  188. //
  189. //
  190. // Description:
  191. // Puts a failed mailmsg in the queue of mailmsgs to retry
  192. // Parameters:
  193. // IN pIMailMsgProperties MailMsgProperties to try
  194. // Returns:
  195. // -
  196. // History:
  197. // 1/18/99 - MikeSwa Created
  198. //
  199. //-----------------------------------------------------------------------------
  200. void CFailedMsgQueue::HandleFailedMailMsg(IMailMsgProperties *pIMailMsgProperties)
  201. {
  202. AQueueFailedListEntry *pfli = NULL;
  203. if (!pIMailMsgProperties)
  204. {
  205. m_slPrivateData.ShareLock();
  206. if (m_paqinst)
  207. m_paqinst->DecPendingFailed();
  208. m_slPrivateData.ShareUnlock();
  209. return;
  210. }
  211. pfli = pfliGetListEntryForMsg(pIMailMsgProperties);
  212. //If above fails... there is nothing we can do
  213. _ASSERT(pfli);
  214. if (!pfli)
  215. return;
  216. m_slPrivateData.ExclusiveLock();
  217. if (!m_paqinst)
  218. {
  219. _ASSERT(pfli->m_pIMailMsgProperties);
  220. pfli->m_pIMailMsgProperties->Release();
  221. pfli->m_pIMailMsgProperties = NULL;
  222. }
  223. else
  224. {
  225. InsertTailList(&m_liHead, &(pfli->m_li));
  226. InterlockedIncrement((PLONG) &m_cMsgs);
  227. }
  228. m_slPrivateData.ExclusiveUnlock();
  229. //Make sure we have a retry pending
  230. StartProcessingIfNecessary();
  231. }
  232. //---[ CFailedMsgQueue::InternalStartProcessingIfNecessary ]-------------------
  233. //
  234. //
  235. // Description:
  236. // Called at various times (ie SubmitMessage) to kick off the processing
  237. // of Failed Msgs.
  238. // Parameters:
  239. // -
  240. // Returns:
  241. // -
  242. // History:
  243. // 1/18/99 - MikeSwa Created
  244. //
  245. //-----------------------------------------------------------------------------
  246. void CFailedMsgQueue::InternalStartProcessingIfNecessary()
  247. {
  248. CAQSvrInst *paqinst = NULL;
  249. HRESULT hr = S_OK;
  250. BOOL fCallbackRequestFailed = FALSE;
  251. //See if there is work to be done and no one else doing it or scheduled to
  252. if (!(FMQ_CALLBACK_REQUESTED & m_dwFlags) && m_cMsgs)
  253. {
  254. //Try to set the call back bit.... if this thread gets it... arrange for
  255. //a callback.
  256. if (!(FMQ_CALLBACK_REQUESTED &
  257. dwInterlockedSetBits(&m_dwFlags, FMQ_CALLBACK_REQUESTED)))
  258. {
  259. //Get Virtual server object in a thread safe manner
  260. m_slPrivateData.ShareLock();
  261. paqinst = m_paqinst;
  262. if (paqinst)
  263. paqinst->AddRef();
  264. m_slPrivateData.ShareUnlock();
  265. //Only worry about trying if we have a virtual server object.
  266. if (paqinst)
  267. {
  268. //Retry in 5 minutes
  269. hr = paqinst->SetCallbackTime(
  270. CFailedMsgQueue::ProcessEntriesCallback,
  271. this, 5);
  272. if (FAILED(hr))
  273. fCallbackRequestFailed = TRUE;
  274. }
  275. else
  276. {
  277. fCallbackRequestFailed = TRUE;
  278. }
  279. }
  280. }
  281. //We failed to request a callback... unset the flag, so another thread
  282. //can try
  283. if (fCallbackRequestFailed)
  284. dwInterlockedUnsetBits(&m_dwFlags, FMQ_CALLBACK_REQUESTED);
  285. if (paqinst)
  286. paqinst->Release();
  287. }
  288. //---[ CFailedMsgQueue::ProcessEntries ]---------------------------------------
  289. //
  290. //
  291. // Description:
  292. // Walks queues of failed IMailMsgs and proccesses them for retry
  293. // Parameters:
  294. // -
  295. // Returns:
  296. // -
  297. // History:
  298. // 1/18/99 - MikeSwa Created
  299. //
  300. //-----------------------------------------------------------------------------
  301. void CFailedMsgQueue::ProcessEntries()
  302. {
  303. DWORD cMsgsToProcess = m_cMsgs; //Only walk list once.
  304. HRESULT hr = S_OK;
  305. AQueueFailedListEntry *pfli = NULL;
  306. CAQSvrInst *paqinst = NULL;
  307. //There should only be 1 thread processing entries, and we should have
  308. //set the bit
  309. _ASSERT(FMQ_CALLBACK_REQUESTED & m_dwFlags);
  310. m_slPrivateData.ExclusiveLock();
  311. paqinst = m_paqinst;
  312. if (paqinst)
  313. {
  314. paqinst->AddRef();
  315. while (!IsListEmpty(&m_liHead) && cMsgsToProcess-- && m_paqinst)
  316. {
  317. pfli = (AQueueFailedListEntry *) m_liHead.Flink;
  318. _ASSERT(&m_liHead != ((PLIST_ENTRY) pfli));
  319. RemoveEntryList((PLIST_ENTRY)pfli);
  320. m_slPrivateData.ExclusiveUnlock();
  321. //Verify that pli we have now is the same as what the interface
  322. //returns
  323. AQValidateListEntry(pfli);
  324. paqinst->DecPendingFailed();
  325. InterlockedDecrement((PLONG) &m_cMsgs);
  326. hr = paqinst->HrInternalSubmitMessage(pfli->m_pIMailMsgProperties);
  327. if (FAILED(hr) && (AQUEUE_E_SHUTDOWN != hr) &&
  328. paqinst->fShouldRetryMessage(pfli->m_pIMailMsgProperties))
  329. {
  330. HandleFailedMailMsg(pfli->m_pIMailMsgProperties);
  331. }
  332. pfli->m_pIMailMsgProperties->Release();
  333. //Should be lock when we hit top of loop
  334. m_slPrivateData.ExclusiveLock();
  335. }
  336. paqinst->Release();
  337. paqinst = NULL;
  338. }
  339. m_slPrivateData.ExclusiveUnlock();
  340. }
  341. //---[ CFailedMsgQueue::ProcessEntries ]---------------------------------------
  342. //
  343. //
  344. // Description:
  345. // Static function that is used as a retry callback for ProcessEntries.
  346. // Parameters:
  347. // IN pvContext This ptr of CFailedMsgQueue object
  348. // Returns:
  349. // -
  350. // History:
  351. // 1/18/99 - MikeSwa Created
  352. //
  353. //-----------------------------------------------------------------------------
  354. void CFailedMsgQueue::ProcessEntriesCallback(PVOID pvContext)
  355. {
  356. CFailedMsgQueue *pfmq = (CFailedMsgQueue *) pvContext;
  357. _ASSERT(pfmq);
  358. _ASSERT(FAILEDMSGQUEUE_SIG == pfmq->m_dwSignature);
  359. if (pfmq && (FAILEDMSGQUEUE_SIG == pfmq->m_dwSignature))
  360. {
  361. pfmq->ProcessEntries();
  362. _ASSERT(FMQ_CALLBACK_REQUESTED & (pfmq->m_dwFlags));
  363. dwInterlockedUnsetBits(&(pfmq->m_dwFlags), FMQ_CALLBACK_REQUESTED);
  364. pfmq->StartProcessingIfNecessary();
  365. }
  366. }