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.

714 lines
22 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: taskman.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains the core functions of the input sub-system
  7. *
  8. * History:
  9. * 02-27-91 MikeHar Created.
  10. * 02-23-92 MattFe rewrote sleeptask
  11. * 09-07-93 DaveHart Per-process nonpreemptive scheduler for
  12. * multiple WOW VDM support.
  13. \***************************************************************************/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. /***************************************************************************\
  17. * WakeWowTask
  18. *
  19. * If needed the wowtask is woken by setting its event. It is assumed that if
  20. * any wow task is currently scheduled that it is a waste of time to wake the
  21. * specified wow task since rescheduling will occur when the currently
  22. * scheduled wow task enters xxxSleepTask.
  23. *
  24. * History:
  25. * ????
  26. \***************************************************************************/
  27. VOID
  28. WakeWowTask(
  29. PTHREADINFO pti)
  30. {
  31. PWOWPROCESSINFO pwpi;
  32. pwpi = pti->ppi->pwpi;
  33. if (pwpi && !pwpi->ptiScheduled) {
  34. KeSetEvent(pti->pEventQueueServer, EVENT_INCREMENT, FALSE);
  35. }
  36. }
  37. /***************************************************************************\
  38. * InsertTask
  39. *
  40. * This function removes a task from its old location and inserts
  41. * in the proper prioritized location
  42. *
  43. * Find a place for this task such that it must be inserted
  44. * after any task with greater or equal priority and must be
  45. * before any task with higher priorty. The higher the priority
  46. * the less urgent the task.
  47. *
  48. * History:
  49. * 19-Nov-1993 mikeke Created
  50. \***************************************************************************/
  51. VOID InsertTask(
  52. PPROCESSINFO ppi,
  53. PTDB ptdbNew)
  54. {
  55. PTDB *pptdb;
  56. PTDB ptdb;
  57. int nPriority;
  58. PWOWPROCESSINFO pwpi = ppi->pwpi;
  59. CheckCritIn();
  60. UserAssert(pwpi != NULL);
  61. pptdb = &pwpi->ptdbHead;
  62. nPriority = ptdbNew->nPriority;
  63. while ((ptdb = *pptdb) != NULL) {
  64. /*
  65. * Remove it from it's old location
  66. */
  67. if (ptdb == ptdbNew) {
  68. *pptdb = ptdbNew->ptdbNext;
  69. /*
  70. * continue to search for the place to insert it
  71. */
  72. while ((ptdb = *pptdb) != NULL) {
  73. if (nPriority < ptdb->nPriority) {
  74. break;
  75. }
  76. pptdb = &(ptdb->ptdbNext);
  77. }
  78. break;
  79. }
  80. /*
  81. * if this is the place to insert continue to search for the
  82. * place to delete it from
  83. */
  84. if (nPriority < ptdb->nPriority) {
  85. do {
  86. if (ptdb->ptdbNext == ptdbNew) {
  87. ptdb->ptdbNext = ptdbNew->ptdbNext;
  88. break;
  89. }
  90. ptdb = ptdb->ptdbNext;
  91. } while (ptdb != NULL);
  92. break;
  93. }
  94. pptdb = &(ptdb->ptdbNext);
  95. }
  96. /*
  97. * insert the new task
  98. */
  99. ptdbNew->ptdbNext = *pptdb;
  100. *pptdb = ptdbNew;
  101. }
  102. /***************************************************************************\
  103. * DestroyTask()
  104. *
  105. * History:
  106. * 02-27-91 MikeHar Created.
  107. \***************************************************************************/
  108. VOID DestroyTask(
  109. PPROCESSINFO ppi,
  110. PTHREADINFO ptiToRemove)
  111. {
  112. PTDB ptdbToRemove = ptiToRemove->ptdb;
  113. PTDB ptdb;
  114. PTDB* pptdb;
  115. PWOWPROCESSINFO pwpi = ppi->pwpi;
  116. // try to catch #150446
  117. CheckCritIn();
  118. BEGINATOMICCHECK();
  119. UserAssert(pwpi != NULL);
  120. if (ptdbToRemove != NULL) {
  121. if (ptdbToRemove->TDB_Flags & TDBF_SETUP) {
  122. /*
  123. * This means that the WoW app was a setup app (checked in
  124. * SetAppCompatFlags). If so, the shell needs to be notified so
  125. * it can clean up potential problems caused by bad calls to
  126. * DDE, etc.
  127. */
  128. PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiToRemove);
  129. if (pdeskinfo->spwndShell) {
  130. _PostMessage(pdeskinfo->spwndShell, DTM_SETUPAPPRAN, 0, 0);
  131. }
  132. }
  133. /*
  134. * Remove the WOW per thread info.
  135. */
  136. if (ptdbToRemove->pwti) {
  137. PWOWTHREADINFO *ppwti = &gpwtiFirst;
  138. while (*ppwti != ptdbToRemove->pwti && (*ppwti)->pwtiNext != NULL) {
  139. ppwti = &((*ppwti)->pwtiNext);
  140. }
  141. if (*ppwti == ptdbToRemove->pwti) {
  142. *ppwti = ptdbToRemove->pwti->pwtiNext;
  143. }
  144. CLOSE_PSEUDO_EVENT(&ptdbToRemove->pwti->pIdleEvent);
  145. UserFreePool(ptdbToRemove->pwti);
  146. }
  147. gpsi->nEvents -= ptdbToRemove->nEvents;
  148. /*
  149. * remove it from any lists
  150. */
  151. pptdb = &pwpi->ptdbHead;
  152. while ((ptdb = *pptdb) != NULL) {
  153. /*
  154. * Remove it from it's old location
  155. */
  156. if (ptdb == ptdbToRemove) {
  157. *pptdb = ptdb->ptdbNext;
  158. UserAssert(ptiToRemove->ptdb == ptdbToRemove);
  159. UserFreePool(ptdbToRemove);
  160. ptiToRemove->ptdb = NULL;
  161. break;
  162. }
  163. pptdb = &(ptdb->ptdbNext);
  164. }
  165. UserAssert(ptdb == ptdbToRemove);
  166. }
  167. ENDATOMICCHECK();
  168. /*
  169. * If the task being destroyed is the active task, make nobody active.
  170. * We will go through this code path for 32-bit threads that die while
  171. * Win16 threads are waiting for a SendMessage reply from them.
  172. */
  173. if (pwpi->ptiScheduled == ptiToRemove) {
  174. pwpi->ptiScheduled = NULL;
  175. ExitWowCritSect(ptiToRemove, pwpi);
  176. /*
  177. * Wake next task with events, or wowexec to run the scheduler
  178. */
  179. if (pwpi->ptdbHead != NULL) {
  180. for (ptdb = pwpi->ptdbHead; ptdb; ptdb = ptdb->ptdbNext) {
  181. if (ptdb->nEvents > 0) {
  182. KeSetEvent(ptdb->pti->pEventQueueServer,
  183. EVENT_INCREMENT, FALSE);
  184. break;
  185. }
  186. }
  187. if (!ptdb) {
  188. KeSetEvent(pwpi->pEventWowExec, EVENT_INCREMENT, FALSE);
  189. }
  190. }
  191. }
  192. UserAssert(ptiToRemove != pwpi->CSOwningThread);
  193. }
  194. /***************************************************************************\
  195. * xxxSleepTask
  196. *
  197. * This function puts this task to sleep and wakes the next (if any)
  198. * deserving task.
  199. *
  200. * BOOL fInputIdle - app is going idle, may do idle hooks
  201. * HANDLE hEvent - if nonzero, WowExec's event (client side) for
  202. * virtual HW Interrupt HotPath.
  203. * History:
  204. * 02-27-91 MikeHar Created.
  205. * 02-23-91 MattFe rewrote
  206. * 12-17-93 Jonle add wowexec hotpath for VirtualInterrupts
  207. \***************************************************************************/
  208. BOOL xxxSleepTask(
  209. BOOL fInputIdle,
  210. HANDLE hEvent)
  211. {
  212. PTDB ptdb;
  213. PTHREADINFO pti;
  214. PPROCESSINFO ppi;
  215. PWOWPROCESSINFO pwpi;
  216. PSMS psms;
  217. NTSTATUS Status;
  218. int nHandles;
  219. BOOLEAN bWaitedAtLeastOnce;
  220. /*
  221. * !!!
  222. * ClearSendMessages assumes that this function does NOT leave the
  223. * critical section when called with fInputIdle==FALSE and from a
  224. * 32bit thread!
  225. */
  226. CheckCritIn();
  227. pti = PtiCurrent();
  228. ppi = pti->ppi;
  229. pwpi = ppi->pwpi;
  230. /*
  231. * If this task has received a message from outside of the current
  232. * wow scheduler and hasn't yet replied to the message, the scheduler
  233. * will deadlock because the send\receive lock counts are updated
  234. * in ReplyMessage and not in receive message. Check for this
  235. * condition and do the DirectedSchedukeTask that normally occurs
  236. * in ReplyMessage. 16-Feb-1995 Jonle
  237. */
  238. psms = pti->psmsCurrent;
  239. if (psms && psms->ptiReceiver == pti &&
  240. psms->ptiSender && !(psms->flags & SMF_REPLY) &&
  241. psms->flags & (SMF_RECEIVERBUSY | SMF_RECEIVEDMESSAGE) &&
  242. psms->ptiSender->TIF_flags & TIF_16BIT &&
  243. (pwpi != psms->ptiSender->ppi->pwpi || !(pti->TIF_flags & TIF_16BIT)) ) {
  244. DirectedScheduleTask(psms->ptiReceiver, psms->ptiSender, FALSE, psms);
  245. }
  246. /*
  247. * Return immediately if we are not 16 bit (don't have a pwpi).
  248. */
  249. if (!(pti->TIF_flags & TIF_16BIT)) {
  250. return FALSE;
  251. }
  252. /*
  253. * Deschedule the current task.
  254. */
  255. if (pti == pwpi->ptiScheduled) {
  256. ExitWowCritSect(pti, pwpi);
  257. pwpi->ptiScheduled = NULL;
  258. }
  259. UserAssert(pti != pwpi->CSOwningThread);
  260. /*
  261. * If this is wowexec calling on WowWaitForMsgAndEvent
  262. * set up the WakeMask for all messages , and check for wake
  263. * bits set since the last time. Reinsert wowexec, at the end
  264. * of the list so other 16 bit tasks will be scheduled first.
  265. */
  266. if (pwpi->hEventWowExecClient == hEvent) {
  267. InsertTask(ppi, pti->ptdb);
  268. pti->pcti->fsWakeMask = QS_ALLINPUT | QS_EVENT;
  269. if (pti->pcti->fsChangeBits & pti->pcti->fsWakeMask) {
  270. pti->ptdb->nEvents++;
  271. gpsi->nEvents++;
  272. }
  273. }
  274. bWaitedAtLeastOnce = FALSE;
  275. do {
  276. /*
  277. * If nobody is Active look for the highest priority task with
  278. * some events pending. if MsgWaitForMultiple call don't
  279. * reschedule self
  280. */
  281. if (pwpi->ptiScheduled == NULL) {
  282. rescan:
  283. if (pwpi->nRecvLock >= pwpi->nSendLock) {
  284. for (ptdb = pwpi->ptdbHead; ptdb; ptdb = ptdb->ptdbNext) {
  285. if (ptdb->nEvents > 0 &&
  286. !(hEvent == HEVENT_REMOVEME && ptdb->pti == pti)) {
  287. pwpi->ptiScheduled = ptdb->pti;
  288. break;
  289. }
  290. }
  291. if (bWaitedAtLeastOnce) {
  292. //
  293. // If not first entry into sleep task avoid waiting
  294. // more than needed, if the curr task is now scheduled.
  295. //
  296. if (pwpi->ptiScheduled == pti) {
  297. break;
  298. }
  299. } else {
  300. //
  301. // On the first entry into sleep task input is going
  302. // idle if no tasks are ready to run. Call the idle
  303. // hook if there is one.
  304. //
  305. if (fInputIdle &&
  306. pwpi->ptiScheduled == NULL &&
  307. IsHooked(pti, WHF_FOREGROUNDIDLE)) {
  308. /*
  309. * Make this the active task so that no other
  310. * task will become active while we're calling
  311. * the hook.
  312. */
  313. pwpi->ptiScheduled = pti;
  314. xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
  315. /*
  316. * Reset state so that no tasks are active. We
  317. * then need to rescan the task list to see if
  318. * a task was scheduled during the call to the
  319. * hook. Clear the input idle flag to ensure
  320. * that the hook won't be called again if there
  321. * are no tasks ready to run.
  322. */
  323. pwpi->ptiScheduled = NULL;
  324. fInputIdle = FALSE;
  325. goto rescan;
  326. }
  327. }
  328. }
  329. /*
  330. * If there is a task ready, wake it up.
  331. */
  332. if (pwpi->ptiScheduled != NULL) {
  333. KeSetEvent(pwpi->ptiScheduled->pEventQueueServer,
  334. EVENT_INCREMENT,
  335. FALSE
  336. );
  337. /*
  338. * There is no one to wake up, but we may have to wake
  339. * wowexec to service virtual hardware interrupts
  340. */
  341. } else if (ppi->W32PF_Flags & W32PF_WAKEWOWEXEC) {
  342. if (pwpi->hEventWowExecClient == hEvent) {
  343. pwpi->ptiScheduled = pti;
  344. ppi->W32PF_Flags &= ~W32PF_WAKEWOWEXEC;
  345. InsertTask(ppi, pti->ptdb);
  346. EnterWowCritSect(pti, pwpi);
  347. UserAssert(pti == pwpi->ptiScheduled);
  348. return TRUE;
  349. } else {
  350. KeSetEvent(pwpi->pEventWowExec, EVENT_INCREMENT, FALSE);
  351. }
  352. } else if ((pti->TIF_flags & TIF_SHAREDWOW) && !bWaitedAtLeastOnce) {
  353. if (pwpi->hEventWowExecClient == hEvent) {
  354. /*
  355. * We have to call zzzWakeInputIdle only if this will
  356. * awake WowExec's thread and not other thread. Bug 44060.
  357. */
  358. zzzWakeInputIdle(pti); // need to DeferWinEventNotify() ?? IANJA ??
  359. }
  360. }
  361. }
  362. /*
  363. * Return if we are a 32 bit thread, or if we were called by
  364. * MsgWaitForMultiple to exit the wow scheduler.
  365. */
  366. if (!(pti->TIF_flags & TIF_16BIT)) {
  367. return FALSE;
  368. } else if (hEvent == HEVENT_REMOVEME) {
  369. InsertTask(ppi, pti->ptdb);
  370. KeClearEvent(pti->pEventQueueServer);
  371. return FALSE;
  372. }
  373. if (pti->apEvent == NULL) {
  374. pti->apEvent = UserAllocPoolNonPaged(POLL_EVENT_CNT * sizeof(PKEVENT), TAG_EVENT);
  375. if (pti->apEvent == NULL)
  376. return FALSE;
  377. }
  378. /*
  379. * Wait for input to this thread.
  380. */
  381. pti->apEvent[IEV_TASK] = pti->pEventQueueServer;
  382. /*
  383. * Add the WowExec, handle for virtual hw interrupts
  384. */
  385. if (pwpi->hEventWowExecClient == hEvent) {
  386. pti->apEvent[IEV_WOWEXEC] = pwpi->pEventWowExec;
  387. nHandles = 2;
  388. } else {
  389. nHandles = 1;
  390. }
  391. if (pti->TIF_flags & TIF_MEOW) {
  392. xxxClientWOWTask16SchedNotify(WOWSCHNOTIFY_WAIT, 0);
  393. }
  394. LeaveCrit();
  395. Status = KeWaitForMultipleObjects(nHandles,
  396. &pti->apEvent[IEV_TASK],
  397. WaitAny,
  398. WrUserRequest,
  399. UserMode,
  400. TRUE,
  401. NULL,
  402. NULL);
  403. EnterCrit();
  404. if (pti->TIF_flags & TIF_MEOW) {
  405. xxxClientWOWTask16SchedNotify(WOWSCHNOTIFY_RUN, 0);
  406. }
  407. bWaitedAtLeastOnce = TRUE;
  408. // remember if we woke up for wowexec
  409. if (Status == STATUS_WAIT_1) {
  410. ppi->W32PF_Flags |= W32PF_WAKEWOWEXEC;
  411. } else if (Status == STATUS_USER_APC) {
  412. /*
  413. * ClientDeliverUserApc() delivers User-mode APCs by calling back
  414. * to the client and immediately returning without doing anything:
  415. * KeUserModeCallback will automatically deliver any pending APCs.
  416. */
  417. ClientDeliverUserApc();
  418. }
  419. } while (pwpi->ptiScheduled != pti);
  420. /*
  421. * We are the Active Task, reduce number of Events
  422. * Place ourselves at the far end of tasks in the same priority
  423. * so that next time we sleep someone else will run.
  424. */
  425. pti->ptdb->nEvents--;
  426. gpsi->nEvents--;
  427. UserAssert(gpsi->nEvents >= 0);
  428. InsertTask(ppi, pti->ptdb);
  429. ppi->W32PF_Flags &= ~W32PF_WAKEWOWEXEC;
  430. EnterWowCritSect(pti, pwpi);
  431. UserAssert(pti == pwpi->ptiScheduled);
  432. return FALSE;
  433. }
  434. /***************************************************************************\
  435. * xxxUserYield
  436. *
  437. * Does exactly what Win3.1 UserYield does.
  438. *
  439. * History:
  440. * 10-19-92 Scottlu Created.
  441. \***************************************************************************/
  442. BOOL xxxUserYield(
  443. PTHREADINFO pti)
  444. {
  445. PPROCESSINFO ppi = pti->ppi;
  446. /*
  447. * Deal with any pending messages. Only call it this first time if
  448. * this is the current running 16 bit app. In the case when starting
  449. * up a 16 bit app, the starter calls UserYield() to yield to the new
  450. * task, but at this time ppi->ptiScheduled is set to the new task.
  451. * Receiving messages at this point would be bad!
  452. */
  453. if (pti->TIF_flags & TIF_16BIT) {
  454. if (pti == ppi->pwpi->ptiScheduled) {
  455. xxxReceiveMessages(pti);
  456. }
  457. } else {
  458. xxxReceiveMessages(pti);
  459. }
  460. /*
  461. * If we are a 16 bit task
  462. * Mark our task so it comes back some time. Also, remove it and
  463. * re-add it to the list so that we are the last task of our priority
  464. * to run.
  465. */
  466. if ((pti->TIF_flags & TIF_16BIT) && (pti->ptdb != NULL)) {
  467. if (pti->ptdb->nEvents == 0) {
  468. pti->ptdb->nEvents++;
  469. gpsi->nEvents++;
  470. }
  471. InsertTask(ppi, pti->ptdb);
  472. /*
  473. * Sleep. Return right away if there are no higher priority tasks
  474. * in need of running.
  475. */
  476. xxxSleepTask(TRUE, NULL);
  477. /*
  478. * Deal with any that arrived since we weren't executing.
  479. */
  480. xxxReceiveMessages(pti);
  481. }
  482. return TRUE;
  483. }
  484. /***************************************************************************\
  485. * DirectedScheduleTask
  486. *
  487. * History:
  488. * 25-Jun-1992 mikeke Created.
  489. \***************************************************************************/
  490. VOID DirectedScheduleTask(
  491. PTHREADINFO ptiOld,
  492. PTHREADINFO ptiNew,
  493. BOOL bSendMsg,
  494. PSMS psms)
  495. {
  496. PWOWPROCESSINFO pwpiOld;
  497. PWOWPROCESSINFO pwpiNew;
  498. CheckCritIn();
  499. pwpiOld = ptiOld->ppi->pwpi;
  500. pwpiNew = ptiNew->ppi->pwpi;
  501. /*
  502. * If old task is 16 bit, reinsert the task in its wow scheduler list
  503. * so that it is lowest in priority. Note that ptiOld is always the
  504. * same as pwpiOld->ptiScheduled except when called from ReceiverDied.
  505. */
  506. if (ptiOld->TIF_flags & TIF_16BIT) {
  507. if (pwpiOld->ptiScheduled == ptiOld) {
  508. ptiOld->ptdb->nEvents++;
  509. gpsi->nEvents++;
  510. InsertTask(ptiOld->ppi, ptiOld->ptdb);
  511. }
  512. // Update the Send\Recv counts for interprocess scheduling in SleepTask
  513. if (pwpiOld != pwpiNew || !(ptiNew->TIF_flags & TIF_16BIT)) {
  514. if (bSendMsg) {
  515. pwpiOld->nSendLock++;
  516. psms->flags |= SMF_WOWSEND;
  517. }
  518. else if (pwpiOld->nRecvLock && psms->flags & SMF_WOWRECEIVE) {
  519. pwpiOld->nRecvLock--;
  520. psms->flags &= ~SMF_WOWRECEIVE;
  521. }
  522. }
  523. }
  524. /*
  525. * If the new task is 16 bit, reinsert into the wow scheduler list
  526. * so that it will run, if its a sendmsg raise priority of the receiver.
  527. * If its a reply and the sender is waiting for this psms or the sender
  528. * has a message to reply to raise priority of the sender.
  529. */
  530. if (ptiNew->TIF_flags & TIF_16BIT) {
  531. BOOL bRaisePriority;
  532. ptiNew->ptdb->nEvents++;
  533. gpsi->nEvents++;
  534. bRaisePriority = bSendMsg || psms == ptiNew->psmsSent;
  535. if (bRaisePriority) {
  536. ptiNew->ptdb->nPriority--;
  537. }
  538. InsertTask(ptiNew->ppi, ptiNew->ptdb);
  539. if (bRaisePriority) {
  540. ptiNew->ptdb->nPriority++;
  541. WakeWowTask(ptiNew);
  542. }
  543. // Update the Send\Recv counts for interprocess scheduling in SleepTask
  544. if (pwpiOld != pwpiNew || !(ptiOld->TIF_flags & TIF_16BIT)) {
  545. if (bSendMsg) {
  546. pwpiNew->nRecvLock++;
  547. psms->flags |= SMF_WOWRECEIVE;
  548. }
  549. else if (pwpiNew->nSendLock && psms->flags & SMF_WOWSEND) {
  550. pwpiNew->nSendLock--;
  551. psms->flags &= ~SMF_WOWSEND;
  552. }
  553. }
  554. }
  555. }
  556. /***************************************************************************\
  557. * xxxDirectedYield
  558. *
  559. * History:
  560. * 09-17-92 JimA Created.
  561. \***************************************************************************/
  562. VOID xxxDirectedYield(
  563. DWORD dwThreadId)
  564. {
  565. PTHREADINFO ptiOld;
  566. PTHREADINFO ptiNew;
  567. CheckCritIn();
  568. ptiOld = PtiCurrent();
  569. if (!(ptiOld->TIF_flags & TIF_16BIT) || !ptiOld->ppi->pwpi) {
  570. RIPMSG0(RIP_ERROR, "DirectedYield called from 32 bit thread!");
  571. return;
  572. }
  573. /*
  574. * If the old task is 16 bit, reinsert the task in its wow
  575. * scheduler list so that it is lowest in priority.
  576. */
  577. ptiOld->ptdb->nEvents++;
  578. gpsi->nEvents++;
  579. InsertTask(ptiOld->ppi, ptiOld->ptdb);
  580. /*
  581. * -1 supports Win 3.1 OldYield mechanics
  582. */
  583. if (dwThreadId != DY_OLDYIELD) {
  584. ptiNew = PtiFromThreadId(dwThreadId);
  585. if ((ptiNew == NULL) || (ptiNew->ppi != ptiOld->ppi)) {
  586. RIPMSG0(RIP_ERROR, "DirectedYield called from different process or invalid thread id!");
  587. return;
  588. }
  589. if (ptiNew->TIF_flags & TIF_16BIT) {
  590. ptiNew->ptdb->nEvents++;
  591. gpsi->nEvents++;
  592. ptiNew->ptdb->nPriority--;
  593. InsertTask(ptiNew->ppi, ptiNew->ptdb);
  594. ptiNew->ptdb->nPriority++;
  595. }
  596. }
  597. xxxSleepTask(TRUE, NULL);
  598. }