Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

552 lines
13 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. All rights reserved
  4. Module Name:
  5. ThreadM.c
  6. Abstract:
  7. Generic thread manager for spooler.
  8. Author:
  9. Albert Ting (AlbertT) 13-Feb-1994
  10. Environment:
  11. User Mode -Win32
  12. Revision History:
  13. Albert Ting (AlbertT) 28-May-1994 C++ized
  14. --*/
  15. #include "spllibp.hxx"
  16. #pragma hdrstop
  17. pfCreateThread gpfSafeCreateThread;
  18. /********************************************************************
  19. Public interfaces.
  20. ********************************************************************/
  21. TThreadM::
  22. TThreadM(
  23. UINT uMaxThreads,
  24. UINT uIdleLife,
  25. MCritSec* pCritSec OPTIONAL
  26. ) :
  27. _uMaxThreads(uMaxThreads), _uIdleLife(uIdleLife), _uActiveThreads(0),
  28. _uRunNowThreads(0), _iIdleThreads(0)
  29. /*++
  30. Routine Description:
  31. Construct a Thread Manager object.
  32. Arguments:
  33. uMaxThreads - Upper limit of threads that will be created.
  34. uIdleLife - Maximum life once a thread goes idle (in ms).
  35. pCritSec - Use this crit sec for synchronization (if not specified,
  36. a private one will be created).
  37. Return Value:
  38. Notes:
  39. _hTrigger is our validity variable. When this value is NULL,
  40. instantiation failed. If it's non-NULL, the entire object is valid.
  41. --*/
  42. {
  43. _hTrigger = CreateEvent( NULL,
  44. FALSE,
  45. FALSE,
  46. NULL );
  47. if( !_hTrigger ){
  48. return;
  49. }
  50. //
  51. // If no critical section, create our own.
  52. //
  53. if (!pCritSec) {
  54. _pCritSec = new MCritSec();
  55. if( !VALID_PTR( _pCritSec )){
  56. //
  57. // _hTrigger is our valid variable. If we fail to create
  58. // the critical section, prepare to return failure.
  59. //
  60. CloseHandle( _hTrigger );
  61. _hTrigger = NULL;
  62. return;
  63. }
  64. _State |= kPrivateCritSec;
  65. } else {
  66. _pCritSec = pCritSec;
  67. }
  68. }
  69. VOID
  70. TThreadM::
  71. vDelete(
  72. VOID
  73. )
  74. /*++
  75. Routine Description:
  76. Indicates that the object is pending deletion. Any object that
  77. inherits from vDelete should _not_ call the destructor directly,
  78. since there may be pending jobs. Instead, they should call
  79. TThreadM::vDelete().
  80. Arguments:
  81. Return Value:
  82. --*/
  83. {
  84. BOOL bDestroy = FALSE;
  85. {
  86. TCritSecLock CSL( *_pCritSec );
  87. //
  88. // Mark as wanting to be destroyed.
  89. //
  90. _State |= kDestroyReq;
  91. if( !_iIdleThreads && !_uActiveThreads ){
  92. bDestroy = TRUE;
  93. }
  94. }
  95. if( bDestroy ){
  96. //
  97. // Delete the object. Note that absolutely no object fields
  98. // can be accessed after this, since the object is returned
  99. // to free store.
  100. //
  101. delete this;
  102. }
  103. }
  104. BOOL
  105. TThreadM::
  106. bJobAdded(
  107. BOOL bRunNow
  108. )
  109. /*++
  110. Routine Description:
  111. Notify the thread manager that a new job has been added. This job
  112. will be processed fifo.
  113. Arguments:
  114. bRunNow - Ignore the thread limits and run the job now.
  115. Return Value:
  116. TRUE - Job successfully added.
  117. FALSE - Job could not be added.
  118. --*/
  119. {
  120. DWORD dwThreadId;
  121. HANDLE hThread;
  122. BOOL rc = TRUE;
  123. TCritSecLock CSL( *_pCritSec );
  124. if( _State.bBit( kDestroyReq )){
  125. DBGMSG( DBG_THREADM | DBG_ERROR,
  126. ( "ThreadM.bJobAdded: add failed since DESTROY requested.\n" ));
  127. SetLastError( ERROR_INVALID_PARAMETER );
  128. rc = FALSE;
  129. } else {
  130. //
  131. // We either: give it to an idle thread, create a new thread,
  132. // or leave it in the queue.
  133. //
  134. if( _iIdleThreads > 0 ){
  135. //
  136. // There are some idle threads--trigger the event and it
  137. // will be picked up.
  138. //
  139. --_iIdleThreads;
  140. DBGMSG( DBG_THREADM,
  141. ( "ThreadM.bJobAdded: Trigger: --iIdle %d, uActive %d\n",
  142. _iIdleThreads, _uActiveThreads ));
  143. //
  144. // If we set the event, then the worker thread that receives
  145. // this event should _not_ decrement _iIdleThreads, since
  146. // we already did this.
  147. //
  148. SetEvent( _hTrigger );
  149. } else if( _uActiveThreads < _uMaxThreads || bRunNow ){
  150. //
  151. // No idle threads, but we can create a new one since we
  152. // haven't reached the limit, or the bRunNow flag is set.
  153. //
  154. hThread = gpfSafeCreateThread( NULL,
  155. 0,
  156. xdwThreadProc,
  157. this,
  158. 0,
  159. &dwThreadId );
  160. if( hThread ){
  161. CloseHandle( hThread );
  162. //
  163. // We have successfully created a thread; up the
  164. // count.
  165. //
  166. ++_uActiveThreads;
  167. //
  168. // We have less active threads than the max; create a new one.
  169. //
  170. DBGMSG( DBG_THREADM,
  171. ( "ThreadM.bJobAdded: ct: iIdle %d, ++uActive %d\n",
  172. _iIdleThreads,
  173. _uActiveThreads ));
  174. } else {
  175. rc = FALSE;
  176. DBGMSG( DBG_THREADM | DBG_WARN,
  177. ( "ThreadM.bJobAdded: unable to ct %d\n",
  178. GetLastError( )));
  179. }
  180. } else {
  181. //
  182. // No idle threads, and we are already at the max so we
  183. // can't create new threads. Dec iIdleThreads anyway
  184. // (may go negative, but that's ok).
  185. //
  186. // iIdleThreads represents the number of threads that
  187. // are currently not processing jobs. If the number is
  188. // negative, this indicates that even if a thread suddenly
  189. // completes a job and would go idle, there is a queued
  190. // job that would immediately grab it, so the thread really
  191. // didn't go into an idle state.
  192. //
  193. // The negative number indicates the number of outstanding
  194. // jobs that are queued (e.g., -5 indicate 5 jobs queued).
  195. //
  196. // There is always an increment of iIdleThreads when a
  197. // job is compeleted.
  198. //
  199. --_iIdleThreads;
  200. //
  201. // No threads idle, and at max threads.
  202. //
  203. DBGMSG( DBG_THREADM,
  204. ( "ThreadM.bJobAdded: wait: --iIdle %d, uActive %d\n",
  205. _iIdleThreads,
  206. _uActiveThreads ));
  207. }
  208. //
  209. // If we succeeded and bRunNow is set, this indicates that
  210. // we were able to start a special thread, so we need to adjust
  211. // the maximum number of threads. When a this special thread
  212. // job completes, we will decrement it.
  213. //
  214. if( bRunNow && rc ){
  215. ++_uMaxThreads;
  216. ++_uRunNowThreads;
  217. }
  218. }
  219. return rc;
  220. }
  221. /********************************************************************
  222. Private routines.
  223. ********************************************************************/
  224. TThreadM::
  225. ~TThreadM(
  226. VOID
  227. )
  228. /*++
  229. Routine Description:
  230. Destroy the thread manager object. This is private; to request
  231. that the thread manager quit, call vDelete().
  232. Arguments:
  233. Return Value:
  234. --*/
  235. {
  236. SPLASSERT( _State.bBit( kDestroyReq ));
  237. if( _State.bBit( kPrivateCritSec )){
  238. SPLASSERT( _pCritSec->bOutside( ));
  239. delete _pCritSec;
  240. }
  241. if( _hTrigger )
  242. CloseHandle( _hTrigger );
  243. vThreadMDeleteComplete();
  244. }
  245. VOID
  246. TThreadM::
  247. vThreadMDeleteComplete(
  248. VOID
  249. )
  250. /*++
  251. Routine Description:
  252. Stub routine for objects that don't need deletion
  253. complete notification.
  254. Arguments:
  255. Return Value:
  256. --*/
  257. {
  258. }
  259. DWORD
  260. TThreadM::
  261. xdwThreadProc(
  262. LPVOID pVoid
  263. )
  264. /*++
  265. Routine Description:
  266. Worker thread routine that calls the client to process the jobs.
  267. Arguments:
  268. pVoid - pTMStateVar
  269. Return Value:
  270. Ignored.
  271. --*/
  272. {
  273. TThreadM* pThreadM = (TThreadM*)pVoid;
  274. return pThreadM->dwThreadProc();
  275. }
  276. DWORD
  277. TThreadM::
  278. dwThreadProc(
  279. VOID
  280. )
  281. {
  282. BOOL bDestroy = FALSE;
  283. {
  284. TCritSecLock CSL( *_pCritSec );
  285. DBGMSG( DBG_THREADM,
  286. ( "ThreadM.dwThreadProc: ct: iIdle %d, uActive %d\n",
  287. _iIdleThreads,
  288. _uActiveThreads));
  289. PJOB pJob = pThreadMJobNext();
  290. while( TRUE ){
  291. for( ; pJob; pJob=pThreadMJobNext( )){
  292. //
  293. // If bRunNow count is non-zero, this indicates that we just
  294. // picked up a RunNow job. As soon as it completes, we
  295. // can decrement the count.
  296. //
  297. BOOL bRunNowCompleted = _uRunNowThreads > 0;
  298. {
  299. TCritSecUnlock CSU( *_pCritSec );
  300. //
  301. // Call back to client to process the job.
  302. //
  303. DBGMSG( DBG_THREADM,
  304. ( "ThreadM.dwThreadProc: %x processing\n",
  305. (ULONG_PTR)pJob ));
  306. //
  307. // Call through virtual function to process the
  308. // user's job.
  309. //
  310. vThreadMJobProcess( pJob );
  311. DBGMSG( DBG_THREADM,
  312. ( "ThreadM.dwThreadProc: %x processing done\n",
  313. (ULONG_PTR)pJob ));
  314. }
  315. //
  316. // If a RunNow job has been completed, then decrement both
  317. // counts. uMaxThreads was increased by one when the job was
  318. // accepted, so now it must be lowered.
  319. //
  320. if( bRunNowCompleted ){
  321. --_uMaxThreads;
  322. --_uRunNowThreads;
  323. }
  324. ++_iIdleThreads;
  325. DBGMSG( DBG_THREADM,
  326. ( "ThreadM.dwThreadProc: ++iIdle %d, uActive %d\n",
  327. _iIdleThreads,
  328. _uActiveThreads ));
  329. }
  330. DBGMSG( DBG_THREADM,
  331. ( "ThreadM.dwThreadProc: Sleep: iIdle %d, uActive %d\n",
  332. _iIdleThreads,
  333. _uActiveThreads ));
  334. {
  335. TCritSecUnlock CSU( *_pCritSec );
  336. //
  337. // Done, now relax and go idle for a bit. We don't
  338. // care whether we timeout or get triggered; in either
  339. // case we check for another job.
  340. //
  341. WaitForSingleObject( _hTrigger, _uIdleLife );
  342. }
  343. //
  344. // We must check here instead of relying on the return value
  345. // of WaitForSingleObject since someone may see iIdleThreads!=0
  346. // and set the trigger, but we timeout before it gets set.
  347. //
  348. pJob = pThreadMJobNext();
  349. if( pJob ){
  350. DBGMSG( DBG_THREADM,
  351. ( "ThreadM.dwThreadProc: Woke and found job: iIdle %d, uActive %d\n",
  352. _iIdleThreads,
  353. _uActiveThreads ));
  354. } else {
  355. //
  356. // No jobs found; break. Be sure to reset the hTrigger, since
  357. // there are no waiting jobs, and the main thread might
  358. // have set it in the following case:
  359. //
  360. // MainThread: WorkerThread:
  361. // Sleeping
  362. // Awoke, not yet in CS.
  363. // GotJob
  364. // SetEvent
  365. // --iIdleThreads
  366. // Enter CS, found job, process it.
  367. //
  368. // In this case, the event is set, but there is no thread
  369. // to pick it up.
  370. //
  371. ResetEvent( _hTrigger );
  372. break;
  373. }
  374. }
  375. //
  376. // Decrement ActiveThreads. This was incremented when the thread
  377. // was successfully created, and should be decremented when the thread
  378. // is about to exit.
  379. //
  380. --_uActiveThreads;
  381. //
  382. // The thread enters an idle state right before it goes to sleep.
  383. //
  384. // When a job is added, the idle count is decremented by the main
  385. // thread, so the worker thread doesn't decrement it (avoids sync
  386. // problems). If the worker thread timed out and there were no jobs,
  387. // then we need to decrement the matching initial increment here.
  388. //
  389. --_iIdleThreads;
  390. if( _State.bBit( kDestroyReq ) &&
  391. !_uActiveThreads &&
  392. !_iIdleThreads ){
  393. //
  394. // Destroy requested.
  395. //
  396. bDestroy = TRUE;
  397. }
  398. DBGMSG( DBG_THREADM,
  399. ( "ThreadM.dwThreadProc: dt: --iIdle %d, --uActive %d\n",
  400. _iIdleThreads,
  401. _uActiveThreads));
  402. }
  403. if( bDestroy ){
  404. delete this;
  405. }
  406. return 0;
  407. }