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.

461 lines
9.9 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. All rights reserved.
  4. Module Name:
  5. exec.cxx
  6. Abstract:
  7. Handles async commands.
  8. The client give TExec a worker object (MExecWork). At some later
  9. time, the worker is given a thread (called via svExecute).
  10. If the Job is already on the list but is waiting, we just return.
  11. If it's not on the list but currently executing, we add it to the
  12. list when it's done executing.
  13. kExecActive -> currently executing (so it's not on the list)
  14. kExecActiveReq -> kExecActive is on, and it needs to run again.
  15. If kExecExit is added, we call vExecExitComplete() (virtual function)
  16. when all jobs have been completed and assume the the client is cleaning
  17. up after itself. In fact, the MExecWork object may be destroyed
  18. in vExecExitComplete().
  19. Author:
  20. Albert Ting (AlbertT) 8-Nov-1994
  21. Revision History:
  22. --*/
  23. #include "spllibp.hxx"
  24. #pragma hdrstop
  25. BOOL
  26. TExec::
  27. bJobAdd(
  28. MExecWork* pExecWork,
  29. STATEVAR StateVar
  30. )
  31. /*++
  32. Routine Description:
  33. Adds a job request to move to a given state.
  34. Arguments:
  35. pExecWork - Work structure.
  36. StateVar - StateVar that we want to move toward. If kExecExit
  37. is set, then we will complete the currently executing job
  38. then exit by calling vExecExitComplete().
  39. Note that kExecExit should be added by itself; if it is added
  40. with other commands, they will be ignored.
  41. Return Value:
  42. TRUE = Job added (or job already on wait list)
  43. FALSE = failed, GLE.
  44. Adding kExecExit is guarenteed to succeed here.
  45. --*/
  46. {
  47. BOOL bReturn = TRUE;
  48. BOOL bCallExecExitComplete = FALSE;
  49. {
  50. TCritSecLock CSL( *_pCritSec );
  51. DBGMSG( DBG_EXEC,
  52. ( "Exec.bJobAdd: %x StateVar = %x\n", pExecWork, StateVar ));
  53. //
  54. // Job bits must not have PRIVATE bits.
  55. //
  56. SPLASSERT( !( StateVar & kExecPrivate ));
  57. //
  58. // Don't allow adding a job after we are set to exit.
  59. //
  60. SPLASSERT( !( pExecWork->_State & kExecExit ));
  61. //
  62. // If it's active (currently executing in a thread), then it is
  63. // not on the wait list and we mark it as ACTIVE_REQ so that
  64. // when the job completes, it sees that more jobs have accumulated.
  65. //
  66. if( pExecWork->_State & kExecActive ){
  67. DBGMSG( DBG_EXEC,
  68. ( "\n ACTIVE, ++REQ _State %x _StatePending %x\n",
  69. (STATEVAR)pExecWork->_State,
  70. (STATEVAR)pExecWork->_StatePending ));
  71. //
  72. // Can't be an immediate request if executing already.
  73. //
  74. SPLASSERT( !( StateVar & kExecRunNow ));
  75. pExecWork->_StatePending |= StateVar;
  76. pExecWork->_State |= kExecActiveReq;
  77. bReturn = TRUE;
  78. } else {
  79. //
  80. // Store up the work requested since we aren't currently active.
  81. //
  82. pExecWork->_State |= StateVar;
  83. //
  84. // If we are not on the wait list, add it.
  85. //
  86. if( !pExecWork->Work_bLinked( )){
  87. if( StateVar & kExecExit ){
  88. bCallExecExitComplete = TRUE;
  89. } else {
  90. DBGMSG( DBG_EXEC, ( "not linked, added\n" ));
  91. SPLASSERT( NULL == Work_pFind( pExecWork ));
  92. bReturn = bJobAddWorker( pExecWork );
  93. }
  94. } else {
  95. DBGMSG( DBG_EXEC, ( "linked, NOP\n" ));
  96. }
  97. }
  98. }
  99. if( bCallExecExitComplete ){
  100. //
  101. // Special case exit: we should exit. Once we call
  102. // vExecExitComplete, we can't refer to *this anymore,
  103. // since we may have deleted ourself.
  104. //
  105. pExecWork->vExecExitComplete();
  106. bReturn = TRUE;
  107. }
  108. return bReturn;
  109. }
  110. VOID
  111. TExec::
  112. vJobDone(
  113. MExecWork* pExecWork,
  114. STATEVAR StateVar
  115. )
  116. /*++
  117. Routine Description:
  118. A job has compeleted execution, clean up.
  119. Arguments:
  120. pExecWork - Unit that just completed executing.
  121. StateVar - New state that the object is in (requests that
  122. successfully completed should be turned off--done by client).
  123. Return Value:
  124. --*/
  125. {
  126. BOOL bCallExecExitComplete = FALSE;
  127. BOOL bCallExecFailedAddJob = FALSE;
  128. {
  129. TCritSecLock CSL( *_pCritSec );
  130. DBGMSG( DBG_EXEC,
  131. ( "Exec.vJobDone: %x completed -> %x +(new) %x = %x\n",
  132. pExecWork, StateVar, (DWORD)pExecWork->_StatePending,
  133. (DWORD)pExecWork->_State | pExecWork->_StatePending ));
  134. //
  135. // kExecRunNow can not be set when the object is working.
  136. //
  137. SPLASSERT( !( StateVar & kExecRunNow ));
  138. //
  139. // We have completed the work request, put in the new state.
  140. // Keep the private bits, and add in the new state variable,
  141. // plus any additional work that was pending.
  142. //
  143. // The ExecNow bit is not saved (it's not part of kExecPrivate)
  144. // since it's good for one shot only.
  145. //
  146. pExecWork->_State = ( pExecWork->_State & kExecPrivate ) |
  147. ( StateVar & ~kExecPrivate ) |
  148. pExecWork->_StatePending;
  149. pExecWork->_State &= ~kExecActive;
  150. //
  151. // If job is done, then quit.
  152. //
  153. if( pExecWork->_State & kExecExit ){
  154. DBGMSG( DBG_EXEC,
  155. ( "Exec.vJobDone: _State %x, calling vExecExitComplete\n",
  156. (STATEVAR)pExecWork->_State ));
  157. bCallExecExitComplete = TRUE;
  158. } else {
  159. //
  160. // If we have more work to do, add ourselves back
  161. // to the queue.
  162. //
  163. if( pExecWork->_State & kExecActiveReq &&
  164. !bJobAddWorker( pExecWork )){
  165. bCallExecFailedAddJob = TRUE;
  166. }
  167. }
  168. }
  169. if( bCallExecFailedAddJob ){
  170. //
  171. // Fail on delayed job add.
  172. //
  173. pExecWork->vExecFailedAddJob();
  174. }
  175. if( bCallExecExitComplete ){
  176. //
  177. // Once vExecExitComplete has been called, the current object
  178. // pExecWork may be destroyed.
  179. //
  180. // Don't refer to it again since vExecExitComplete may delete
  181. // this as part of cleanup.
  182. //
  183. pExecWork->vExecExitComplete();
  184. }
  185. }
  186. STATEVAR
  187. TExec::
  188. svClearPendingWork(
  189. MExecWork* pExecWork
  190. )
  191. /*++
  192. Routine Description:
  193. Queries what work is currently pending.
  194. Arguments:
  195. pExecWork -- Work item.
  196. Return Value:
  197. --*/
  198. {
  199. TCritSecLock CSL( *_pCritSec );
  200. //
  201. // Return pending work, minus the private and kExecRunNow
  202. // bits.
  203. //
  204. STATEVAR svPendingWork = pExecWork->_StatePending & ~kExecNoOutput;
  205. pExecWork->_StatePending = 0;
  206. return svPendingWork;
  207. }
  208. /********************************************************************
  209. Private
  210. ********************************************************************/
  211. TExec::
  212. TExec(
  213. MCritSec* pCritSec
  214. ) : TThreadM( 10, 2000, pCritSec ), _pCritSec( pCritSec )
  215. {
  216. }
  217. BOOL
  218. TExec::
  219. bJobAddWorker(
  220. MExecWork* pExecWork
  221. )
  222. /*++
  223. Routine Description:
  224. Common code to add a job to our linked list.
  225. Must be called inside the _pCritSec. It does leave it inside
  226. this function, however.
  227. Arguments:
  228. Return Value:
  229. --*/
  230. {
  231. SPLASSERT( _pCritSec->bInside( ));
  232. BOOL bRunNow = FALSE;
  233. //
  234. // Check if the client wants the job to run right now.
  235. //
  236. if( pExecWork->_State & kExecRunNow ){
  237. //
  238. // Add the job to the head of the queue. Since we always pull
  239. // jobs from the beginning, we'll get to this job first.
  240. //
  241. // If a non-RunNow job is added to the list before we execute,
  242. // we'll still run, since the other job will be added to the
  243. // end of the list.
  244. //
  245. // If another RunNow job is added, we'll spawn separate threads
  246. // for each (unless an idle thread is available).
  247. //
  248. Work_vAdd( pExecWork );
  249. bRunNow = TRUE;
  250. } else {
  251. Work_vAppend( pExecWork );
  252. }
  253. if( !bJobAdded( bRunNow ) ){
  254. DBGMSG( DBG_INFO, ( "Exec.vJobProcess: unable to add job %x: %d\n",
  255. pExecWork,
  256. GetLastError( )));
  257. Work_vDelink( pExecWork );
  258. return FALSE;
  259. }
  260. return TRUE;
  261. }
  262. PJOB
  263. TExec::
  264. pThreadMJobNext(
  265. VOID
  266. )
  267. /*++
  268. Routine Description:
  269. Gets the next job from the queue. This function is defined in
  270. TThreadM.
  271. Arguments:
  272. Return Value:
  273. --*/
  274. {
  275. TCritSecLock CSL( *_pCritSec );
  276. MExecWork* pExecWork = Work_pHead();
  277. if( !pExecWork ){
  278. return NULL;
  279. }
  280. Work_vDelink( pExecWork );
  281. //
  282. // Job should never be active here.
  283. //
  284. SPLASSERT( !(pExecWork->_State & kExecActive) );
  285. //
  286. // We will handle all requests now, so clear kExecActiveReq.
  287. // Also remove kExecRunNow, since it's one shot only, and mark us
  288. // as currently active (kExecActive).
  289. //
  290. pExecWork->_State &= ~( kExecActiveReq | kExecRunNow );
  291. pExecWork->_State |= kExecActive;
  292. return (PJOB)pExecWork;
  293. }
  294. VOID
  295. TExec::
  296. vThreadMJobProcess(
  297. PJOB pJob
  298. )
  299. /*++
  300. Routine Description:
  301. Process a job in the current thread. We call the virtual function
  302. with the job object, then clear out the bits that it has completed.
  303. (This is a member of TThreadM.)
  304. If there is additional pending work (ACTIVE_REQ), then we re-add
  305. the job.
  306. If there is a failure in the re-add case, we must send an
  307. asynchronous fail message.
  308. Arguments:
  309. pJob - MExecWork instance.
  310. Return Value:
  311. --*/
  312. {
  313. SPLASSERT( _pCritSec->bOutside( ));
  314. STATEVAR StateVar;
  315. MExecWork* pExecWork = (MExecWork*)pJob;
  316. //
  317. // Do the work.
  318. //
  319. StateVar = pExecWork->svExecute( pExecWork->State() & ~kExecNoOutput );
  320. vJobDone( pExecWork, StateVar );
  321. }