Thread.cpp
上传用户:yanganfa
上传日期:2007-01-01
资源大小:195k
文件大小:70k
源码类别:

进程与线程

开发平台:

Visual C++

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Abstract Base CThread class for creating general Worker Thread Objects
  3. /////////////////////////////////////////////////////////////////////////////
  4. /////////////////////////////////////////////////////////////////////////////
  5. //
  6. //@doc CTHREAD
  7. //
  8. //@module CThread.cpp |
  9. //
  10. //PROJECT : CThread Class
  11. //<nl>SUBSYSTEM : CThread Base Class
  12. //<nl>AUTHOR : Dominik Filipp, <cp>1999, Slovakia, Europe
  13. //
  14. //<nl>DESCRIPTION:
  15. //<nl>Abstract Base CThread Class implementation file.
  16. //
  17. /////////////////////////////////////////////////////////////////////////////
  18. #include "stdafx.h"
  19. #include "Thread.h"
  20. #ifdef _DEBUG
  21. #define new DEBUG_NEW
  22. #undef THIS_FILE
  23. static char THIS_FILE[] = __FILE__;
  24. #endif
  25. IMPLEMENT_DYNAMIC(CThread, CObject)
  26. /////////////////////////////////////////////////////////////////////////////
  27. // CThread static members
  28. CList<CThread::THREAD_SYNCHRONICITY, CThread::THREAD_SYNCHRONICITY&> CThread::s_synchronicityList;
  29. CThread::CInternalSynchronization CThread::s_InternalThreadSynchronization;
  30. LPCRITICAL_SECTION CThread::s_pProcessLockObject = NULL;
  31. // @mfunc | CThreadException | CThreadException | CThreadException constructor.
  32. // General CThreadException class intended to be used by CThread-derived classes.
  33. // @parmopt CThread* | pThread | NULL | [in] CThread object causing an exception.
  34. // @xref <mf CThreadException::SetThread>
  35. CThreadException::CThreadException(CThread* pThread /*= NULL*/)
  36. {
  37. m_pThread = pThread;
  38. m_strErrorMsg = _T("");
  39. m_nExceptionType = CThreadException::UNKNOWN_ERROR;
  40. }
  41. // @mfunc | CThreadException | CThreadException | CThreadException constructor.
  42. // General CThreadException class intended to be used by CThread-derived classes.
  43. // CThread-derived methods should always throw CThreadException exceptions.
  44. // To distinguish the origin of thrown exceptions the user should use the type of CThreadException exception.
  45. // CThread methods use predefined CThreadException types but the user may define own types in CThread-derived class.
  46. // Predefined types used in CThread class are described in 'Parameters' section.
  47. // @parm CThread* | pThread | [in] CThread object causing an exception.
  48. // @parm CString | strErrorMsg | [in] String describing an exception origin.
  49. // @parmopt int | nExceptionType | CThreadException::UNKNOWN_ERROR | [in] Type of thrown exception.
  50. // @flag CThreadException::UNKNOWN_ERROR  | Exception origin not known.
  51. // @flag CThreadException::CANNOT_CREATE_THREAD  | WINDOWS Thread cannot be created.
  52. // @flag CThreadException::THREAD_ALREADY_STARTED  | Thread is already started.
  53. // @flag CThreadException::CANNOT_TERMINATE_THREAD  | Thread termination failed.
  54. // @flag CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT  | Internal Synchronization error.
  55. // @flag CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT  | Internal Synchronization error.
  56. // @xref <mf CThreadException::SetThread>
  57. // <nl><mf CThreadException::SetExceptionType>
  58. CThreadException::CThreadException(CThread* pThread, CString strErrorMsg, int nExceptionType /*= CThreadException::UNKNOWN_ERROR*/)
  59. {
  60. m_pThread = pThread;
  61. m_strErrorMsg = strErrorMsg;
  62. m_nExceptionType = nExceptionType;
  63. }
  64. // @mfunc | CThreadException | CThreadException | CThreadException constructor.
  65. // General CThreadException class intended to be used by CThread-derived classes.
  66. // CThread-derived methods should always throw CThreadException exceptions.
  67. // To distinguish the origin of thrown exceptions the user should use the type of CThreadException exception.
  68. // CThread methods use predefined CThreadException types but the user may define own types in CThread-derived class.
  69. // Predefined types used in CThread class are described in 'Parameters' section.
  70. // @parm CThread* | pThread | [in] CThread object causing an exception.
  71. // @parm UINT | nErrorMsgID | [in] Resource ID of the string describing an exception origin.
  72. // @parmopt int | nExceptionType | CThreadException::UNKNOWN_ERROR | [in] Type of thrown exception.
  73. // @flag CThreadException::UNKNOWN_ERROR  | Exception origin not known.
  74. // @flag CThreadException::CANNOT_CREATE_THREAD  | WINDOWS Thread cannot be created.
  75. // @flag CThreadException::THREAD_ALREADY_STARTED  | Thread is already started.
  76. // @flag CThreadException::CANNOT_TERMINATE_THREAD  | Thread termination failed.
  77. // @flag CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT  | Internal Synchronization error.
  78. // @flag CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT  | Internal Synchronization error.
  79. // @xref <mf CThreadException::SetThread>
  80. // <nl><mf CThreadException::SetExceptionType>
  81. CThreadException::CThreadException(CThread* pThread, UINT nErrorMsgID, int nExceptionType /*= CThreadException::UNKNOWN_ERROR*/)
  82. {
  83. m_pThread = pThread;
  84. m_strErrorMsg.Format(nErrorMsgID);
  85. m_nExceptionType = nExceptionType;
  86. }
  87. // @mfunc | CThreadException | GetThread | Gets CThread object that caused an exception.
  88. // @rdesc
  89. // CThread object that caused an exception.
  90. // @xref <mf CThreadException::SetThread>
  91. CThread* CThreadException::GetThread() const
  92. {
  93. return m_pThread;
  94. }
  95. // @mfunc void | CThreadException | SetThread | Sets CThread object that caused an exception.
  96. // @parm CThread* | pThread | [in] CThread object that caused an exception.
  97. // @xref <mf CThreadException::GetThread>
  98. void CThreadException::SetThread(CThread* pThread)
  99. {
  100. m_pThread = pThread;
  101. }
  102. // @mfunc int | CThreadException | ReportError | Display an exception-error message in the standard Message Box.
  103. // @parmopt UINT | nType | MB_OK | [in] Type of the Message Box to be displayed.
  104. // @parmopt UINT | nMessageID | 0 | [in] Error message string resource ID.
  105. // @rdesc
  106. // Message Box return value.
  107. int CThreadException::ReportError(UINT nType /*= MB_OK*/, UINT nMessageID /*= 0*/)
  108. {
  109. CString strErrorMsg = m_strErrorMsg;
  110. if (nMessageID != 0) strErrorMsg.Format(nMessageID);
  111. return AfxMessageBox(m_strErrorMsg, nType);
  112. }
  113. // @mfunc void | CThreadException | SetErrorMsg | Sets an error message describing an exception origin.
  114. // @parm CString | strErrorMsg | [in] Error message string.
  115. // @xref <mf CThreadException::GetErrorMsg>
  116. void CThreadException::SetErrorMsg(CString strErrorMsg)
  117. {
  118. m_strErrorMsg = strErrorMsg;
  119. }
  120. // @mfunc void | CThreadException | SetErrorMsg | Sets an error message describing an exception origin.
  121. // @parm UINT | nErrorMsgID | [in] Error message string resource ID.
  122. // @xref <mf CThreadException::GetErrorMsg>
  123. void CThreadException::SetErrorMsg(UINT nErrorMsgID)
  124. {
  125. m_strErrorMsg.Format(nErrorMsgID);
  126. }
  127. // @mfunc CString | CThreadException | GetErrorMsg | Gets an error message describing an exception origin.
  128. // @rdesc
  129. // Error message string describing an exception.
  130. // @xref <mf CThreadException::SetErrorMsg>
  131. CString CThreadException::GetErrorMsg() const
  132. {
  133. return m_strErrorMsg;
  134. }
  135. // @mfunc void | CThreadException | SetExceptionType | Sets the type of an exception.
  136. // CThread-derived methods should always throw CThreadException exceptions.
  137. // To distinguish the origin of thrown exceptions the user may define own types of
  138. // CThreadException exceptions in the CThread-derived class.
  139. // Predefined types used in CThread class are described in 'Parameters' section.
  140. // @parm UINT | nErrorMsgID | [in] Exception type.
  141. // @flag CThreadException::UNKNOWN_ERROR  | Exception origin not known.
  142. // @flag CThreadException::CANNOT_CREATE_THREAD  | WINDOWS Thread cannot be created.
  143. // @flag CThreadException::THREAD_ALREADY_STARTED  | Thread is already started.
  144. // @flag CThreadException::CANNOT_TERMINATE_THREAD  | Thread termination failed.
  145. // @flag CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT  | Internal Synchronization error.
  146. // @flag CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT  | Internal Synchronization error.
  147. // @xref <mf CThreadException::GetExceptionType>
  148. void CThreadException::SetExceptionType(int nExceptionType)
  149. {
  150. m_nExceptionType = nExceptionType;
  151. }
  152. // @mfunc int | CThreadException | GetExceptionType | Gets the type of an exception.
  153. // Types used in the CThread class are described in 'Return Value' section.
  154. // @rdesc
  155. // Predefined types that may be thrown by CThread methods are as follows:
  156. // @flag CThreadException::UNKNOWN_ERROR  | Exception origin not known.
  157. // @flag CThreadException::CANNOT_CREATE_THREAD  | WINDOWS Thread cannot be created.
  158. // @flag CThreadException::THREAD_ALREADY_STARTED  | Thread is already started.
  159. // @flag CThreadException::CANNOT_TERMINATE_THREAD  | Thread termination failed.
  160. // @flag CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT  | Internal Synchronization error.
  161. // @flag CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT  | Internal Synchronization error.
  162. // @xref <mf CThreadException::SetExceptionType>
  163. int CThreadException::GetExceptionType() const
  164. {
  165. return m_nExceptionType;
  166. }
  167. // @mfunc static CString | CThreadException | GetLastSystemErrorMsg | Static method which returns the message text
  168. // corresponding to the system error code. This error code should be returned
  169. // by GetLastError() WINDOWS function immediately after the system error occurs.
  170. // @rdesc
  171. // String representation of the system error code.
  172. // @parm DWORD | dwSysErrorCode | [in] System error code returned by GetLastError() WINDOWS function.
  173. CString CThreadException::GetLastSystemErrorMsg(DWORD dwSysErrorCode)
  174. {
  175. CString strErrMsg("");
  176. LPVOID lpMsgBuf = NULL;
  177. ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  178. NULL,
  179. dwSysErrorCode,
  180. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  181. (LPTSTR) &lpMsgBuf,
  182. 0,
  183. NULL
  184. );
  185. if (lpMsgBuf != NULL)
  186. {
  187. strErrMsg = (LPCSTR)lpMsgBuf;
  188. ::LocalFree(lpMsgBuf);
  189. };
  190. return strErrMsg;
  191. }
  192. /////////////////////////////////////////////////////////////////////////////
  193. // Internal nested CThread Synchronization Class implementation /////////////
  194. // Only one instance of this class is established
  195. CThread::CInternalSynchronization::CInternalSynchronization()
  196. {
  197. CThread::ResetSynchronizationList();
  198. m_pInternalCriticalSection = new CRITICAL_SECTION;
  199. ::InitializeCriticalSection(m_pInternalCriticalSection);
  200. }
  201. CThread::CInternalSynchronization::~CInternalSynchronization()
  202. {
  203. CThread::ResetSynchronizationList();
  204. ::DeleteCriticalSection(m_pInternalCriticalSection);
  205. delete m_pInternalCriticalSection;
  206. m_pInternalCriticalSection = NULL;
  207. }
  208. void CThread::CInternalSynchronization::InternalLock()
  209. {
  210. if (m_pInternalCriticalSection != NULL)
  211. ::EnterCriticalSection(m_pInternalCriticalSection);
  212. }
  213. void CThread::CInternalSynchronization::InternalUnlock()
  214. {
  215. if (m_pInternalCriticalSection != NULL)
  216. ::LeaveCriticalSection(m_pInternalCriticalSection);
  217. }
  218. /////////////////////////////////////////////////////////////////////////////
  219. // Process Locking mechanism (Process locking ) ///////////////////////////////
  220. // @mfunc static void | CThread | OpenProcessLocking | Static method that opens the process
  221. // synchronization. After the first call the process synchronization is opened.
  222. // Additional call of this method has no any effect - the synchronization remains opened.
  223. // The synchronization itself is accomplished by calling <mf CThread::ProcessLock> and <mf CThread::ProcessUnlock> methods
  224. // between which is the critical code that is to be executed process-exclusively.
  225. // Opened process-synchronization should be finally closed by <mf CThread::CloseProcessLocking>
  226. // method.
  227. // <nl>This locking mechanism does not support an inter-process synchronization.
  228. // <nl><nl>As a static method may be used wherever in the code without
  229. // constructing any CThread object.
  230. // @xref <mf CThread::CloseProcessLocking>
  231. // <nl><mf CThread::ProcessLock>
  232. // <nl><mf CThread::ProcessUnlock>
  233. void CThread::OpenProcessLocking()
  234. {
  235. if (CThread::s_pProcessLockObject == NULL)
  236. {
  237. CThread::s_pProcessLockObject = new CRITICAL_SECTION;
  238. ::InitializeCriticalSection(CThread::s_pProcessLockObject);
  239. };
  240. }
  241. // Process Locking mechanism (Process locking ) ///////////////////////////////
  242. // @mfunc static void | CThread | ProcessLock | Static method that marks the beginning of the
  243. // critical code that is to be executed process-exclusively.
  244. // <nl><mf CThread::ProcessUnlock> method is the counterpart of the ProcessLock() method which bounds the end of
  245. // the critical code. Both methods should be used in a way to prevent
  246. // eventual deadlocks. Before using the method the process-synchronization must be opened
  247. // by <mf CThread::OpenProcessLocking> method.
  248. // <nl>This locking mechanism does not support an inter-process synchronization.
  249. // <nl><nl>As a static method may be used wherever in the code without
  250. // constructing any CThread object.
  251. // @xref <mf CThread::OpenProcessLocking>
  252. // <nl><mf CThread::CloseProcessLocking>
  253. // <nl><mf CThread::ProcessUnlock>
  254. void CThread::ProcessLock()
  255. {
  256. if (CThread::s_pProcessLockObject != NULL)
  257. ::EnterCriticalSection(CThread::s_pProcessLockObject);
  258. }
  259. // Process Locking mechanism (Process locking ) ///////////////////////////////
  260. // @mfunc static void | CThread | ProcessUnlock | Static method that marks the end of the
  261. // critical code that is to be executed process-exclusively.
  262. // <nl><mf CThread::ProcessLock> method is the counterpart of ProcessUnlock() method which bounds the start of
  263. // the critical code. Both methods should be used in a way to prevent
  264. // eventual deadlocks. Before using the method the process-synchronization must be opened
  265. // by <mf CThread::OpenProcessLocking> method.
  266. // <nl>This locking mechanism does not support an inter-process synchronization.
  267. // <nl><nl>As a static method may be used wherever in the code without
  268. // constructing any CThread object.
  269. // @xref <mf CThread::OpenProcessLocking>
  270. // <nl><mf CThread::CloseProcessLocking>
  271. // <nl><mf CThread::ProcessLock>
  272. void CThread::ProcessUnlock()
  273. {
  274. if (CThread::s_pProcessLockObject != NULL)
  275. ::LeaveCriticalSection(CThread::s_pProcessLockObject);
  276. }
  277. // Process Locking mechanism (Process locking ) ///////////////////////////////
  278. // @mfunc static void | CThread | CloseProcessLocking | Static method that closes the process
  279. // synchronization that was previously opened by <mf CThread::OpenProcessLocking>.
  280. // <nl>The synchronization itself is accomplished by calling <mf CThread::ProcessLock>
  281. // and <mf CThread::ProcessUnlock> methods between which is the critical code that
  282. // is to be executed process-exclusively.
  283. // <nl><nl>As a static method may be used wherever in the code without
  284. // constructing any CThread object.
  285. // @xref <mf CThread::OpenProcessLocking>
  286. // <nl><mf CThread::ProcessLock>
  287. // <nl><mf CThread::ProcessUnlock>
  288. void CThread::CloseProcessLocking()
  289. {
  290. if (CThread::s_pProcessLockObject != NULL)
  291. {
  292. ::DeleteCriticalSection(CThread::s_pProcessLockObject);
  293. delete CThread::s_pProcessLockObject;
  294. CThread::s_pProcessLockObject = NULL;
  295. };
  296. }
  297. /////////////////////////////////////////////////////////////////////////////
  298. /////////////////////////////////////////////////////////////////////////////
  299. // Worker Thread Class implementation ///////////////////////////////////////
  300. /////////////////////////////////////////////////////////////////////////////
  301. // Basic Data initialization.
  302. // @mfunc | CThread | CThread | CThread constructor.
  303. // User may pass the so-called 'Owner object' to the CThread class. The Owner object
  304. // is an arbitrary object claimed to be an owner of CThread object.
  305. // In such way the Owner object may be notified by CThread object while running the thread.
  306. // Optional LPARAM parameter allows the user to pass an additional useful information.
  307. // It's up to the developer's responsibility how to interpret and implement the
  308. // Owner object parameters in CThread-derived class. CThread class itself
  309. // just stores the passed values.The mentioned parameters may be also set up or read
  310. // after CThread object construction by calling the <mf CThread::SetOwnerParams> and
  311. // <mf CThread::GetOwnerParams> methods.
  312. // <nl><nl>While constructing CThread object no any WINDOWS thread is being started.
  313. // @parmopt void* | pOwnerObject | NULL | [in] CThread Owner object.
  314. // @parmopt LPARAM | lParam | 0 | [in] General parameter passed to CThread object.
  315. // @xref <mf CThread::SetOwnerParams>
  316. // <nl><mf CThread::GetOwnerParams>
  317. CThread::CThread(void* pOwnerObject /*= NULL*/, LPARAM lParam /*= 0L*/)
  318. {
  319. m_pOwnerObject = pOwnerObject;
  320. m_lParam = lParam;
  321. m_lpThreadAttributes = NULL;
  322. m_dwStackSize = 0;
  323. m_bIsSynchronized = FALSE;
  324. m_bSupportSignalNotification = FALSE;
  325. m_runtimeSynchronizationObjectPosition = NULL;
  326. m_hSignalObject = NULL;
  327. m_dwThreadExitCode = (DWORD)CThread::DW_UNDEFINED;
  328. m_strThreadHandlerClassName = _T("");
  329. ResetThreadRuntimeData();
  330. }
  331. // Removing WINDOWS Thread (if it is not already stopped or killed)
  332. // when last instance of CThread Object is being destructed.
  333. // @mfunc | CThread | ~CThread | CThread destructor.
  334. // CThread destructor kills the running WINDOWS thread that was not previously stopped
  335. // and closes the CThread notification mode.
  336. CThread::~CThread()
  337. {
  338. if (IsAlive()) try {Kill();} catch (...) {};
  339. // Lock /////////////////////////////////////////////////////////////////
  340. s_InternalThreadSynchronization.InternalLock();
  341. /////////////////////////////////////////////////////////////////////////
  342. UnregisterNotificationObject();
  343. ResetThreadRuntimeData();
  344. // Unlock ///////////////////////////////////////////////////////////////
  345. s_InternalThreadSynchronization.InternalUnlock();
  346. /////////////////////////////////////////////////////////////////////////
  347. }
  348. // Basic Data initialization.
  349. // @mfunc void | CThread | SetOwnerParams | Sets CThread Owner object parameters.
  350. // User may pass the so-called 'Owner object' to the CThread class. The Owner object
  351. // is an arbitrary object claimed to be an owner of CThread object.
  352. // In such way the Owner object may be notified by CThread object while running the thread.
  353. // Optional LPARAM parameter allows the user to pass an additional useful information.
  354. // It's up to the developer's responsibility how to interpret and implement the
  355. // Owner object parameters in CThread-derived class. CThread class itself
  356. // just store the passed values.The mentioned parameters can be read
  357. // after the CThread object construction by calling the <mf CThread::GetOwnerParams> method.
  358. // @parm void* | pOwnerObject | [in] CThread Owner object.
  359. // @parmopt LPARAM | lParam | 0 | [in] General parameter passed to CThread object.
  360. // @xref <mf CThread::GetOwnerParams>
  361. void CThread::SetOwnerParams(void* pOwnerObject, LPARAM lParam /*= 0L*/)
  362. {
  363. m_pOwnerObject = pOwnerObject;
  364. m_lParam = lParam;
  365. }
  366. // @mfunc void | CThread | GetOwnerParams | Retrieves the CThread Owner object parameters.
  367. // @parm void*& | pOwnerObject | [out] Reference to the CThread Owner object being retrieved.
  368. // @parm LPARAM& | lParam | [out] Reference to the LPARAM parameter being retrieved.
  369. // @xref <mf CThread::SetOwnerParams>
  370. void CThread::GetOwnerParams(void*& pOwnerObject, LPARAM& lParam) const
  371. {
  372. pOwnerObject = m_pOwnerObject;
  373. lParam = m_lParam;
  374. }
  375. /////////////////////////////////////////////////////////////////////////////
  376. // CThread diagnostics
  377. #ifdef _DEBUG
  378. void CThread::AssertValid() const
  379. {
  380. CObject::AssertValid();
  381. }
  382. void CThread::Dump(CDumpContext& dc) const
  383. {
  384. CObject::Dump(dc);
  385. }
  386. #endif //_DEBUG
  387. /////////////////////////////////////////////////////////////////////////////
  388. void CThread::ResetThreadRuntimeData()
  389. {
  390. m_hThreadHandle = NULL;
  391. m_dwThreadID = 0;
  392. m_nActivityStatus = CThread::THREAD_STOPPED;
  393. m_nCommandList.RemoveAll();
  394. m_nCommandList.FreeExtra();
  395. }
  396. void CThread::ResetThreadObject(DWORD dwExitCode)
  397. {
  398. // Lock /////////////////////////////////////////////////////////////////
  399. s_InternalThreadSynchronization.InternalLock();
  400. /////////////////////////////////////////////////////////////////////////
  401. UnregisterRuntimeSynchronization();
  402. if (m_hThreadHandle != NULL) ::CloseHandle(m_hThreadHandle);
  403. ResetThreadRuntimeData();
  404. m_dwThreadExitCode = dwExitCode;
  405. // Unlock ///////////////////////////////////////////////////////////////
  406. s_InternalThreadSynchronization.InternalUnlock();
  407. /////////////////////////////////////////////////////////////////////////
  408. }
  409. /////////////////////////////////////////////////////////////////////////////
  410. // CThread Attributes and Operations
  411. // Exact-Class specific Synchronization helper methods
  412. void CThread::ResetSynchronizationList()
  413. {
  414. if (CThread::s_synchronicityList.GetCount() > 0)
  415. {
  416. while (CThread::s_synchronicityList.GetHeadPosition() != NULL)
  417. {
  418. if (CThread::s_synchronicityList.GetHead().pCriticalSection != NULL)
  419. {
  420. ::DeleteCriticalSection(CThread::s_synchronicityList.GetHead().pCriticalSection);
  421. delete CThread::s_synchronicityList.GetHead().pCriticalSection;
  422. };
  423. CThread::s_synchronicityList.RemoveHead();
  424. };
  425. };
  426. }
  427. void CThread::ResetSynchronizationObject(THREAD_SYNCHRONICITY& synchronizationObject)
  428. {
  429. synchronizationObject.strThreadHandlerClassName = _T("");
  430. synchronizationObject.pCriticalSection = NULL;
  431. synchronizationObject.dwCountOfRunningThreads = 0;
  432. }
  433. void CThread::UpdateRuntimeSynchronizationObject(THREAD_SYNCHRONICITY& synchronizationObject)
  434. {
  435. CThread::s_synchronicityList.SetAt(m_runtimeSynchronizationObjectPosition, synchronizationObject);
  436. }
  437. void CThread::DeleteRuntimeSynchronizationObject()
  438. {
  439. CThread::s_synchronicityList.RemoveAt(m_runtimeSynchronizationObjectPosition);
  440. }
  441. BOOL CThread::FindRuntimeSynchronizationObject()
  442. {
  443. THREAD_SYNCHRONICITY synchronizationElement;
  444. POSITION currentPos = CThread::s_synchronicityList.GetHeadPosition(), newPos = currentPos;
  445. ResetSynchronizationObject(synchronizationElement);
  446. while (currentPos != NULL)
  447. {
  448. synchronizationElement = CThread::s_synchronicityList.GetNext(newPos);
  449. if (synchronizationElement.strThreadHandlerClassName == m_strThreadHandlerClassName)
  450. {
  451. m_runtimeSynchronizationObjectPosition = currentPos;
  452. return TRUE;
  453. }
  454. else
  455. currentPos = newPos;
  456. };
  457. return FALSE;
  458. }
  459. CThread::THREAD_SYNCHRONICITY CThread::GetRuntimeSynchronizationObject()
  460. {
  461. return CThread::s_synchronicityList.GetAt(m_runtimeSynchronizationObjectPosition);
  462. }
  463. void CThread::RegisterRuntimeSynchronization()
  464. {
  465. if (m_bIsSynchronized)
  466. {
  467. THREAD_SYNCHRONICITY runtimeSynchronizationObject;
  468. if (!FindRuntimeSynchronizationObject())
  469. {
  470. ResetSynchronizationObject(runtimeSynchronizationObject);
  471. runtimeSynchronizationObject.strThreadHandlerClassName = m_strThreadHandlerClassName;
  472. runtimeSynchronizationObject.dwCountOfRunningThreads++;
  473. runtimeSynchronizationObject.pCriticalSection = new CRITICAL_SECTION;
  474. m_runtimeSynchronizationObjectPosition = CThread::s_synchronicityList.AddTail(runtimeSynchronizationObject);
  475. ::InitializeCriticalSection(runtimeSynchronizationObject.pCriticalSection);
  476. }
  477. else
  478. {
  479. runtimeSynchronizationObject = GetRuntimeSynchronizationObject();
  480. runtimeSynchronizationObject.dwCountOfRunningThreads++;
  481. UpdateRuntimeSynchronizationObject(runtimeSynchronizationObject);
  482. };
  483. };
  484. }
  485. void CThread::UnregisterRuntimeSynchronization()
  486. {
  487. if (m_bIsSynchronized)
  488. {
  489. if (m_runtimeSynchronizationObjectPosition != NULL)
  490. {
  491. THREAD_SYNCHRONICITY runtimeSynchronizationObject;
  492. runtimeSynchronizationObject = GetRuntimeSynchronizationObject();
  493. if (runtimeSynchronizationObject.dwCountOfRunningThreads > 1)
  494. {
  495. runtimeSynchronizationObject.dwCountOfRunningThreads--;
  496. UpdateRuntimeSynchronizationObject(runtimeSynchronizationObject);
  497. }
  498. else
  499. {
  500. ::DeleteCriticalSection(runtimeSynchronizationObject.pCriticalSection);
  501. delete runtimeSynchronizationObject.pCriticalSection;
  502. DeleteRuntimeSynchronizationObject();
  503. m_runtimeSynchronizationObjectPosition = NULL;
  504. };
  505. };
  506. };
  507. }
  508. // End of Object-specific Synchronizing helper methods //////////////////////
  509. // Lock critical code used in the main 'ThreadHandler' overriden method in the derived class.
  510. // @mfunc void | CThread | Lock | Marks the beginning of the critical code to be
  511. // 'Thread-Handler-Oriented' synchronized. This special synchronization mechanism
  512. // allows to lock the critical code and make it exclusive for the first thread
  513. // reaching this method. Consequently all other CThread objects that operate on the same
  514. // <mf CThread::ThreadHandler> are blocked until the thread unlocks the critical code.
  515. // Other threads operating on the different <mf CThread::ThreadHandler> are not blocked and are
  516. // synchronized in exactly same way in their own group. Thus, CThreads objects can be
  517. // splitted into the groups operating on the same <mf CThread::ThreadHandler> method. Each group
  518. // is synchronized independently to each other.
  519. // <nl>All managing concerning this kind of synchronization is done automatically, users do not
  520. // care about any aspect of synchronization. All they have to do is to use the Lock() and <mf CThread::Unlock>
  521. // methods to bound the critical code.
  522. // <nl>Thread-Handler-Oriented synchronization is, however, not always usable. If the user needs to lock
  523. // the shared common object that cannot be accessed simultaneously under any circumstances,
  524. // he should use <mf CThread::ProcessLock> and <mf CThread::ProcessUnlock> methods. These methods guarantee
  525. // the process-exclusive access to the critical code (but not an inter-process exclusive access).
  526. // <nl>Lock() method must be followed by <mf CThread::Unlock> method in a way to prevent eventual deadlocks.
  527. // <nl><nl>This kind of synchronization can be established by adding SUPPORT_THREAD_SYNCHRONIZATION(<ClassName>) macro
  528. // to the user CThread-derived class constructor in which <mf CThread::ThreadHandler> method is implemented.
  529. // @xref <mf CThread::Unlock>
  530. // <nl><mf CThread::ProcessLock>
  531. // <nl><mf CThread::ProcessUnlock>
  532. void CThread::Lock()
  533. {
  534. // Lock /////////////////////////////////////////////////////////////////
  535. s_InternalThreadSynchronization.InternalLock();
  536. /////////////////////////////////////////////////////////////////////////
  537. if (m_bIsSynchronized && m_runtimeSynchronizationObjectPosition != NULL)
  538. {
  539. LPCRITICAL_SECTION pCritSection = GetRuntimeSynchronizationObject().pCriticalSection;
  540. // Unlock ///////////////////////////////////////////////////////////
  541. s_InternalThreadSynchronization.InternalUnlock();
  542. /////////////////////////////////////////////////////////////////////
  543. ::EnterCriticalSection(pCritSection);
  544. }
  545. else
  546. {
  547. // Unlock ///////////////////////////////////////////////////////////
  548. s_InternalThreadSynchronization.InternalUnlock();
  549. /////////////////////////////////////////////////////////////////////
  550. };
  551. }
  552. // Unlock critical code used in the main 'ThreadHandler' overriden method in the derived class.
  553. // @mfunc void | CThread | Unlock | Marks the end of the critical code in
  554. // Thread-Handler-Oriented synchronization mode. This method must be preceded by
  555. // <mf CThread::Lock> method.
  556. // @xref <mf CThread::Lock>
  557. void CThread::Unlock()
  558. {
  559. // Lock /////////////////////////////////////////////////////////////////
  560. s_InternalThreadSynchronization.InternalLock();
  561. /////////////////////////////////////////////////////////////////////////
  562. if (m_bIsSynchronized && m_runtimeSynchronizationObjectPosition != NULL)
  563. {
  564. LPCRITICAL_SECTION pCritSection = GetRuntimeSynchronizationObject().pCriticalSection;
  565. // Unlock ///////////////////////////////////////////////////////////
  566. s_InternalThreadSynchronization.InternalUnlock();
  567. /////////////////////////////////////////////////////////////////////
  568. ::LeaveCriticalSection(pCritSection);
  569. }
  570. else
  571. {
  572. // Unlock ///////////////////////////////////////////////////////////
  573. s_InternalThreadSynchronization.InternalUnlock();
  574. /////////////////////////////////////////////////////////////////////
  575. };
  576. }
  577. // Set WINDOWS-Thread specific attributes.
  578. // @mfunc void | CThread | SetAttributes | Sets the WINDOWS thread security attributes
  579. // (see WINDOWS Threads in Microsoft MSDN documentation).
  580. // <nl>Thread attributes - if necessary - should be set before CThread thread is started.
  581. // @parmopt LPSECURITY_ATTRIBUTES | lpThreadAttributes | NULL | [in] Pointer to the security attributes structure.
  582. // @flag SECURITY_ATTRIBUTES structure may be created temporarily. Passing its pointer
  583. // to this method means copying the structure contents to the internal data space of CThread
  584. // object. Therefore, thread attributes remain valid regardless the SECURITY_ATTRIBUTES structure (which
  585. // 'lpThreadAttributes' parameter points to) exists or not.
  586. // @xref <mf CThread::GetAttributes>
  587. void CThread::SetAttributes(LPSECURITY_ATTRIBUTES lpThreadAttributes /*= NULL*/)
  588. {
  589. m_lpThreadAttributes = lpThreadAttributes;
  590. if (m_lpThreadAttributes != NULL)
  591. {
  592. m_ThreadAttributes = *m_lpThreadAttributes;
  593. m_lpThreadAttributes = &m_ThreadAttributes;
  594. };
  595. }
  596. // Get the current WINDOWS-Thread handler
  597. // @mfunc HANDLE | CThread | GetHandle | Gets the WINDOWS thread handle
  598. // (see WINDOWS Threads in Microsoft MSDN documentation).
  599. // @rdesc
  600. // Handle of WINDOWS thread or NULL if the thread is not created.
  601. HANDLE CThread::GetHandle() const
  602. {
  603. return m_hThreadHandle;
  604. }
  605. // Get the current WINDOWS-Thread ID
  606. // @mfunc DWORD | CThread | GetID | Gets the WINDOWS thread ID
  607. // (see WINDOWS Threads in Microsoft MSDN documentation).
  608. // @rdesc
  609. // WINDOWS thread ID or 0 if the thread is not created.
  610. DWORD CThread::GetID() const
  611. {
  612. return m_dwThreadID;
  613. }
  614. // Get the current WINDOWS-Thread security attributes
  615. // @mfunc SECURITY_ATTRIBUTES | CThread | GetAttributes | Gets the WINDOWS thread security attributes
  616. // (see WINDOWS Threads in Microsoft MSDN documentation).
  617. // @rdesc
  618. // Fills up the SECURITY_ATTRIBUTES structure with valid data.
  619. // If the WINDOWS thread is not created the SECURITY_ATTRIBUTES structure is null-reset.
  620. // @xref <mf CThread::SetAttributes>
  621. SECURITY_ATTRIBUTES CThread::GetAttributes() const
  622. {
  623. return m_ThreadAttributes;
  624. }
  625. // Set WINDOWS-Thread stack size
  626. // @mfunc void | CThread | SetStackSize | Sets the WINDOWS thread stack size
  627. // (see WINDOWS Threads in Microsoft MSDN documentation).
  628. // <nl>Thread stack size - if necessary - should be set before CThread thread is started.
  629. // @parmopt DWORD | dwStackSize | 0 | [in] Desired WINDOWS thread stack size.
  630. // @xref <mf CThread::GetStackSize>
  631. void CThread::SetStackSize(DWORD dwStackSize /*= 0*/)
  632. {
  633. m_dwStackSize = dwStackSize;
  634. }
  635. // Get the current WINDOWS-Thread stack size
  636. // @mfunc DWORD | CThread | GetStackSize | Gets the WINDOWS thread stack size
  637. // (see WINDOWS Threads).
  638. // @rdesc
  639. // WINDOWS thread stack size.
  640. // @xref <mf CThread::SetStackSize>
  641. DWORD CThread::GetStackSize() const
  642. {
  643. return m_dwStackSize;
  644. }
  645. // Set the Thread activity status (see: Protected 'enum {}' values in 'CThread.h')
  646. // @mfunc void | CThread | SetActivityStatus | Sets CThread activity status.
  647. // CThread activity status describes the current CThread activity.
  648. // This method should be used in the <mf CThread::ThreadHandler> CThread-derived class method
  649. // before the appropriate task is being performed. The status informs the thread-owner
  650. // process about the current CThread object activity. Thread-owner
  651. // process may call <mf CThread::GetActivityStatus> method to restore the current status.
  652. // <nl>CThread class offers the predefined values as seen in the 'Parameters' section.
  653. // <nl><nl>For more information how to use this method see 'Developer.doc' documentation.
  654. // @parm int | nActivityStatus | [in] CThread activity status.
  655. // @flag CThread::THREAD_STOPPED  | Thread is stopped.
  656. // @flag CThread::THREAD_RUNNING  | Thread is running.
  657. // @flag CThread::THREAD_PAUSED  | Thread is paused.
  658. // @flag CThread::THREAD_CONTINUING  | Thread is continuing.
  659. // @flag CThread::THREAD_PENDING  | Thread is pending.
  660. // @flag CThread::THREAD_USER_ACTIVITY  | Base offset for user-defined activity statuses.
  661. // @xref <mf CThread::GetActivityStatus>
  662. void CThread::SetActivityStatus(int nActivityStatus)
  663. {
  664. m_nActivityStatus = nActivityStatus;
  665. }
  666. // Get the current Thread activity status (see: Protected 'enum {}' values in 'CThread.h')
  667. // User-defined Activity Status values in range: CThread::THREAD_USER_ACTIVITY (5) and higher
  668. // @mfunc int | CThread | GetActivityStatus | Gets the current CThread activity status.
  669. // This method is normally called from within the thread-owner process to obtain the current
  670. // activity of the owned thread.
  671. // <nl>CThread class offers the predefined values as seen in the 'Return Value' section.
  672. // @rdesc
  673. // Current CThread activity status.
  674. // @flag CThread::THREAD_STOPPED  | Thread is stopped.
  675. // @flag CThread::THREAD_RUNNING  | Thread is running.
  676. // @flag CThread::THREAD_PAUSED  | Thread is paused.
  677. // @flag CThread::THREAD_CONTINUING  | Thread is continuing.
  678. // @flag CThread::THREAD_PENDING  | Thread is pending.
  679. // @flag CThread::THREAD_USER_ACTIVITY  | Base offset for user-defined activity statuses.
  680. // @xref <mf CThread::SetActivityStatus>
  681. int CThread::GetActivityStatus() const
  682. {
  683. return m_nActivityStatus;
  684. }
  685. // Wait for Activity Status
  686. // @mfunc BOOL | CThread | WaitForActivityStatus | Waits until the desired CThread activity
  687. // status is reached. Used in the thread-owner process.
  688. // <nl>This is a "good-servant-but-bad-master" method. The thread-owner process may fire
  689. // an arbitrary command to the CThread object by the <mf CThread::SendCommand> method.
  690. // That means, the <mf CThread::SendCommand> method fires a command and returns immediately.
  691. // However, in some cases the thread-owner process may want to wait until the command is
  692. // actually handled. For this reason the process may use this method which waits until
  693. // the desired activity status is set in the <mf CThread::ThreadHandler> method.
  694. // In this case the user is responsible to set the desired activity status in the
  695. // <mf CThread::ThreadHandler> by the <mf CThread::SetActivityStatus> method.
  696. // <nl>Using of this method requires an implementation of the proper 'set-thread-activity-status'
  697. // strategy. If, for example, the desired activity status is not properly set or is immediately
  698. // rewritten by another value, the desired status is consequently never reached and
  699. // the WaitForActivityStatus() method hangs. Sometimes the thread-synchronization locking
  700. // mechanism is necessary.
  701. // <nl>WaitForActivityStatus() method returns only in the case when the desired activity status
  702. // was actually reached or timeout interval elapsed.
  703. // <nl><nl>Note:
  704. // <nl>The Start() and Stop() methods do not have to be checked by this method. These methods
  705. // are executed synchronously and return when the CThread-thread actually starts or stops.
  706. // @parm int | nActivityStatus | [in] Desired CThread activity status that is to be reached.
  707. // @parmopt DWORD | dwTimeout | CThread::DW_INFINITE | [in] Timeout interval in tenths of second.
  708. // @rdesc
  709. // TRUE if the desired acivity status is reached.
  710. // @xref <mf CThread::SetActivityStatus>
  711. // <nl><mf CThread::SendCommand>
  712. // <nl><mf CThread::ThreadHandler>
  713. BOOL CThread::WaitForActivityStatus(int nActivityStatus, DWORD dwTimeout /*= CThread::DW_INFINITE*/) const
  714. {
  715. DWORD dwCount = 0, dwMaxCount = 0;
  716. dwMaxCount = (dwTimeout * 100) / CThread::BASE_DELAY_INTERVAL;
  717. while (m_nActivityStatus != nActivityStatus &&
  718.    (dwTimeout == INFINITE || dwCount++ < dwMaxCount))
  719. {
  720. ::Sleep(CThread::BASE_DELAY_INTERVAL);
  721. };
  722. return (BOOL)(m_nActivityStatus == nActivityStatus);
  723. }
  724. // @mfunc DWORD | CThread | SetPriority |
  725. // This method sets the WINDOWS thread priority. The priority may be set after
  726. // the WINDOWS thread has been started.
  727. // <nl>SetPriority() just encapsulates the WINDOWS SetThreadPriority() function.
  728. // For more information see Win32 SDK Programmer抯 Reference.
  729. // @rdesc
  730. // TRUE - the priority was properly set.
  731. // <nl>FALSE - the priority was not set or the thread is not runnning.
  732. // @parmopt int | nPriority | THREAD_PRIORITY_NORMAL | [in] WINDOWS thread priority.
  733. // Can be one of the following:
  734. // <nl><nl>THREAD_PRIORITY_NORMAL
  735. // <nl>THREAD_PRIORITY_ABOVE_NORMAL
  736. // <nl>THREAD_PRIORITY_BELOW_NORMAL
  737. // <nl>THREAD_PRIORITY_HIGHEST
  738. // <nl>THREAD_PRIORITY_IDLE
  739. // <nl>THREAD_PRIORITY_LOWEST
  740. // <nl>THREAD_PRIORITY_TIME_CRITICAL
  741. // @xref <mf CThread::GetPriority>
  742. BOOL CThread::SetPriority(int nPriority /*= THREAD_PRIORITY_NORMAL*/)
  743. {
  744. // Lock /////////////////////////////////////////////////////////////////
  745. s_InternalThreadSynchronization.InternalLock();
  746. /////////////////////////////////////////////////////////////////////////
  747. if (m_hThreadHandle != NULL)
  748. {
  749. BOOL bResult = ::SetThreadPriority(m_hThreadHandle, nPriority);
  750. // Unlock ///////////////////////////////////////////////////////////
  751. s_InternalThreadSynchronization.InternalUnlock();
  752. /////////////////////////////////////////////////////////////////////
  753. return bResult;
  754. }
  755. else
  756. {
  757. // Unlock ///////////////////////////////////////////////////////////
  758. s_InternalThreadSynchronization.InternalUnlock();
  759. /////////////////////////////////////////////////////////////////////
  760. return FALSE;
  761. };
  762. }
  763. // @mfunc int | CThread | GetPriority |
  764. // This method gets the WINDOWS thread priority.
  765. // <nl><nl>GetPriority() just encapsulates the WINDOWS GetThreadPriority() function.
  766. // For more information see Win32 SDK Programmer抯 Reference.
  767. // @rdesc
  768. // Current WINDOWS thread priority or THREAD_PRIORITY_ERROR_RETURN if an error occurs.
  769. // Can be one of the following:
  770. // <nl><nl>THREAD_PRIORITY_NORMAL
  771. // <nl>THREAD_PRIORITY_ABOVE_NORMAL
  772. // <nl>THREAD_PRIORITY_BELOW_NORMAL
  773. // <nl>THREAD_PRIORITY_HIGHEST
  774. // <nl>THREAD_PRIORITY_IDLE
  775. // <nl>THREAD_PRIORITY_LOWEST
  776. // <nl>THREAD_PRIORITY_TIME_CRITICAL
  777. // @xref <mf CThread::SetPriority>
  778. int CThread::GetPriority() const
  779. {
  780. // Lock /////////////////////////////////////////////////////////////////
  781. s_InternalThreadSynchronization.InternalLock();
  782. /////////////////////////////////////////////////////////////////////////
  783. if (m_hThreadHandle != NULL)
  784. {
  785. int nPriority = ::GetThreadPriority(m_hThreadHandle);
  786. // Unlock ///////////////////////////////////////////////////////////
  787. s_InternalThreadSynchronization.InternalUnlock();
  788. /////////////////////////////////////////////////////////////////////
  789. return nPriority;
  790. }
  791. else
  792. {
  793. // Unlock ///////////////////////////////////////////////////////////
  794. s_InternalThreadSynchronization.InternalUnlock();
  795. /////////////////////////////////////////////////////////////////////
  796. return THREAD_PRIORITY_ERROR_RETURN;
  797. };
  798. }
  799. void CThread::StoreThreadExitCode()
  800. {
  801. if (m_hThreadHandle != NULL)
  802. ::GetExitCodeThread(m_hThreadHandle, &m_dwThreadExitCode);
  803. }
  804. // Get the current WINDOWS Thread exit code
  805. // @mfunc DWORD | CThread | GetExitCode | Gets CThread thread exit code -
  806. // value that is returned by <mf CThread::ThreadHandler> method - or, generally, it's the
  807. // return value of the thread controlling function.
  808. // <nl>GetExitCode() method returns the valid exit code regardless the WINDOWS
  809. // thread is alive or not.
  810. // @rdesc
  811. // CThread thread exit code.
  812. DWORD CThread::GetExitCode() const
  813. {
  814. // Lock /////////////////////////////////////////////////////////////////
  815. s_InternalThreadSynchronization.InternalLock();
  816. /////////////////////////////////////////////////////////////////////////
  817. DWORD dwThreadExitCode;
  818. if (m_hThreadHandle != NULL)
  819. ::GetExitCodeThread(m_hThreadHandle, &dwThreadExitCode);
  820. else
  821. dwThreadExitCode = m_dwThreadExitCode;
  822. // Unlock ///////////////////////////////////////////////////////////////
  823. s_InternalThreadSynchronization.InternalUnlock();
  824. /////////////////////////////////////////////////////////////////////////
  825. return dwThreadExitCode;
  826. }
  827. // Send the Thread Command (internal version)
  828. BOOL CThread::SendThreadCommand(int nCommand, CString& strError)
  829. {
  830. strError.Empty();
  831. if (m_bSupportSignalNotification &&
  832. m_hSignalObject != NULL &&
  833. nCommand != CThread::CMD_NONE)
  834. {
  835. if (m_nCommandList.GetSize() > 0 &&
  836. m_nCommandList.GetAt(m_nCommandList.GetSize() - 1) == (UINT)nCommand)
  837. return TRUE;
  838. m_nCommandList.Add((UINT)nCommand);
  839. if (!::SetEvent(m_hSignalObject))
  840. {
  841. strError = CThreadException::GetLastSystemErrorMsg(::GetLastError());
  842. return FALSE;
  843. }
  844. else
  845. return TRUE;
  846. }
  847. else
  848. return TRUE;
  849. }
  850. // Send the Thread Command (public version)
  851. // User-defined Command values in range: <CMD_USER_COMMAND - MAX_INT> (0 - 7 are reserved)
  852. // @mfunc void | CThread | SendCommand |
  853. // <tab><f throws CThreadException> of type:
  854. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  855. // <nl><nl>SendCommand() method is the base method with which the thread-owner process may manage
  856. // notificable CThread objects. By calling SendCommand() the thread-owner process fires desired commands
  857. // to the thread belonging to CThread object. The thread consequently receives a signal to leave <mf CThread::WaitForNotification>
  858. // method which is used in the <mf CThread::ThreadHandler> method (implemented in CThread-derived class) and restore the command fired
  859. // from the thread-owner process. Obtained command is immediately executed in the thread body.
  860. // Developer is responsible to handle the fired command in the <mf CThread::ThreadHandler> method.
  861. // SendCommand() mechanism is the recommended way how to manage CThread objects. It's also the base
  862. // concept for other command-oriented methods like Start(), Pause(), Continue(), Reset() or Stop().
  863. // Each of these methods sets the appropriate command and sends a signal to the thread
  864. // which in handles the command. Using this paradigm the thread notifications
  865. // coming from outside keep the synchronization features provided by CThread class.
  866. // <nl>SendCommand() method supports so-called 'stackable commands' which allows the thread-owner
  867. // process to pump all desired commands at one step. All such commands will be resolved
  868. // in the thread task body in exactly the same order as they were pumped in the owner process
  869. // (cyclic stack mechanism).
  870. // <nl>User may define an arbitrary count of user-specific commands. For this purpose the CThread
  871. // class offers the start offset command ID: CThread::CMD_USER_COMMAND. All command IDs below
  872. // this value is reserved by CThread class and should not be used. In this case
  873. // the user is responsible to handle the user-specific command in the <mf CThread::ThreadHandler> method in the
  874. // CThread-derived class.
  875. // <nl><nl>This method is valid only if CThread-derived class supports Thread Notification. Thread
  876. // Notification may be established by adding SUPPORT_THREAD_NOTIFICATION macro in the CThread-derived
  877. // class constructor in which <mf CThread::ThreadHandler> method is actually implemented.
  878. // Otherwise SendCommand() method has no any effect.
  879. // <nl>This method is usable after the CThread thread has been successfully started.
  880. // <nl><nl>SendCommand() returns immediately after the command has been actually fired.
  881. // The thread-owner process may, however, wait for completition of the task corresponding
  882. // to the fired command (implemented in the <mf CThread::ThreadHandler> method) by using
  883. // the paradigm described in the <mf CThread::WaitForActivityStatus> method.
  884. // <nl><nl>Detailed information how to use this method as well as how to manage fired commands in the
  885. // thread task body can be found in the 'Developer.doc' documentation.
  886. // @parm int | nCommand | [in] Desired command fired from within the thread-owner process
  887. // that is to be executed in CThread task body - <mf CThread::ThreadHandler>.
  888. // @xref <mf CThread::WaitForNotification>
  889. // <nl><mf CThread::WaitForActivityStatus>
  890. // <nl><t CThread Predefined Commands>
  891. void CThread::SendCommand(int nCommand)
  892. // throws CThreadException of type: CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  893. {
  894. // Lock /////////////////////////////////////////////////////////////////
  895. s_InternalThreadSynchronization.InternalLock();
  896. /////////////////////////////////////////////////////////////////////////
  897. CString strError;
  898. if (!SendThreadCommand(nCommand, strError))
  899. {
  900. CThreadException* pe = new CThreadException(this, strError, CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT);
  901. // Unlock ///////////////////////////////////////////////////////////
  902. s_InternalThreadSynchronization.InternalUnlock();
  903. /////////////////////////////////////////////////////////////////////
  904. throw pe;
  905. }
  906. else
  907. {
  908. // Unlock ///////////////////////////////////////////////////////////
  909. s_InternalThreadSynchronization.InternalUnlock();
  910. /////////////////////////////////////////////////////////////////////
  911. };
  912. }
  913. // Cancel all notification commands waiting in the process-queue
  914. // @mfunc void | CThread | ResetCommands | Cancels all thread-notification commands
  915. // fired by the <mf CThread::SendCommand> method and waiting in the queue to be processed.
  916. // <nl>This method is useful when the last important command is intended to be sent
  917. // to the CThread object. To ensure that no any other command is to be processed,
  918. // the thread-owner process may cancel all such commands pending in the CThread command
  919. // queue.
  920. // @xref <mf CThread::SendCommand>
  921. // <nl><mf CThread::WaitForNotification>
  922. void CThread::ResetCommands()
  923. {
  924. // Lock /////////////////////////////////////////////////////////////////
  925. s_InternalThreadSynchronization.InternalLock();
  926. /////////////////////////////////////////////////////////////////////////
  927. m_nCommandList.RemoveAll();
  928. m_nCommandList.FreeExtra();
  929. // Unlock ///////////////////////////////////////////////////////////////
  930. s_InternalThreadSynchronization.InternalUnlock();
  931. /////////////////////////////////////////////////////////////////////////
  932. }
  933. // User-defined Command values in range: 6 and higher
  934. BOOL CThread::ReceiveCommand(int& nIncomingCommand)
  935. {
  936. // Lock /////////////////////////////////////////////////////////////////
  937. s_InternalThreadSynchronization.InternalLock();
  938. /////////////////////////////////////////////////////////////////////////
  939. nIncomingCommand = CThread::CMD_NONE;
  940. if (m_bSupportSignalNotification && m_hSignalObject != NULL)
  941. {
  942. if (m_nCommandList.GetSize() > 0)
  943. {
  944. nIncomingCommand = (int)m_nCommandList.GetAt(0);
  945. m_nCommandList.RemoveAt(0);
  946. // Unlock ///////////////////////////////////////////////////////
  947. s_InternalThreadSynchronization.InternalUnlock();
  948. /////////////////////////////////////////////////////////////////
  949. return TRUE;
  950. }
  951. else
  952. {
  953. // Unlock ///////////////////////////////////////////////////////
  954. s_InternalThreadSynchronization.InternalUnlock();
  955. /////////////////////////////////////////////////////////////////
  956. return FALSE;
  957. };
  958. }
  959. else
  960. {
  961. // Unlock ///////////////////////////////////////////////////////////
  962. s_InternalThreadSynchronization.InternalUnlock();
  963. /////////////////////////////////////////////////////////////////////
  964. return FALSE;
  965. };
  966. }
  967. BOOL CThread::RegisterNotificationObject(CString& strError)
  968. {
  969. if (m_bSupportSignalNotification && m_hSignalObject == NULL)
  970. {
  971. if ((m_hSignalObject = ::CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
  972. {
  973. strError = CThreadException::GetLastSystemErrorMsg(::GetLastError());
  974. return FALSE;
  975. }
  976. else
  977. return TRUE;
  978. }
  979. else
  980. return TRUE;
  981. }
  982. void CThread::UnregisterNotificationObject()
  983. {
  984. if (m_bSupportSignalNotification && m_hSignalObject != NULL)
  985. {
  986. ::CloseHandle(m_hSignalObject);
  987. m_hSignalObject = NULL;
  988. };
  989. }
  990. // Wait until thread is notified or timeout interval (in miliseconds) elapses
  991. // @mfunc void | CThread | WaitForNotification | The main <mf CThread::ThreadHandler> notification
  992. // method. This method waits for incoming notifications (commands) fired from the
  993. // thread-owner process via <mf CThread::SendCommand> method and returns immediately after the command was received.
  994. // Returned command has to be handled in <mf CThread::ThreadHandler> method.
  995. // <nl>User may, however, set the timeout interval which forces the method to return after the timeout interval elapses.
  996. // <nl><nl>This method is valid only if CThread-derived class supports Thread Notification.
  997. // Thread Notification can be established by adding SUPPORT_THREAD_NOTIFICATION macro in CThread-derived
  998. // class constructor in which <mf CThread::ThreadHandler> method is actually implemented.
  999. // Otherwise it has no any effect and the method returns immediately.
  1000. // <nl>This method is usable after the CThread thread has been successfully started.
  1001. // <nl><nl>Detailed information how to use this method as well as how to manage fired commands in the
  1002. // thread task body can be found in the 'Developer.doc' documentation.
  1003. // @parm int& | nIncomingCommand | [out] Reference to the Command ID that was fired from within
  1004. // the thread-owner process.
  1005. // @parmopt DWORD | dwDefaultTimeout | CThread::DW_INFINITE | [in] Timeout interval in 'miliseconds'.
  1006. // @xref <mf CThread::SendCommand>
  1007. // <nl><t CThread Predefined Commands>
  1008. void CThread::WaitForNotification(int& nIncomingCommand, DWORD dwDefaultTimeout /*= CThread::DW_INFINITE*/)
  1009. {
  1010. if (m_bSupportSignalNotification && m_hSignalObject != NULL)
  1011. {
  1012. DWORD dwEvent;
  1013. BOOL bExit = FALSE;
  1014. do
  1015. {
  1016. if (!ReceiveCommand(nIncomingCommand))
  1017. {
  1018. dwEvent = ::WaitForSingleObject(m_hSignalObject, dwDefaultTimeout);
  1019. switch (dwEvent)
  1020. {
  1021. case WAIT_OBJECT_0:
  1022. break;
  1023. case WAIT_TIMEOUT:
  1024. nIncomingCommand = CThread::CMD_TIMEOUT_ELAPSED;
  1025. bExit = TRUE;
  1026. break;
  1027. default:
  1028. break;
  1029. };
  1030. }
  1031. else
  1032. break;
  1033. } while (!bExit);
  1034. }
  1035. else
  1036. nIncomingCommand = CThread::CMD_NONE;
  1037. }
  1038. // Run the current CThread thread.
  1039. // @mfunc void | CThread | Run |
  1040. // <tab><f throws CThreadException> of type:
  1041. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1042. // <nl><nl>Fires CThread::CMD_RUN Command and notifies CThread object.
  1043. // Used mainly in the thread-owner process. This method is valid only if CThread-derived class
  1044. // supports Thread Notification and CThread thread has been successfully started.
  1045. // Otherwise it has no any effect.
  1046. // <nl>This method is used in CThread-derived class - in the <mf CThread::ThreadHandler> method.
  1047. // To use the method properly the <mf CThread::ThreadHandler> method must be able to handle
  1048. // CThread::CMD_RUN Command as well as implement the specific task corresponding to this command.
  1049. // <nl>This method returns immediately after the command has been fired.
  1050. // The thread-owner process may, however, wait for completition of the corresponding task
  1051. // by using the paradigm described in the <mf CThread::WaitForActivityStatus> method.
  1052. // <nl><nl>For more information see 'Developer.doc' documentation.
  1053. // @xref <mf CThread::SendCommand>
  1054. // <nl><mf CThread::WaitForActivityStatus>
  1055. // <nl><t CThread Predefined Commands>
  1056. void CThread::Run()
  1057. // throws CThreadException of type: CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1058. {
  1059. SendCommand(CThread::CMD_RUN);
  1060. }
  1061. // Pause the current Thread (used in the CThread-derived Class - in the 'ThreadHandler' method)
  1062. // @mfunc void | CThread | Pause |
  1063. // <tab><f throws CThreadException> of type:
  1064. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1065. // <nl><nl>Fires CThread::CMD_PAUSE Command and notifies CThread object.
  1066. // Used mainly in the thread-owner process. This method is valid only if CThread-derived class
  1067. // supports Thread Notification and CThread thread has been successfully started.
  1068. // Otherwise it has no any effect.
  1069. // <nl>This method is used in CThread-derived class - in the <mf CThread::ThreadHandler> method.
  1070. // To use the method properly the <mf CThread::ThreadHandler> method must be able to handle
  1071. // CThread::CMD_PAUSE Command as well as implement the specific task corresponding to this command.
  1072. // <nl>This method returns immediately after the command has been fired.
  1073. // The thread-owner process may, however, wait for completition of the corresponding task
  1074. // by using the paradigm described in the <mf CThread::WaitForActivityStatus> method.
  1075. // <nl><nl>For more information see 'Developer.doc' documentation.
  1076. // @xref <mf CThread::SendCommand>
  1077. // <nl><mf CThread::WaitForActivityStatus>
  1078. // <nl><t CThread Predefined Commands>
  1079. void CThread::Pause()
  1080. // throws CThreadException of type: CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1081. {
  1082. SendCommand(CThread::CMD_PAUSE);
  1083. }
  1084. // Pause the current Thread (used in the CThread-derived Class - in the 'ThreadHandler' method)
  1085. // @mfunc void | CThread | Continue |
  1086. // <tab><f throws CThreadException> of type:
  1087. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1088. // <nl><nl>Fires CThread::CMD_CONTINUE Command and notifies CThread object.
  1089. // Used mainly in the thread-owner process. This method is valid only if CThread-derived class
  1090. // supports Thread Notification and CThread thread has been successfully started.
  1091. // Otherwise it has no any effect.
  1092. // <nl>This method is used in CThread-derived class - in the <mf CThread::ThreadHandler> method.
  1093. // To use the method properly the <mf CThread::ThreadHandler> method must be able to handle
  1094. // CThread::CMD_CONTINUE Command as well as implement the specific task corresponding to this command.
  1095. // <nl>This method returns immediately after the command has been fired.
  1096. // The thread-owner process may, however, wait for completition of the corresponding task
  1097. // by using the paradigm described in the <mf CThread::WaitForActivityStatus> method.
  1098. // <nl><nl>For more information see 'Developer.doc' documentation.
  1099. // @xref <mf CThread::SendCommand>
  1100. // <nl><mf CThread::WaitForActivityStatus>
  1101. // <nl><t CThread Predefined Commands>
  1102. void CThread::Continue()
  1103. // throws CThreadException of type: CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1104. {
  1105. SendCommand(CThread::CMD_CONTINUE);
  1106. }
  1107. // Reset the current Thread (used in the CThread-derived Class - in the 'ThreadHandler' method)
  1108. // @mfunc void | CThread | Reset |
  1109. // <tab><f throws CThreadException> of type:
  1110. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1111. // <nl><nl>Fires CThread::CMD_RESET Command and notifies CThread object.
  1112. // Used mainly in the thread-owner process. This method is valid only if CThread-derived class
  1113. // supports Thread Notification and CThread thread has been successfully started.
  1114. // Otherwise it has no any effect.
  1115. // <nl>This method is used in CThread-derived class - in the <mf CThread::ThreadHandler> method.
  1116. // To use the method properly the <mf CThread::ThreadHandler> method must be able to handle
  1117. // CThread::CMD_RESET Command as well as implement the specific task corresponding to this command.
  1118. // <nl>This method returns immediately after the command has been fired.
  1119. // The thread-owner process may, however, wait for completition of the corresponding task
  1120. // by using the paradigm described in the <mf CThread::WaitForActivityStatus> method.
  1121. // <nl><nl>For more information see 'Developer.doc' documentation.
  1122. // @xref <mf CThread::SendCommand>
  1123. // <nl><mf CThread::WaitForActivityStatus>
  1124. // <nl><t CThread Predefined Commands>
  1125. void CThread::Reset()
  1126. // throws CThreadException of type: CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1127. {
  1128. SendCommand(CThread::CMD_RESET);
  1129. }
  1130. // Check if the current Thread is still alive (internal version)
  1131. BOOL CThread::IsThreadAlive()
  1132. {
  1133. if (m_hThreadHandle == NULL) return FALSE;
  1134. ::GetExitCodeThread(m_hThreadHandle, &m_dwThreadExitCode);
  1135. if (m_dwThreadExitCode == STILL_ACTIVE)
  1136. return TRUE;
  1137. else
  1138. {
  1139. ::CloseHandle(m_hThreadHandle);
  1140. m_hThreadHandle = NULL;
  1141. return FALSE;
  1142. };
  1143. }
  1144. // Check if the current Thread is still alive (public version)
  1145. // @mfunc BOOL | CThread | IsAlive | Checks if the current CThread thread is still alive.
  1146. // @rdesc
  1147. // TRUE if the thread is alive otherwise FALSE.
  1148. BOOL CThread::IsAlive()
  1149. {
  1150. // Lock /////////////////////////////////////////////////////////////////
  1151. s_InternalThreadSynchronization.InternalLock();
  1152. /////////////////////////////////////////////////////////////////////////
  1153. if (IsThreadAlive())
  1154. {
  1155. // Unlock ///////////////////////////////////////////////////////////
  1156. s_InternalThreadSynchronization.InternalUnlock();
  1157. /////////////////////////////////////////////////////////////////////
  1158. return TRUE;
  1159. }
  1160. else
  1161. {
  1162. // Unlock ///////////////////////////////////////////////////////////
  1163. s_InternalThreadSynchronization.InternalUnlock();
  1164. /////////////////////////////////////////////////////////////////////
  1165. return FALSE;
  1166. };
  1167. }
  1168. /////////////////////////////////////////////////////////////////////////////
  1169. // Just for AutoDuck purposes...
  1170. /////////////////////////////////////////////////////////////////////////////
  1171. // @mfunc DWORD | CThread | ThreadHandler |
  1172. // Main CThread-handler virtual abstract method declaration.
  1173. // <nl><nl>This method is not implemented in the CThread class and must be implemented
  1174. // at least at one CThread-derived class in the CThread class hierarchy.
  1175. // <nl>ThreadHandler() method concentrates the whole task that CThread thread
  1176. // has to execute. The implementation itself varies according to the type of
  1177. // CThread thread operating on this method (Trivial or Notificable Threads).
  1178. // <nl><nl>Detailed information as well as the examples how to implement, manage and use
  1179. // this method is written in the 'Developer.doc' documentation in 'Doc' directory.
  1180. // @rdesc
  1181. // Thread task completition exit code.
  1182. // @xref <mf CThread::SendCommand>
  1183. // <nl><mf CThread::WaitForNotification>
  1184. // DWORD CThread::ThreadHandler(){}
  1185. // Main Thread Handler function /////////////////////////////////////////////
  1186. DWORD WINAPI CThread::ControllingFunction(LPVOID lpParameter)
  1187. {
  1188. DWORD dwExitCode = ((CThread *)lpParameter)->ThreadHandler();
  1189. ((CThread *)lpParameter)->ResetThreadObject(dwExitCode);
  1190. return dwExitCode;
  1191. }
  1192. /////////////////////////////////////////////////////////////////////////////
  1193. // Start the current Thread
  1194. // @mfunc void | CThread | Start |
  1195. // <tab><f throws CThreadException> of types:
  1196. // <nl><tab>CThreadException::CANNOT_CREATE_THREAD,
  1197. // <nl><tab>CThreadException::THREAD_ALREADY_STARTED,
  1198. // <nl><tab>CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT,
  1199. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1200. // <nl><nl>This method starts the WINDOWS thread connected to CThread object.
  1201. // Used in the thread-owner process. Once established the WINDOWS thread cannot be started
  1202. // again until it terminates.
  1203. // <nl>Before starting a thread user may set up WINDOWS thread attributes or thread stack size
  1204. // if necessary. This can be accomplished by calling the <mf CThread::SetAttributes> or
  1205. // <mf CThread::SetStackSize> methods.
  1206. // <nl>In notificable threads Start() method fires CThread::CMD_INITIALIZE Command
  1207. // immediately after the WINDOWS thread was being established. CThread::CMD_INITIALIZE Command must
  1208. // be handled in the <mf CThread::ThreadHandler> method.
  1209. // <nl><nl>This method is a synchronous method. It returns only if the WINDOWS thread has been
  1210. // successfully started or some significant error occurs. In this case the method returns
  1211. // immediately by throwing the appropriate CThreadException exception.
  1212. // @xref <mf CThread::Stop>
  1213. // <nl><mf CThread::SetAttributes>
  1214. // <nl><mf CThread::SetStackSize>
  1215. // <nl><mf CThread::SendCommand>
  1216. // <nl><mf CThread::WaitForNotification>
  1217. void CThread::Start()
  1218. // throws CThreadException of types: CThreadException::CANNOT_CREATE_THREAD,
  1219. //   CThreadException::THREAD_ALREADY_STARTED,
  1220. //   CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT,
  1221. //   CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1222. {
  1223. // Lock /////////////////////////////////////////////////////////////////
  1224. s_InternalThreadSynchronization.InternalLock();
  1225. /////////////////////////////////////////////////////////////////////////
  1226. if (!IsThreadAlive())
  1227. {
  1228. CString strError;
  1229. ResetThreadRuntimeData();
  1230. if (!RegisterNotificationObject(strError))
  1231. {
  1232. CThreadException* pe = new CThreadException(this, strError, CThreadException::CANNOT_CREATE_NOTIFICATION_OBJECT);
  1233. // Unlock ///////////////////////////////////////////////////////
  1234. s_InternalThreadSynchronization.InternalUnlock();
  1235. /////////////////////////////////////////////////////////////////
  1236. throw pe;
  1237. };
  1238. RegisterRuntimeSynchronization();
  1239. m_hThreadHandle = ::CreateThread(m_lpThreadAttributes,
  1240.  m_dwStackSize,
  1241.  &ControllingFunction,
  1242.  this,
  1243.  0,
  1244.  &m_dwThreadID);
  1245. if (m_hThreadHandle != NULL)
  1246. {
  1247. SetActivityStatus(CThread::THREAD_RUNNING);
  1248. if (!SendThreadCommand(CThread::CMD_INITIALIZE, strError))
  1249. {
  1250. CThreadException* pe = new CThreadException(this, strError, CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT);
  1251. // Unlock ///////////////////////////////////////////////////////
  1252. s_InternalThreadSynchronization.InternalUnlock();
  1253. /////////////////////////////////////////////////////////////////
  1254. throw pe;
  1255. }
  1256. else
  1257. {
  1258. // Unlock ///////////////////////////////////////////////////////
  1259. s_InternalThreadSynchronization.InternalUnlock();
  1260. /////////////////////////////////////////////////////////////////
  1261. return;
  1262. };
  1263. }
  1264. else
  1265. {
  1266. CThreadException* pe = new CThreadException(this, CThreadException::GetLastSystemErrorMsg(::GetLastError()), CThreadException::CANNOT_CREATE_THREAD);
  1267. // Unlock ///////////////////////////////////////////////////////
  1268. s_InternalThreadSynchronization.InternalUnlock();
  1269. /////////////////////////////////////////////////////////////////
  1270. throw pe;
  1271. };
  1272. }
  1273. else
  1274. {
  1275. CThreadException* pe = new CThreadException(this, _T(""), CThreadException::THREAD_ALREADY_STARTED);
  1276. // Unlock ///////////////////////////////////////////////////////////
  1277. s_InternalThreadSynchronization.InternalUnlock();
  1278. /////////////////////////////////////////////////////////////////////
  1279. throw pe;
  1280. };
  1281. }
  1282. // Stop the current running Thread
  1283. // Parameter: dwTimeout - in 'Seconds'
  1284. // @mfunc BOOL | CThread | Stop |
  1285. // <tab><f throws CThreadException> of type:
  1286. // <nl><tab>CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1287. // <nl><nl>This method stops the WINDOWS running thread connected to CThread object.
  1288. // Used in the thread-owner process.
  1289. // In notificable threads Stop() method fires CThread::CMD_STOP Command
  1290. // immediately after the WINDOWS thread was asked to be stopped. CThread::CMD_STOP Command must
  1291. // be handled in <mf CThread::ThreadHandler> method to properly terminates the thread.
  1292. // If CThread object does not support Thread Notification Stop() will wait until
  1293. // the WINDOWS thread actually terminates, this method is, however, intended to use in
  1294. // the notification mode.
  1295. // In both modes the method returns only in the case when the WINDOWS thread actually terminates or
  1296. // the timeout interval elapses or some significant error occurs. Otherwise it waits (synchronous stopping).
  1297. // When an immediate return is required the user may set 'dwTimeout' variable to zero.
  1298. // In such case, however, the user is responsible to find out if the thread actually runs or is already stopped.
  1299. // @parm DWORD& | dwExitCode | [out] WINDOWS thread exit code.
  1300. // @parmopt DWORD | dwTimeout | CThread::DW_INFINITE | [in] Timeout interval in tenths of second.
  1301. // @rdesc Stop return status
  1302. // @flag TRUE | WINDOWS thread was properly stopped.
  1303. // @flag FALSE | WINDOWS thread was not stopped after the timeout interval elapsed.
  1304. // @xref <mf CThread::Start>
  1305. // <nl><mf CThread::SendCommand>
  1306. // <nl><mf CThread::WaitForNotification>
  1307. BOOL CThread::Stop(DWORD& dwExitCode, DWORD dwTimeout /*= CThread::DW_INFINITE*/)
  1308. // throws CThreadException of type: CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT
  1309. {
  1310. // Lock /////////////////////////////////////////////////////////////////
  1311. s_InternalThreadSynchronization.InternalLock();
  1312. /////////////////////////////////////////////////////////////////////////
  1313. if (IsThreadAlive())
  1314. {
  1315. CString strError;
  1316. int nAttemption = 0, nMaxAttemptions;
  1317. nMaxAttemptions = (dwTimeout != (DWORD)CThread::DW_INFINITE) ? ((int)dwTimeout * 100)/CThread::BASE_DELAY_INTERVAL : -1;
  1318. if (!SendThreadCommand(CThread::CMD_STOP, strError))
  1319. {
  1320. dwExitCode = (DWORD)CThread::DW_ERROR;
  1321. m_dwThreadExitCode = dwExitCode;
  1322. CThreadException* pe = new CThreadException(this, strError, CThreadException::CANNOT_SIGNAL_NOTIFICATION_OBJECT);
  1323. // Unlock ///////////////////////////////////////////////////////
  1324. s_InternalThreadSynchronization.InternalUnlock();
  1325. /////////////////////////////////////////////////////////////////
  1326. throw pe;
  1327. }
  1328. else
  1329. {
  1330. // Unlock ///////////////////////////////////////////////////////
  1331. s_InternalThreadSynchronization.InternalUnlock();
  1332. /////////////////////////////////////////////////////////////////
  1333. };
  1334. while (IsAlive() && (nMaxAttemptions == -1 || nAttemption++ < nMaxAttemptions))
  1335. {
  1336. ::Sleep(CThread::BASE_DELAY_INTERVAL);
  1337. };
  1338. // Lock /////////////////////////////////////////////////////////////
  1339. s_InternalThreadSynchronization.InternalLock();
  1340. /////////////////////////////////////////////////////////////////////
  1341. if (m_dwThreadExitCode != STILL_ACTIVE)
  1342. {
  1343. dwExitCode = m_dwThreadExitCode;
  1344. // Unlock ///////////////////////////////////////////////////////
  1345. s_InternalThreadSynchronization.InternalUnlock();
  1346. /////////////////////////////////////////////////////////////////
  1347. return TRUE;
  1348. }
  1349. else
  1350. {
  1351. dwExitCode = (DWORD)CThread::DW_TIMEOUT_ELAPSED;
  1352. // Unlock ///////////////////////////////////////////////////////
  1353. s_InternalThreadSynchronization.InternalUnlock();
  1354. /////////////////////////////////////////////////////////////////
  1355. return FALSE;
  1356. };
  1357. }
  1358. else
  1359. {
  1360. dwExitCode = m_dwThreadExitCode;
  1361. ResetThreadRuntimeData();
  1362. // Unlock ///////////////////////////////////////////////////////////
  1363. s_InternalThreadSynchronization.InternalUnlock();
  1364. /////////////////////////////////////////////////////////////////////
  1365. return TRUE;
  1366. };
  1367. }
  1368. // @mfunc void | CThread | OnKill |
  1369. // Virtual method that is invoked while the <mf CThread::Kill> method is being executed.
  1370. // OnKill() is called after the WINDOWS thread has been actually destroyed.
  1371. // Users may unallocate all extra resources that were needed in the CThread-derived instance object.
  1372. // <nl>Default version does nothing users may, however, implement this method in their CThread-derived class.
  1373. // <nl>All extra resourced should be referenced through CThread-derived class member data,
  1374. // because in the moment of OnKill() execution all local variables from the <mf CThread::ThreadHandler> method
  1375. // are not more valid - thread is already destroyed and its controlling function is detached.
  1376. // <nl>OnKill() virtual method doesn't have to call the base OnKill() method.
  1377. // <nl><nl>Note 1:
  1378. // <nl>Users should unallocate (detach, close...) resources that rely only to the
  1379. // current instance of CThread-derived object that is being killed. Keep in mind that in some cases
  1380. // the same resources may be used by other running instances as well. In this situation the
  1381. // user is responsible to clean up such shared resources.
  1382. // <nl><nl>Note 2:
  1383. // <nl>OnKill() method doesn't have to be invoked under any circumstances. If the thread
  1384. // regularly finished before the <mf CThread::Kill> method has been called the <mf CThread::Kill>
  1385. // method does nothing, thus, OnKill() method is not invoked.
  1386. // OnKill() is normally used as a last chance emergency cleaning up.
  1387. // @xref <mf CThread::Kill>
  1388. // <nl><mf CThread::ThreadHandler>
  1389. void CThread::OnKill()
  1390. {
  1391. // Does nothing by default //////////////////////////////////////////////
  1392. }
  1393. // Kill the current running Thread (used in emergency cases only!)
  1394. // @mfunc void | CThread | Kill |
  1395. // <tab><f throws CThreadException> of type:
  1396. // <nl><tab>CThreadException::CANNOT_TERMINATE_THREAD
  1397. // <nl><nl>This method kills the WINDOWS thread connected to CThread object.
  1398. // Used in the thread-owner process.
  1399. // The method returns after the WINDOWS thread has been actually killed, or some significant error occurs.
  1400. // <nl>CThread threads should always terminate either by leaving the thread-controlling function attached
  1401. // to the WINDOWS thread or by calling <mf CThread::Stop> method in notificable threads.
  1402. // <nl>Killing threads is the last-chance stopping mechanism and should be used in emergency
  1403. // cases only. While killing the thread by using this method the <mf CThread::OnKill> virtual method
  1404. // is invoked to provide the last chance for making necessary unallocations specific to the
  1405. // currently killed thread.
  1406. // <nl><nl>For more information see 'Terminating WINDOWS Threads' in Microsoft MSDN documentation.
  1407. // @parmopt DWORD | dwExitCode | CThread::DW_OK | [in] Suggested WINDOWS thread exit code.
  1408. // (see 'TerminateThread()' WINDOWS function in Microsoft MSDN documentation).
  1409. // @xref <mf CThread::OnKill>
  1410. // <nl><mf CThread::Stop>
  1411. void CThread::Kill(DWORD dwExitCode /*= CThread::DW_OK*/)
  1412. // throws CThreadException of type: CThreadException::CANNOT_TERMINATE_THREAD
  1413. {
  1414. // Lock /////////////////////////////////////////////////////////////////
  1415. s_InternalThreadSynchronization.InternalLock();
  1416. /////////////////////////////////////////////////////////////////////////
  1417. if (IsThreadAlive())
  1418. {
  1419. DWORD dwError = (DWORD)CThread::DW_OK;
  1420. BOOL bIsTerminated = TRUE;
  1421. if (m_hThreadHandle != NULL) bIsTerminated = ::TerminateThread(m_hThreadHandle, dwExitCode);
  1422. if (!bIsTerminated) dwError = ::GetLastError();
  1423. StoreThreadExitCode();
  1424. if (m_hThreadHandle != NULL) ::CloseHandle(m_hThreadHandle);
  1425. UnregisterRuntimeSynchronization();
  1426. ResetThreadRuntimeData();
  1427. if (!bIsTerminated)
  1428. {
  1429. CThreadException* pe = new CThreadException(this, CThreadException::GetLastSystemErrorMsg(dwError), CThreadException::CANNOT_TERMINATE_THREAD);
  1430. // Unlock ///////////////////////////////////////////////////////
  1431. s_InternalThreadSynchronization.InternalUnlock();
  1432. /////////////////////////////////////////////////////////////////
  1433. throw pe;
  1434. }
  1435. else
  1436. {
  1437. // Unlock ///////////////////////////////////////////////////////
  1438. s_InternalThreadSynchronization.InternalUnlock();
  1439. /////////////////////////////////////////////////////////////////
  1440. };
  1441. // Do necessary cleaning up after the thread has been destroyed /////
  1442. OnKill();
  1443. }
  1444. else
  1445. {
  1446. ResetThreadRuntimeData();
  1447. // Unlock ///////////////////////////////////////////////////////////
  1448. s_InternalThreadSynchronization.InternalUnlock();
  1449. /////////////////////////////////////////////////////////////////////
  1450. };
  1451. }