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.

358 lines
8.8 KiB

  1. // worker.cpp
  2. //
  3. // Implementation of the worker thread object
  4. //
  5. #include "priv.h"
  6. // Do not build this file if on Win9X or NT4
  7. #ifndef DOWNLEVEL_PLATFORM
  8. #include "resource.h"
  9. #include "worker.h"
  10. //--------------------------------------------------------------------
  11. //
  12. //
  13. // CWorkerThread class
  14. //
  15. //
  16. //--------------------------------------------------------------------
  17. // CWorkerThread constructor
  18. CWorkerThread::CWorkerThread() : _cRef(1)
  19. {
  20. ASSERT(NULL == _hthreadWorker);
  21. ASSERT(NULL == _hwndWorker);
  22. ASSERT(FALSE == _fKillWorker);
  23. ASSERT(NULL == _pwe);
  24. ASSERT(0 == _cRefLockWorker);
  25. InitializeCriticalSection(&_csWorker);
  26. DllAddRef();
  27. }
  28. // CWorkerThread destructor
  29. CWorkerThread::~CWorkerThread()
  30. {
  31. ASSERT(0 == _cRefLockWorker);
  32. if (IsWindow(_hwndWorker))
  33. {
  34. DestroyWindow(_hwndWorker);
  35. }
  36. SetListenerWT(NULL);
  37. DeleteCriticalSection(&_csWorker);
  38. DllRelease();
  39. }
  40. BOOL CWorkerThread::PostWorkerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
  41. {
  42. BOOL bRet = FALSE;
  43. _LockWorker();
  44. // Only if we are not being killed, and the worker window is valid we
  45. // post the message, we don't want to post to NULL window (desktop)
  46. if (!_fKillWorker && _hwndWorker)
  47. bRet = PostMessage(_hwndWorker, uMsg, wParam, lParam);
  48. _UnlockWorker();
  49. return bRet;
  50. }
  51. /*--------------------------------------------------------------------
  52. Purpose: IUnknown::QueryInterface
  53. */
  54. STDMETHODIMP CWorkerThread::QueryInterface(REFIID riid, LPVOID * ppvObj)
  55. {
  56. static const QITAB qit[] = {
  57. QITABENT(CWorkerThread, IARPWorker),
  58. { 0 },
  59. };
  60. return QISearch(this, (LPCQITAB)qit, riid, ppvObj);
  61. }
  62. STDMETHODIMP_(ULONG) CWorkerThread::AddRef()
  63. {
  64. LONG cRef = InterlockedIncrement(&_cRef);
  65. TraceAddRef(CWorkerThread, cRef);
  66. return cRef;
  67. }
  68. STDMETHODIMP_(ULONG) CWorkerThread::Release()
  69. {
  70. LONG cRef = InterlockedDecrement(&_cRef);
  71. TraceRelease(CWorkerThread, cRef);
  72. if (cRef)
  73. return cRef;
  74. delete this;
  75. return 0;
  76. }
  77. /*-------------------------------------------------------------------------
  78. Purpose: Sets the event listener so the worker thread can fire
  79. events incrementally.
  80. */
  81. HRESULT CWorkerThread::SetListenerWT(IWorkerEvent * pwe)
  82. {
  83. _LockWorker();
  84. {
  85. // We have to protect _pwe because it can be accessed by this
  86. // thread and the main thread.
  87. // Don't AddRef the event listener or we'll have a circular
  88. // reference.
  89. _pwe = pwe;
  90. }
  91. _UnlockWorker();
  92. return S_OK;
  93. }
  94. /*-------------------------------------------------------------------------
  95. Purpose: Start the thread
  96. */
  97. HRESULT CWorkerThread::StartWT(int iPriority)
  98. {
  99. DWORD thid; // Not used but we have to pass something in
  100. // Create a hidden top-level window to post messages to from the
  101. // worker thread
  102. _hwndWorker = SHCreateWorkerWindow(_WorkerWndProcWrapper, NULL, 0, 0, NULL, this);
  103. if (_hwndWorker)
  104. {
  105. AddRef(); // AddRef myself, the background thread is responsible of releasing
  106. // this ref count
  107. // Kick off the worker thread to do the slow enumeration.
  108. _hthreadWorker = CreateThread(NULL, 0,
  109. (LPTHREAD_START_ROUTINE)_ThreadStartProcWrapper,
  110. (LPVOID)this, CREATE_SUSPENDED, &thid);
  111. if (_hthreadWorker)
  112. {
  113. // Demote the priority so it doesn't interfere with the
  114. // initial HTML databinding.
  115. SetThreadPriority(_hthreadWorker, iPriority);
  116. ResumeThread(_hthreadWorker);
  117. }
  118. else
  119. {
  120. // Release my refcount in case of failure
  121. Release();
  122. // If we can't create the background thread, don't bother with the window
  123. DestroyWindow(_hwndWorker);
  124. _hwndWorker = NULL;
  125. }
  126. }
  127. return (_hthreadWorker != NULL) ? S_OK : E_FAIL;
  128. }
  129. /*-------------------------------------------------------------------------
  130. Purpose: Kills the worker thread if one is around
  131. */
  132. HRESULT CWorkerThread::KillWT(void)
  133. {
  134. MSG msg;
  135. // I should never call KillWT to kill myself
  136. ASSERT(_hthreadWorker != GetCurrentThread());
  137. TraceMsg(TF_TASKS, "[%x] Killing worker thread...", _dwThreadId);
  138. // Tell the worker thread to stop when it can
  139. // Do this inside the critical section because we don't want random messages
  140. // get posted to the worker window after this.
  141. _LockWorker();
  142. _fKillWorker = TRUE;
  143. _UnlockWorker();
  144. // If we have no worker thread, nothing to do
  145. if (_hthreadWorker)
  146. {
  147. // Now wait for the worker to stop
  148. if (WaitForSingleObject(_hthreadWorker, 10000) == WAIT_TIMEOUT)
  149. TraceMsg(TF_ERROR, "[%x] Worker thread termination wait timed out!", _dwThreadId);
  150. else
  151. TraceMsg(TF_TASKS, "[%x] Worker thread wait exited cleanly", _dwThreadId);
  152. // Now that the thread is stopped, release our hold so all its memory can go away
  153. CloseHandle(_hthreadWorker);
  154. _hthreadWorker = NULL;
  155. }
  156. // Make sure that all messages to our worker HWND get processed
  157. if (_hwndWorker)
  158. {
  159. while (PeekMessage(&msg, _hwndWorker, 0, 0, PM_REMOVE))
  160. DispatchMessage(&msg);
  161. DestroyWindow(_hwndWorker);
  162. _hwndWorker = NULL;
  163. }
  164. SetListenerWT(NULL);
  165. return S_OK;
  166. }
  167. //--------------------------------------------------------------------
  168. // Private methods
  169. void CWorkerThread::_LockWorker(void)
  170. {
  171. EnterCriticalSection(&_csWorker);
  172. DEBUG_CODE( _cRefLockWorker++; )
  173. }
  174. void CWorkerThread::_UnlockWorker(void)
  175. {
  176. DEBUG_CODE( _cRefLockWorker--; )
  177. LeaveCriticalSection(&_csWorker);
  178. }
  179. /*-------------------------------------------------------------------------
  180. Purpose: Static wndproc wrapper. Calls the real non-static WndProc.
  181. */
  182. LRESULT
  183. CALLBACK
  184. CWorkerThread::_WorkerWndProcWrapper(
  185. HWND hwnd,
  186. UINT uMsg,
  187. WPARAM wParam,
  188. LPARAM lParam
  189. )
  190. {
  191. if (uMsg == WM_DESTROY)
  192. SetWindowLongPtr(hwnd, 0, 0);
  193. else
  194. {
  195. CWorkerThread * pWorker = (CWorkerThread*)GetWindowLongPtr(hwnd, 0);
  196. if (pWorker)
  197. return pWorker->_WorkerWndProc(hwnd, uMsg, wParam, lParam);
  198. }
  199. return 0;
  200. }
  201. /*-------------------------------------------------------------------------
  202. Purpose: Used to fire events back to Trident since they can't be fired from
  203. the worker thread.
  204. */
  205. LRESULT
  206. CWorkerThread::_WorkerWndProc(
  207. HWND hwnd,
  208. UINT uMsg,
  209. WPARAM wParam,
  210. LPARAM lParam
  211. )
  212. {
  213. switch (uMsg)
  214. {
  215. case WORKERWIN_FIRE_ROW_READY:
  216. // Posted by worker thread when async data is ready
  217. _LockWorker();
  218. {
  219. if (_pwe)
  220. {
  221. TraceMsg(TF_TASKS, "[%x] Firing event for row #%d", _dwThreadId, wParam);
  222. _pwe->FireOnDataReady((LONG)wParam);
  223. }
  224. }
  225. _UnlockWorker();
  226. return 0;
  227. case WORKERWIN_FIRE_FINISHED:
  228. // Posted by worker thread when thread is finished enumerating
  229. // async data.
  230. _LockWorker();
  231. {
  232. if (_pwe)
  233. {
  234. TraceMsg(TF_TASKS, "[%x] Firing finished", _dwThreadId);
  235. _pwe->FireOnFinished();
  236. }
  237. }
  238. _UnlockWorker();
  239. return 0;
  240. case WORKERWIN_FIRE_DATASETCHANGED:
  241. // Posted by worker thread when matrix array finished enumerating
  242. _LockWorker();
  243. {
  244. if (_pwe)
  245. {
  246. TraceMsg(TF_TASKS, "[%x] Firing DatasetChanged", _dwThreadId);
  247. _pwe->FireOnDatasetChanged();
  248. }
  249. }
  250. _UnlockWorker();
  251. return 0;
  252. }
  253. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  254. }
  255. /*-------------------------------------------------------------------------
  256. Purpose: Start and exit of worker thread to do slow app information
  257. */
  258. DWORD
  259. CALLBACK
  260. CWorkerThread::_ThreadStartProcWrapper(
  261. LPVOID lpParam // this pointer of object since wrapper is static
  262. )
  263. {
  264. CWorkerThread * pwt = (CWorkerThread *)lpParam;
  265. pwt->_dwThreadId = GetCurrentThreadId();
  266. return pwt->_ThreadStartProc();
  267. }
  268. /*-------------------------------------------------------------------------
  269. Purpose: Contains the code run on the worker thread where we get the slow
  270. information about applications
  271. */
  272. DWORD
  273. CWorkerThread::_ThreadStartProc()
  274. {
  275. // Don't bother killing the worker window here, let the main thread take care
  276. // of the life time of the worker window.
  277. // Signal that we don't have a worker thread anymore. Prevents race
  278. // conditions.
  279. _fKillWorker = FALSE;
  280. TraceMsg(TF_TASKS, "[%x] Exiting worker thread", _dwThreadId);
  281. // Release the ref count to "this" object at end of thread, be it CMtxArray or CDataSrc because we
  282. // AddRef()ed before this thread started.
  283. Release();
  284. return 0;
  285. }
  286. #endif //DOWNLEVEL_PLATFORM