Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

567 lines
16 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. Worker.hxx
  5. Abstract:
  6. Declaration of CVssWorkerThread
  7. Adi Oltean [aoltean] 10/10/1999
  8. Revision History:
  9. Name Date Comments
  10. aoltean 10/10/1999 Created
  11. aoltean 11/02/1999 Adding asserts and traces.
  12. Removing TerminateThread.
  13. --*/
  14. #ifndef __VSS_WORKER_HXX__
  15. #define __VSS_WORKER_HXX__
  16. #if _MSC_VER > 1000
  17. #pragma once
  18. #endif
  19. /////////////////////////////////////////////////////////////////////////////
  20. // Includes
  21. #include "vssmsg.h"
  22. ////////////////////////////////////////////////////////////////////////
  23. // Standard foo for file name aliasing. This code block must be after
  24. // all includes of VSS header files.
  25. //
  26. #ifdef VSS_FILE_ALIAS
  27. #undef VSS_FILE_ALIAS
  28. #endif
  29. #define VSS_FILE_ALIAS "INCWORKH"
  30. //
  31. ////////////////////////////////////////////////////////////////////////
  32. /////////////////////////////////////////////////////////////////////////////
  33. // CVssWorkerThread
  34. /*
  35. 1) This abstract class is used to implement a generic worker thread.
  36. The derived class CMyJob must be in the form:
  37. class CMyJob: CVssWorkerThread
  38. {
  39. // Destructor
  40. public:
  41. ~CMyJob() {
  42. FinalReleaseWorkerThreadObject();
  43. };
  44. // Ovverides
  45. protected:
  46. // called after thread creation but before thread starting.
  47. // Called in the creator's thread.
  48. bool OnInit();
  49. {
  50. // Initialize some internal variables
  51. return true;
  52. };
  53. // called in the background thread
  54. void OnRun()
  55. {
  56. // Check if the user wants to terminate the thread
  57. while (!MustBeTerminated()) {
  58. // Do some lengthly task
  59. ...
  60. }
  61. };
  62. // called after finishing the thread procedure.
  63. // in the same background thread as the OnRun
  64. void OnFinish()
  65. {
  66. // Normal uninitialization
  67. ...
  68. // Signal the job object as finished (mandatory call here)
  69. MarkAsFinished();
  70. // You can do auto-destroy here
  71. ..
  72. }
  73. void CVssAsync::OnTerminate()
  74. {
  75. // Called on forced termination
  76. }
  77. };
  78. 2) Generic usage (an example)
  79. HRESULT hr = S_OK;
  80. CMyJob* pJob = new CMyJob;
  81. if (pJob == NULL)
  82. hr = E_OUTOFMEMORY;
  83. if (SUCEEDED(hr))
  84. hr = pJob->PrepareJob(); // Create the background thread in SUSPENDED state
  85. if (SUCEEDED(hr))
  86. hr = pJob->StartJob(); // Run the background thread in SUSPENDED state
  87. if (SUCCEEDED(hr))
  88. if (!::WaitForSingleObject(pJob->GetThreadID()))
  89. hr = HRESULT_FROM_WIN32(GetLastError());
  90. delete pJob;
  91. WARNINGS:
  92. 1) DO NOT ALLOCATE A CVssWorkerThread OBJECT ON THE STACK!
  93. 2) The caller is responsible to call FinalReleaseWorkerThreadObject before destroying the object!
  94. 3) DO NOT destroy the job object before job termination! (i.e. before MarkOnFinish was called in OnFinish)
  95. */
  96. class CVssWorkerThread
  97. {
  98. typedef unsigned ( __stdcall *JobFunction )( void * );
  99. public:
  100. // The thread possible states
  101. typedef enum _VSS_ENUM_THREAD
  102. {
  103. VSS_THREAD_UNKNOWN = 0, // Invalid state
  104. VSS_THREAD_INIT, // Worker object constructed but PrepareJob not called yet.
  105. VSS_THREAD_PREPARED, // PrepareJob called but StartJob not called yet
  106. VSS_THREAD_RUNNING, // StartJob called but hte background thread not yet finished.
  107. VSS_THREAD_FINISHED, // Background thread finished.
  108. VSS_THREAD_ERROR // An error was encountered in one of the phases above.
  109. } VSS_ENUM_THREAD;
  110. // Constructors& destructors
  111. private:
  112. // disable copy constructor
  113. CVssWorkerThread(const CVssWorkerThread&);
  114. public:
  115. // default constructor
  116. CVssWorkerThread():
  117. m_hThread(NULL),
  118. m_eThreadState(VSS_THREAD_INIT),
  119. m_nThreadID(0),
  120. m_bInitSucceeded(false),
  121. m_bTerminateNow(false),
  122. m_bStarted(false)
  123. {};
  124. protected:
  125. // destructor
  126. ~CVssWorkerThread()
  127. {
  128. if (m_hThread)
  129. {
  130. BS_ASSERT( (m_eThreadState == VSS_THREAD_INIT)
  131. || (m_eThreadState == VSS_THREAD_FINISHED)
  132. || (m_eThreadState == VSS_THREAD_ERROR));
  133. // close the handle to the thread since no-one is keeping it.
  134. ::CloseHandle(m_hThread);
  135. m_hThread = NULL;
  136. }
  137. }
  138. // Opperations - to be called only from the derived class
  139. protected:
  140. void MarkAsFinished()
  141. /*++
  142. This method marks the successful thread termination
  143. Called only by OnFinish method to notify that the thread succesfully terminates
  144. This must not be called when the OnFinish reason is "TERMINATE"
  145. It is illegal to mark a error-state thread as finished.
  146. --*/
  147. {
  148. if (m_eThreadState == VSS_THREAD_RUNNING)
  149. m_eThreadState = VSS_THREAD_FINISHED;
  150. else
  151. BS_ASSERT(false);
  152. }
  153. void FinalReleaseWorkerThreadObject()
  154. /*++
  155. This method kills the NT background thread, if still running
  156. Must be called as a last method for killing the thread,
  157. For example it is called in the destructor of the final derived class.
  158. It might be called from any thread exceptig the running BACKGROUND thread.
  159. WARNING: This function calls virtual function. DO NOT call this function
  160. in the destructor of an instance which is not the final derived class, otherwise
  161. only the virtual functions of that class or the base classes will be called.
  162. --*/
  163. {
  164. CVssFunctionTracer ft( VSSDBG_COORD, L"CVssWorkerThread::FinalReleaseWorkerThreadObject" );
  165. try
  166. {
  167. // Wait until the background thread is terminated.
  168. switch(m_eThreadState)
  169. {
  170. case VSS_THREAD_PREPARED:
  171. Terminate(true);
  172. break;
  173. case VSS_THREAD_RUNNING:
  174. Terminate();
  175. break;
  176. case VSS_THREAD_INIT:
  177. case VSS_THREAD_FINISHED:
  178. case VSS_THREAD_ERROR:
  179. break;
  180. default:
  181. ft.Trace( VSSDBG_GEN, L"Bad state %d", m_eThreadState);
  182. BS_ASSERT(false);
  183. break;
  184. }
  185. }
  186. VSS_STANDARD_CATCH(ft)
  187. }
  188. // Main operations - called from the clients of the derived class.
  189. public:
  190. HRESULT PrepareJob()
  191. /*++
  192. Description:
  193. This method prepares the NT background thread for running but it
  194. doesn't start it.
  195. Called by:
  196. - Must be called in order to prepare the background thread. This will
  197. call internally the OnInit method in the CLIENT thread.
  198. - The next method to call is StartJob, to fire up the created thread.
  199. Calling thread:
  200. - The CLIENT thread
  201. What to do on failures:
  202. - Do not call any other methods. Just delete the object instance.
  203. - Beware that the internal state is VSS_THREAD_ERROR now.
  204. Return values:
  205. E_OUTOFMEMORY
  206. - _beginthreadex failed
  207. E_UNEXPECTED
  208. - Function called in improper state: programming error
  209. --*/
  210. {
  211. CVssFunctionTracer ft( VSSDBG_COORD, L"CVssWorkerThread::PrepareJob" );
  212. // Test for valid state
  213. if ((m_hThread != NULL) || (m_eThreadState != VSS_THREAD_INIT))
  214. {
  215. ft.Trace( VSSDBG_GEN, L"Bad state %d", m_eThreadState);
  216. BS_ASSERT(false); // programming error
  217. return E_UNEXPECTED;
  218. }
  219. // Start the expiration thread
  220. uintptr_t nResult = ::_beginthreadex(
  221. NULL, // Security descriptor
  222. 0, // Stack size
  223. reinterpret_cast<JobFunction>
  224. (CVssWorkerThread::_ThreadFunction), // Start address
  225. reinterpret_cast<void*>(this), // Arg list for the new thread.
  226. CREATE_SUSPENDED, // Initflag
  227. &m_nThreadID // Will receive the thread ID
  228. );
  229. // BUG in _beginthreadex - sometimes it returns zero under memory pressure.
  230. // Treat the case of a NULL returned handle
  231. if (nResult == 0) {
  232. ft.Trace( VSSDBG_GEN, L"_beginthreadex failed. errno = 0x%08lx", errno);
  233. ft.LogError( VSS_ERROR_THREAD_CREATION, VSSDBG_GEN << (HRESULT) nResult << (HRESULT) (errno) );
  234. return E_OUTOFMEMORY; // Probably invalid arguments in _beginthreadex...
  235. }
  236. // Error treatment
  237. if (nResult == (uintptr_t)(-1))
  238. {
  239. // Interpret the error code.
  240. if (errno == EAGAIN)
  241. return E_OUTOFMEMORY; // Try again later...
  242. else
  243. {
  244. ft.Trace( VSSDBG_GEN, L"_beginthreadex failed. errno = 0x%08lx", errno);
  245. ft.LogError( VSS_ERROR_THREAD_CREATION, VSSDBG_GEN << (HRESULT) nResult << (HRESULT) (errno) );
  246. return E_OUTOFMEMORY; // Probably invalid arguments in _beginthreadex...
  247. }
  248. }
  249. // Get the thread handle
  250. m_hThread = (HANDLE) nResult;
  251. // Call OnInit on the base class.
  252. m_bInitSucceeded = OnInit(); // Will be used in ThreadFunction in the resumed thread.
  253. m_eThreadState = VSS_THREAD_PREPARED;
  254. return S_OK;
  255. }
  256. HRESULT StartJob()
  257. /*++
  258. Description:
  259. This method starts the prepared NT background thread .
  260. Called by:
  261. - This will call internally the OnInit method in the BACKGROUND thread.
  262. Calling thread:
  263. - The CLIENT thread
  264. What to do on failures:
  265. - Do not call any other methods. Just delete the object instance.
  266. - Beware that the internal state is VSS_THREAD_ERROR now.
  267. Return values:
  268. E_OUTOFMEMORY
  269. - ResumeThread failed
  270. E_UNEXPECTED
  271. - Function called in improper state: programming error
  272. --*/
  273. {
  274. CVssFunctionTracer ft( VSSDBG_COORD, L"CVssWorkerThread::StartJob" );
  275. // Test for valid state
  276. if ((m_hThread == NULL) || (m_eThreadState != VSS_THREAD_PREPARED))
  277. {
  278. ft.Trace( VSSDBG_GEN, L"Bad state %d", m_eThreadState);
  279. BS_ASSERT(false); // programming error
  280. return E_UNEXPECTED;
  281. }
  282. // Resume the thread. Here ThreadFunction will be called on the resumed thread.
  283. DWORD dwResult = ::ResumeThread( m_hThread );
  284. // Check if the thread was in suspended state.
  285. BS_ASSERT(dwResult != 0);
  286. // Check for error
  287. if (dwResult == 0xFFFFFFFF)
  288. {
  289. ft.LogGenericWarning( VSSDBG_GEN,
  290. L"ResumeThread(%p) = -1, GetLastError() == 0x%08lx, m_eThreadState == %d",
  291. m_hThread, GetLastError(), (INT)m_eThreadState );
  292. ft.Trace( VSSDBG_GEN,
  293. L"ResumeThread failed. Error: 0x%08lx. State: %d",
  294. GetLastError(), m_eThreadState);
  295. // Reset the object state
  296. m_eThreadState = VSS_THREAD_ERROR;
  297. return E_OUTOFMEMORY; // Error resuming the thread
  298. }
  299. m_bStarted = true;
  300. return S_OK;
  301. }
  302. // Attributes
  303. public:
  304. // Get the current thread state
  305. VSS_ENUM_THREAD GetThreadState() const { return m_eThreadState; };
  306. // Get the current thread's handle
  307. HANDLE GetThreadHandle() const { return m_hThread; };
  308. // Get the current thread's ID
  309. UINT GetThreadID() const { return m_nThreadID; };
  310. // Check if the thread was marked for Termination
  311. // Called from the OnRun implementation
  312. bool MustBeTerminated() const { return m_bTerminateNow; };
  313. // Shortcut for checking if the worker thread is resumed.
  314. // Must be called from the same thread as StartJob.
  315. bool IsStarted() const { return m_bStarted; };
  316. // Ovverides - these must be defined in the derived class
  317. protected:
  318. // Entry point called after thread creation but before thread starting.
  319. // The caller thread is the CLIENT thread
  320. virtual bool OnInit() = 0;
  321. // Entry point for the thread procedure.
  322. // The caller thread is the BACKGROUND thread
  323. virtual void OnRun() = 0;
  324. // Entry point called after finishing hte thread procedure.
  325. // The caller thread is the BACKGROUND thread
  326. // WARNING: this function may RELEASE the worker thread object!!!
  327. virtual void OnFinish() = 0;
  328. // Entry point called when the thread will be terminated
  329. // The caller thread is the CLIENT thread
  330. // The derived class implementation can use this to signal to the
  331. // running thread a termination event
  332. virtual void OnTerminate() = 0;
  333. // Internal operations
  334. private:
  335. // The thread function
  336. static unsigned __stdcall _ThreadFunction(LPVOID* ptrArg)
  337. {
  338. CVssWorkerThread* pObj = reinterpret_cast<CVssWorkerThread*>( ptrArg );
  339. pObj->ThreadFunction();
  340. return 0;
  341. }
  342. // The C++ version of the thread function
  343. void ThreadFunction()
  344. {
  345. CVssFunctionTracer ft( VSSDBG_COORD, L"CVssWorkerThread::ThreadFunction" );
  346. m_eThreadState = VSS_THREAD_RUNNING;
  347. // Run the job, if OnInit succeeded
  348. if (m_bInitSucceeded && !m_bTerminateNow)
  349. OnRun();
  350. // Finish the job, regardless of the fact that OnRun was caleld or not.
  351. OnFinish();
  352. }
  353. HRESULT Terminate(
  354. IN bool bThreadPreparedOnly = false
  355. )
  356. /*++
  357. Description:
  358. This method terminates prepared NT background thread.
  359. It does NOT call the TerminateThread win32 API.
  360. Called by:
  361. FinalReleaseWorkerThreadObject()
  362. Calling thread:
  363. The CLIENT thread
  364. What to do on failures:
  365. - Do not call any other methods. Just delete the object instance.
  366. - Beware that the internal state is VSS_THREAD_ERROR now.
  367. --*/
  368. // Does not call OnFinish... unless something wrong happens.
  369. {
  370. CVssFunctionTracer ft( VSSDBG_COORD, L"CVssWorkerThread::Terminate" );
  371. if (m_hThread)
  372. {
  373. // Try to terminate the thread. The OnRun code must periodically check the
  374. // m_bTerminateNow variable by callign MustBeTerminated method.
  375. m_bTerminateNow = true;
  376. // Notify that the thread is terminating...
  377. OnTerminate();
  378. if (bThreadPreparedOnly) // TRUE only if the thread is supposed to be non-resumed.
  379. {
  380. // Resume the thread. Here ThreadFunction will be called on the resumed thread.
  381. DWORD dwResult = ::ResumeThread( m_hThread );
  382. // Check for error
  383. if (dwResult == 0xFFFFFFFF)
  384. {
  385. ft.LogGenericWarning( VSSDBG_GEN,
  386. L"ResumeThread(%p) = -1, GetLastError() == 0x%08lx, m_eThreadState == %d",
  387. m_hThread, GetLastError(), (INT)m_eThreadState );
  388. ft.Trace( VSSDBG_GEN,
  389. L"ResumeThread failed. Error: 0x%08lx. State: %d",
  390. GetLastError(), m_eThreadState);
  391. // Reset the object state
  392. m_eThreadState = VSS_THREAD_ERROR;
  393. return E_UNEXPECTED; // Error resuming the thread
  394. }
  395. }
  396. if (::WaitForSingleObject( m_hThread, INFINITE ) == WAIT_FAILED)
  397. {
  398. ft.LogGenericWarning( VSSDBG_GEN,
  399. L"WaitForSingleObject(%p,INFINITE) == WAIT_FAILED, GetLastError() == 0x%08lx, m_eThreadState == %d",
  400. m_hThread, GetLastError(), (INT)m_eThreadState );
  401. ft.Trace( VSSDBG_GEN,
  402. L"WaitForSingleObject failed. Error: 0x%08lx. State: %d",
  403. GetLastError(), m_eThreadState);
  404. // Reset the object state
  405. m_eThreadState = VSS_THREAD_ERROR;
  406. return E_UNEXPECTED; // Error resuming the thread
  407. }
  408. }
  409. return S_OK;
  410. }
  411. // Internal data members.
  412. private:
  413. bool m_bInitSucceeded;
  414. bool m_bTerminateNow;
  415. HANDLE m_hThread;
  416. UINT m_nThreadID;
  417. VSS_ENUM_THREAD m_eThreadState;
  418. bool m_bStarted; // To check that the worker thread is resumed.
  419. };
  420. #endif // __VSS_WORKER_HXX__