Source code of Windows XP (NT5)
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.

607 lines
16 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995
  5. //
  6. // File: thrdcomm.cxx
  7. //
  8. // Contents: Implementation of the CThreadComm class
  9. //
  10. //----------------------------------------------------------------------------
  11. #include "headers.hxx"
  12. DeclareTagOther(tagDontKillThread, "MTScript", "Dont Terminate Threads on Shutdown");
  13. //+---------------------------------------------------------------------------
  14. //
  15. // CThreadComm class
  16. //
  17. // Handles communication between threads.
  18. //
  19. //----------------------------------------------------------------------------
  20. CThreadComm::CThreadComm()
  21. {
  22. Assert(_hCommEvent == NULL);
  23. Assert(_hThreadReady == NULL);
  24. Assert(_hThread == NULL);
  25. Assert(_pMsgData == NULL);
  26. }
  27. CThreadComm::~CThreadComm()
  28. {
  29. MESSAGEDATA *pMsg;
  30. if (_hThread)
  31. CloseHandle(_hThread);
  32. if (_hCommEvent)
  33. CloseHandle(_hCommEvent);
  34. if (_hThreadReady)
  35. CloseHandle(_hThreadReady);
  36. if (_hResultEvt)
  37. CloseHandle(_hResultEvt);
  38. while (_pMsgData)
  39. {
  40. pMsg = _pMsgData->pNext;
  41. delete _pMsgData;
  42. _pMsgData = pMsg;
  43. }
  44. }
  45. //+---------------------------------------------------------------------------
  46. //
  47. // Member: CThreadComm::Init, public
  48. //
  49. // Synopsis: Initializes the class. Must be called on all instances before
  50. // using.
  51. //
  52. //----------------------------------------------------------------------------
  53. BOOL
  54. CThreadComm::Init()
  55. {
  56. _hCommEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  57. if (!_hCommEvent)
  58. goto Error;
  59. _hThreadReady = CreateEvent(NULL, TRUE, FALSE, NULL);
  60. if (!_hThreadReady)
  61. goto Error;
  62. _hResultEvt = CreateEvent(NULL, TRUE, FALSE, NULL);
  63. if (!_hResultEvt)
  64. goto Error;
  65. return TRUE;
  66. Error:
  67. ErrorPopup(L"CThreadComm::Init");
  68. return FALSE;
  69. }
  70. //+---------------------------------------------------------------------------
  71. //
  72. // Member: CThreadComm::SendHelper, public
  73. //
  74. // Synopsis: Send or post the given message to the thread which owns
  75. // this class.
  76. //
  77. // Arguments: [md] -- Message to send
  78. // [pvData] -- Associated data with message.
  79. // [cbData] -- Size of info that [pvData] points to.
  80. // [fSend] -- TRUE if we're doing a send, FALSE if it's a post
  81. // [hResultEvt] -- Event handle to signal when the result is ready
  82. //
  83. //----------------------------------------------------------------------------
  84. DWORD
  85. CThreadComm::SendHelper(THREADMSG mt,
  86. void * pvData,
  87. DWORD cbData,
  88. BOOL fSend,
  89. HANDLE hResultEvt)
  90. {
  91. DWORD dwRet = 0;
  92. MESSAGEDATA *pMsg = NULL, *pMsgLoop;
  93. AssertSz(!pvData || cbData != 0, "Invalid params to PostToThread");
  94. pMsg = new MESSAGEDATA;
  95. if (!pMsg)
  96. goto Error;
  97. pMsg->pNext = NULL;
  98. pMsg->tmMessage = mt;
  99. pMsg->dwResult = 0;
  100. pMsg->hResultEvt = hResultEvt;
  101. if (pvData)
  102. {
  103. AssertSz(cbData <= MSGDATABUFSIZE, "Data is too big!");
  104. pMsg->cbData = cbData;
  105. memcpy(pMsg->bData, pvData, cbData);
  106. }
  107. else
  108. {
  109. pMsg->cbData = 0;
  110. }
  111. {
  112. LOCK_LOCALS(this);
  113. if (!fSend)
  114. {
  115. //
  116. // Stick the new message at the end so we get messages FIFO
  117. //
  118. pMsgLoop = _pMsgData;
  119. while (pMsgLoop && pMsgLoop->pNext)
  120. {
  121. pMsgLoop = pMsgLoop->pNext;
  122. }
  123. if (!pMsgLoop)
  124. {
  125. _pMsgData = pMsg;
  126. }
  127. else
  128. {
  129. pMsgLoop->pNext = pMsg;
  130. }
  131. }
  132. else
  133. {
  134. // Set dwResult to indicate we're expecting a result
  135. pMsg->dwResult = 1;
  136. //
  137. // Put sent messages at the front to minimize potential deadlocks
  138. //
  139. pMsg->pNext = _pMsgData;
  140. _pMsgData = pMsg;
  141. Assert(hResultEvt);
  142. ResetEvent(hResultEvt);
  143. }
  144. }
  145. SetEvent(_hCommEvent);
  146. if (fSend)
  147. {
  148. HANDLE ahEvents[2];
  149. ahEvents[0] = hResultEvt;
  150. ahEvents[1] = _hThread ;
  151. DWORD dwWait = WaitForMultipleObjects(2, ahEvents, FALSE, 50000);
  152. switch(dwWait)
  153. {
  154. case WAIT_OBJECT_0: // OK, this is good
  155. break;
  156. default:
  157. case WAIT_OBJECT_0 + 1:
  158. case WAIT_TIMEOUT:
  159. AssertSz(FALSE, "SendToThread never responded!");
  160. //
  161. // This causes a memory leak, but it's better than a crash. What
  162. // we really need to do is remove the message from the queue.
  163. //
  164. return 0;
  165. }
  166. dwRet = pMsg->dwResult;
  167. delete pMsg;
  168. }
  169. return dwRet;
  170. Error:
  171. ErrorPopup(L"CThreadComm::PostToThread - out of memory");
  172. return dwRet;
  173. }
  174. //+---------------------------------------------------------------------------
  175. //
  176. // Member: CThreadComm::PostToThread, public
  177. //
  178. // Synopsis: Post the given message to the thread which owns pTarget.
  179. //
  180. // Arguments: [md] -- Message to send
  181. // [pvData] -- Associated data with message.
  182. // [cbData] -- Size of info that [pvData] points to.
  183. //
  184. //----------------------------------------------------------------------------
  185. void
  186. CThreadComm::PostToThread(CThreadComm *pTarget,
  187. THREADMSG mt,
  188. void * pvData,
  189. DWORD cbData)
  190. {
  191. (void)pTarget->SendHelper(mt, pvData, cbData, FALSE, NULL);
  192. }
  193. //+---------------------------------------------------------------------------
  194. //
  195. // Member: CThreadComm::SendToThread, public
  196. //
  197. // Synopsis: Send the given message to the thread which owns this class.
  198. //
  199. // Arguments: [md] -- Message to send
  200. // [pvData] -- Associated data with message.
  201. // [cbData] -- Size of info that [pvData] points to.
  202. //
  203. //----------------------------------------------------------------------------
  204. DWORD
  205. CThreadComm::SendToThread(CThreadComm *pTarget,
  206. THREADMSG mt,
  207. void * pvData,
  208. DWORD cbData)
  209. {
  210. DWORD dwRet;
  211. VERIFY_THREAD();
  212. Assert(sizeof(_fInSend) == 4); // We are relying on atomic read/writes
  213. // because multiple threads are accessing
  214. // this variable.
  215. Assert(!_fInSend);
  216. _fInSend = TRUE;
  217. if (pTarget->_fInSend)
  218. {
  219. //
  220. // Somebody is trying to send to us while we're sending to someone else.
  221. // This is potentially a deadlock situation! First, wait and see if it
  222. // resolves, then if it doesn't, assert and bail out!
  223. //
  224. TraceTag((tagError, "Perf Hit! Avoiding deadlock situation!"));
  225. Sleep(100); // Arbitrary 100 ms
  226. if (pTarget->_fInSend)
  227. {
  228. AssertSz(FALSE, "Deadlock - SendToThread called on object doing a send!");
  229. _fInSend = FALSE;
  230. return 0;
  231. }
  232. }
  233. dwRet = pTarget->SendHelper(mt, pvData, cbData, TRUE, _hResultEvt);
  234. _fInSend = FALSE;
  235. return dwRet;
  236. }
  237. //+---------------------------------------------------------------------------
  238. //
  239. // Member: CThreadComm::GetNextMsg, public
  240. //
  241. // Synopsis: Retrieves the next message waiting for this thread.
  242. //
  243. // Arguments: [md] -- Place to put message type.
  244. // [pData] -- Associated data for message. Must be allocated
  245. // memory of size MSGDATABUFSIZE
  246. // [pcbData] -- Size of info in *pData is returned here.
  247. //
  248. // Returns: TRUE if a valid message was returned. FALSE if there are no
  249. // more messages.
  250. //
  251. //----------------------------------------------------------------------------
  252. BOOL
  253. CThreadComm::GetNextMsg(THREADMSG *mt, void * pData, DWORD *pcbData)
  254. {
  255. MESSAGEDATA *pMsg;
  256. BOOL fRet = TRUE;
  257. VERIFY_THREAD();
  258. LOCK_LOCALS(this);
  259. AssertSz(!_pMsgReply, "Sent message not replied to!");
  260. pMsg = _pMsgData;
  261. if (pMsg)
  262. {
  263. _pMsgData = pMsg->pNext;
  264. *mt = pMsg->tmMessage;
  265. *pcbData = pMsg->cbData;
  266. memcpy(pData, pMsg->bData, pMsg->cbData);
  267. //
  268. // If the caller is not expecting a reply, delete the message. If he is,
  269. // then the caller will free the memory.
  270. //
  271. if (pMsg->dwResult == 0)
  272. {
  273. delete pMsg;
  274. }
  275. else
  276. {
  277. _pMsgReply = pMsg;
  278. }
  279. }
  280. else if (!_pMsgData)
  281. {
  282. ResetEvent(_hCommEvent);
  283. fRet = FALSE;
  284. }
  285. return fRet;
  286. }
  287. //+---------------------------------------------------------------------------
  288. //
  289. // Member: CThreadComm::Reply, public
  290. //
  291. // Synopsis: Call this to send the result of a SendToThread call back to
  292. // the calling thread.
  293. //
  294. // Arguments: [dwReply] -- Result to send back.
  295. //
  296. //----------------------------------------------------------------------------
  297. void
  298. CThreadComm::Reply(DWORD dwReply)
  299. {
  300. VERIFY_THREAD();
  301. Assert(_pMsgReply);
  302. _pMsgReply->dwResult = dwReply;
  303. SetEvent(_pMsgReply->hResultEvt);
  304. _pMsgReply = NULL;
  305. }
  306. //+---------------------------------------------------------------------------
  307. //
  308. // Member: MessageEventPump, public
  309. //
  310. // Synopsis: Empties our message queues (both windows' and our private
  311. // threadcomm queue)
  312. //
  313. // Arguments:
  314. // [hEvent] -- event handle to wait for
  315. //
  316. // Returns:
  317. // WAIT_ABANDONED: An event occurred which is causing this thread to
  318. // terminate. The caller should clean up and finish
  319. // what it's doing.
  320. // WAIT_OBJECT_0: If one (or all if fAll==TRUE) of the passed-in
  321. // event handles became signaled. The index of the
  322. // signaled handle is added to MEP_EVENT_0. Returned
  323. // only if one or more event handles were passed in.
  324. //
  325. //----------------------------------------------------------------------------
  326. DWORD
  327. MessageEventPump(HANDLE hEvent)
  328. {
  329. MSG msg;
  330. DWORD dwRet;
  331. DWORD mepReturn = 0;
  332. do
  333. {
  334. //
  335. // Purge out all window messages (primarily for OLE's benefit).
  336. //
  337. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  338. {
  339. if (msg.message == WM_QUIT)
  340. {
  341. return WAIT_ABANDONED;
  342. }
  343. TranslateMessage(&msg);
  344. DispatchMessage(&msg);
  345. }
  346. dwRet = MsgWaitForMultipleObjects(1,
  347. &hEvent,
  348. FALSE,
  349. INFINITE,
  350. QS_ALLINPUT);
  351. if (dwRet == WAIT_OBJECT_0)
  352. {
  353. mepReturn = dwRet;
  354. break;
  355. }
  356. else if (dwRet == WAIT_OBJECT_0 + 1)
  357. {
  358. //
  359. // A windows message came through. It will be handled at the
  360. // top of the loop.
  361. //
  362. }
  363. else if (dwRet == WAIT_FAILED || dwRet == WAIT_ABANDONED)
  364. {
  365. TraceTag((tagError, "WaitForMultipleObjects failure (%d)", GetLastError()));
  366. AssertSz(FALSE, "WaitForMultipleObjects failure");
  367. mepReturn = dwRet;
  368. break;
  369. }
  370. }
  371. while (true);
  372. return mepReturn;
  373. }
  374. //+---------------------------------------------------------------------------
  375. //
  376. // Member: CThreadComm::StartThread, public
  377. //
  378. // Synopsis: Starts a new thread that owns this CThreadComm instance.
  379. //
  380. //----------------------------------------------------------------------------
  381. HRESULT
  382. CThreadComm::StartThread(void * pvParams)
  383. {
  384. if (!Init())
  385. {
  386. AssertSz(FALSE, "Failed to initialize new class.");
  387. return E_FAIL; // TODO: need to change Init() to return HRESULT
  388. }
  389. ResetEvent(_hThreadReady);
  390. //
  391. // Create suspended because we need to set member variables before it
  392. // gets going.
  393. //
  394. _hThread = CreateThread(NULL,
  395. 0,
  396. (LPTHREAD_START_ROUTINE)CThreadComm::TempThreadRoutine,
  397. (LPVOID)this,
  398. CREATE_SUSPENDED,
  399. &_dwThreadId);
  400. if (_hThread == NULL)
  401. {
  402. long e = GetLastError();
  403. return HRESULT_FROM_WIN32(e);
  404. }
  405. _pvParams = pvParams;
  406. _hrThread = S_OK;
  407. //
  408. // Everything's set up - get it going!
  409. //
  410. ResumeThread(_hThread);
  411. //
  412. // Wait for the new thread to say it's ready...
  413. //
  414. MessageEventPump(_hThreadReady); // This is neccessary to allow the new thread to retrieve the script parameter with GetInterfaceFromGlobal().
  415. // On failure, wait for the thread to exit before returning.
  416. if (FAILED(_hrThread))
  417. {
  418. WaitForSingleObject(_hThread, INFINITE);
  419. return _hrThread;
  420. }
  421. return S_OK;
  422. }
  423. //+---------------------------------------------------------------------------
  424. //
  425. // Member: CThreadComm::TempThreadRoutine, public
  426. //
  427. // Synopsis: Static member given to CreateThread. Just a stub that calls
  428. // the real thread routine.
  429. //
  430. //----------------------------------------------------------------------------
  431. DWORD
  432. CThreadComm::TempThreadRoutine(LPVOID pvParam)
  433. {
  434. CThreadComm *pThis = (CThreadComm*)pvParam;
  435. AssertSz(pThis, "Bad arg passed to CThreadComm::TempThreadRoutine");
  436. return pThis->ThreadMain();
  437. }
  438. //+---------------------------------------------------------------------------
  439. //
  440. // Member: CThreadComm::SetName, public
  441. //
  442. // Synopsis: On a debug build, sets the thread name so the debugger
  443. // lists the threads in an understandable manner.
  444. //
  445. // Arguments: [pszName] -- Name to set thread to.
  446. //
  447. //----------------------------------------------------------------------------
  448. void
  449. CThreadComm::SetName(LPCSTR pszName)
  450. {
  451. #if DBG == 1
  452. THREADNAME_INFO info =
  453. {
  454. 0x1000,
  455. pszName,
  456. _dwThreadId
  457. };
  458. __try
  459. {
  460. RaiseException(0x406d1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info);
  461. }
  462. __except(EXCEPTION_CONTINUE_EXECUTION)
  463. {
  464. }
  465. #endif
  466. }
  467. //+---------------------------------------------------------------------------
  468. //
  469. // Member: CThreadComm::Shutdown, public
  470. //
  471. // Synopsis: Forces the thread containing the given instance of the
  472. // ThreadComm object to shutdown.
  473. //
  474. // Arguments: [pTarget] -- Object to shutdown
  475. //
  476. // Returns: TRUE if it shutdown normally, FALSE if it had to be killed.
  477. //
  478. //----------------------------------------------------------------------------
  479. BOOL
  480. CThreadComm::Shutdown(CThreadComm *pTarget)
  481. {
  482. BOOL fReturn = TRUE;
  483. DWORD dwTimeout = (IsTagEnabled(tagDontKillThread)) ? INFINITE : 5000;
  484. DWORD dwCode;
  485. GetExitCodeThread(pTarget->hThread(), &dwCode);
  486. // Is client already dead?
  487. if (dwCode != STILL_ACTIVE)
  488. return TRUE;
  489. //
  490. // Sending the PLEASEEXIT message will flush all other messages out,
  491. // causing any unsent data to be sent before closing the pipe.
  492. //
  493. PostToThread(pTarget, MD_PLEASEEXIT);
  494. // Wait 5 seconds for our thread to terminate and then kill it.
  495. if (WaitForSingleObject(pTarget->hThread(), dwTimeout) == WAIT_TIMEOUT)
  496. {
  497. TraceTag((tagError, "Terminating thread for object %x...", this));
  498. AssertSz(FALSE, "I'm being forced to terminate a thread!");
  499. TerminateThread(pTarget->hThread(), 1);
  500. fReturn = FALSE;
  501. }
  502. return fReturn;
  503. }