Windows NT 4.0 source code leak
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.

551 lines
13 KiB

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