utimer.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. utimer.c
  9. Abstract:
  10. This module implements user mode timer support.
  11. Author:
  12. Evan Green 11-Aug-2013
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "psp.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. #define PROCESS_TIMER_ALLOCATION_TAG 0x6D547350 // 'mTsP'
  25. //
  26. // ------------------------------------------------------ Data Type Definitions
  27. //
  28. /*++
  29. Structure Description:
  30. This structure defines a user mode timer.
  31. Members:
  32. ListEntry - Stores pointers to the next and previous timers in the process
  33. list.
  34. ReferenceCount - Stores the reference count on the timer.
  35. Process - Stores a pointer to the process that owns this timer.
  36. Thread - Stores an optional pointer to the thread that is to be signaled
  37. when the timer expires. If NULL, then the process is signaled.
  38. TimerNumber - Stores the timer's identifying number.
  39. DueTime - Stores the due time of the timer.
  40. Interval - Stores the periodic interval of the timer.
  41. ExpirationCount - Stores the number of timer expirations that have occurred
  42. since the last work item ran.
  43. OverflowCount - Stores the number of overflows that have occurred since the
  44. last time the caller asked.
  45. Timer - Stores a pointer to the timer backing this user mode timer.
  46. Dpc - Stores a pointer to the DPC that runs when the timer fires.
  47. WorkItem - Stores a pointer to the work item that's queued when the DPC
  48. runs.
  49. SignalQueueEntry - Stores the signal queue entry that gets queued when the
  50. timer expires.
  51. --*/
  52. typedef struct _PROCESS_TIMER {
  53. LIST_ENTRY ListEntry;
  54. ULONG ReferenceCount;
  55. PKPROCESS Process;
  56. PKTHREAD Thread;
  57. LONG TimerNumber;
  58. ULONGLONG DueTime;
  59. ULONGLONG Interval;
  60. ULONG ExpirationCount;
  61. ULONG OverflowCount;
  62. PKTIMER Timer;
  63. PDPC Dpc;
  64. PWORK_ITEM WorkItem;
  65. SIGNAL_QUEUE_ENTRY SignalQueueEntry;
  66. } PROCESS_TIMER, *PPROCESS_TIMER;
  67. //
  68. // ----------------------------------------------- Internal Function Prototypes
  69. //
  70. KSTATUS
  71. PspCreateTimer (
  72. PKPROCESS Process,
  73. PKTHREAD Thread,
  74. PPROCESS_TIMER *Timer
  75. );
  76. KSTATUS
  77. PspSetTimer (
  78. PKPROCESS Process,
  79. PPROCESS_TIMER Timer,
  80. PULONGLONG DueTime,
  81. PULONGLONG Period
  82. );
  83. KSTATUS
  84. PspCreateProcessTimer (
  85. PKPROCESS Process,
  86. PKTHREAD Thread,
  87. PPROCESS_TIMER *Timer
  88. );
  89. VOID
  90. PspProcessTimerAddReference (
  91. PPROCESS_TIMER Timer
  92. );
  93. VOID
  94. PspProcessTimerReleaseReference (
  95. PPROCESS_TIMER Timer
  96. );
  97. VOID
  98. PspDestroyProcessTimer (
  99. PPROCESS_TIMER Timer
  100. );
  101. VOID
  102. PspFlushProcessTimer (
  103. PKPROCESS Process,
  104. PPROCESS_TIMER Timer
  105. );
  106. VOID
  107. PspProcessTimerDpcRoutine (
  108. PDPC Dpc
  109. );
  110. VOID
  111. PspProcessTimerWorkRoutine (
  112. PVOID Parameter
  113. );
  114. VOID
  115. PspProcessTimerSignalCompletion (
  116. PSIGNAL_QUEUE_ENTRY SignalQueueEntry
  117. );
  118. VOID
  119. PspExpireRuntimeTimer (
  120. PKTHREAD Thread,
  121. PRUNTIME_TIMER Timer,
  122. ULONG Signal,
  123. ULONGLONG CurrentTime
  124. );
  125. //
  126. // -------------------------------------------------------------------- Globals
  127. //
  128. //
  129. // ------------------------------------------------------------------ Functions
  130. //
  131. INTN
  132. PsSysQueryTimeCounter (
  133. PVOID SystemCallParameter
  134. )
  135. /*++
  136. Routine Description:
  137. This routine implements the system call for getting the current time
  138. counter value.
  139. Arguments:
  140. SystemCallParameter - Supplies a pointer to the parameters supplied with
  141. the system call. This structure will be a stack-local copy of the
  142. actual parameters passed from user-mode.
  143. Return Value:
  144. STATUS_SUCCESS or positive integer on success.
  145. Error status code on failure.
  146. --*/
  147. {
  148. PSYSTEM_CALL_QUERY_TIME_COUNTER Parameters;
  149. Parameters = (PSYSTEM_CALL_QUERY_TIME_COUNTER)SystemCallParameter;
  150. Parameters->Value = HlQueryTimeCounter();
  151. return STATUS_SUCCESS;
  152. }
  153. INTN
  154. PsSysTimerControl (
  155. PVOID SystemCallParameter
  156. )
  157. /*++
  158. Routine Description:
  159. This routine performs timer control operations.
  160. Arguments:
  161. SystemCallParameter - Supplies a pointer to the parameters supplied with
  162. the system call. This structure will be a stack-local copy of the
  163. actual parameters passed from user-mode.
  164. Return Value:
  165. STATUS_SUCCESS or positive integer on success.
  166. Error status code on failure.
  167. --*/
  168. {
  169. PLIST_ENTRY CurrentEntry;
  170. PPROCESS_TIMER CurrentTimer;
  171. BOOL LockHeld;
  172. PSYSTEM_CALL_TIMER_CONTROL Parameters;
  173. PKPROCESS Process;
  174. KSTATUS Status;
  175. PKTHREAD Thread;
  176. PPROCESS_TIMER Timer;
  177. LockHeld = FALSE;
  178. Parameters = (PSYSTEM_CALL_TIMER_CONTROL)SystemCallParameter;
  179. Process = PsGetCurrentProcess();
  180. ASSERT(Process != PsGetKernelProcess());
  181. Timer = NULL;
  182. Thread = NULL;
  183. //
  184. // If it's not a create operation, find the timer being referenced.
  185. //
  186. if (Parameters->Operation != TimerOperationCreateTimer) {
  187. KeAcquireQueuedLock(Process->QueuedLock);
  188. LockHeld = TRUE;
  189. CurrentEntry = Process->TimerList.Next;
  190. while (CurrentEntry != &(Process->TimerList)) {
  191. CurrentTimer = LIST_VALUE(CurrentEntry, PROCESS_TIMER, ListEntry);
  192. if (CurrentTimer->TimerNumber == Parameters->TimerNumber) {
  193. Timer = CurrentTimer;
  194. break;
  195. }
  196. CurrentEntry = CurrentEntry->Next;
  197. }
  198. if (Timer == NULL) {
  199. Status = STATUS_INVALID_HANDLE;
  200. goto SysTimerControlEnd;
  201. }
  202. }
  203. Status = STATUS_SUCCESS;
  204. switch (Parameters->Operation) {
  205. //
  206. // Create a new processs timer and add it to the list.
  207. //
  208. case TimerOperationCreateTimer:
  209. //
  210. // If a thread is to be signaled, validate that the thread belongs to
  211. // the current process.
  212. //
  213. if ((Parameters->Flags & TIMER_CONTROL_FLAG_SIGNAL_THREAD) != 0) {
  214. Thread = PspGetThreadById(Process, Parameters->ThreadId);
  215. if (Thread == NULL) {
  216. Status = STATUS_INVALID_PARAMETER;
  217. goto SysTimerControlEnd;
  218. }
  219. }
  220. Status = PspCreateTimer(Process, Thread, &Timer);
  221. if (!KSUCCESS(Status)) {
  222. goto SysTimerControlEnd;
  223. }
  224. Timer->SignalQueueEntry.Parameters.SignalNumber =
  225. Parameters->SignalNumber;
  226. Timer->SignalQueueEntry.Parameters.SignalCode = SIGNAL_CODE_TIMER;
  227. if ((Parameters->Flags & TIMER_CONTROL_FLAG_USE_TIMER_NUMBER) != 0) {
  228. Timer->SignalQueueEntry.Parameters.Parameter = Timer->TimerNumber;
  229. } else {
  230. Timer->SignalQueueEntry.Parameters.Parameter =
  231. Parameters->SignalValue;
  232. }
  233. Parameters->TimerNumber = Timer->TimerNumber;
  234. break;
  235. //
  236. // Delete an existing process timer.
  237. //
  238. case TimerOperationDeleteTimer:
  239. LIST_REMOVE(&(Timer->ListEntry));
  240. KeReleaseQueuedLock(Process->QueuedLock);
  241. LockHeld = FALSE;
  242. PspFlushProcessTimer(Process, Timer);
  243. PspProcessTimerReleaseReference(Timer);
  244. break;
  245. //
  246. // Get timer information, including the next due time and overflow count.
  247. //
  248. case TimerOperationGetTimer:
  249. Parameters->TimerInformation.DueTime = KeGetTimerDueTime(Timer->Timer);
  250. Parameters->TimerInformation.Period = Timer->Interval;
  251. Parameters->TimerInformation.OverflowCount = Timer->OverflowCount;
  252. break;
  253. //
  254. // Arm or disarm the timer. Save and return the original information.
  255. //
  256. case TimerOperationSetTimer:
  257. Parameters->TimerInformation.OverflowCount = 0;
  258. Status = PspSetTimer(Process,
  259. Timer,
  260. &(Parameters->TimerInformation.DueTime),
  261. &(Parameters->TimerInformation.Period));
  262. if (!KSUCCESS(Status)) {
  263. goto SysTimerControlEnd;
  264. }
  265. break;
  266. default:
  267. ASSERT(FALSE);
  268. Status = STATUS_INVALID_PARAMETER;
  269. goto SysTimerControlEnd;
  270. }
  271. SysTimerControlEnd:
  272. if (LockHeld != FALSE) {
  273. KeReleaseQueuedLock(Process->QueuedLock);
  274. }
  275. if (Thread != NULL) {
  276. ObReleaseReference(Thread);
  277. }
  278. return Status;
  279. }
  280. INTN
  281. PsSysSetITimer (
  282. PVOID SystemCallParameter
  283. )
  284. /*++
  285. Routine Description:
  286. This routine performs gets or sets a thread interval timer.
  287. Arguments:
  288. SystemCallParameter - Supplies a pointer to the parameters supplied with
  289. the system call. This structure will be a stack-local copy of the
  290. actual parameters passed from user-mode.
  291. Return Value:
  292. STATUS_SUCCESS or positive integer on success.
  293. Error status code on failure.
  294. --*/
  295. {
  296. ULONGLONG CurrentCycles;
  297. ULONGLONG CurrentTime;
  298. ULONGLONG DueTime;
  299. ULONGLONG OldPeriod;
  300. PKPROCESS Process;
  301. PPROCESS_TIMER RealTimer;
  302. PSYSTEM_CALL_SET_ITIMER Request;
  303. KSTATUS Status;
  304. PKTHREAD Thread;
  305. RESOURCE_USAGE Usage;
  306. PRUNTIME_TIMER UserTimer;
  307. Thread = KeGetCurrentThread();
  308. Process = Thread->OwningProcess;
  309. Request = SystemCallParameter;
  310. if (Request->Type >= ITimerTypeCount) {
  311. Status = STATUS_INVALID_PARAMETER;
  312. goto SysSetITimerEnd;
  313. }
  314. if (Request->Set == FALSE) {
  315. switch (Request->Type) {
  316. case ITimerReal:
  317. RealTimer = Thread->RealTimer;
  318. if (RealTimer == NULL) {
  319. Request->DueTime = 0;
  320. Request->Period = 0;
  321. break;
  322. }
  323. Request->DueTime = KeGetTimerDueTime(RealTimer->Timer);
  324. CurrentTime = HlQueryTimeCounter();
  325. if (Request->DueTime > CurrentTime) {
  326. Request->DueTime -= CurrentTime;
  327. } else {
  328. Request->DueTime = 0;
  329. }
  330. Request->Period = RealTimer->Interval;
  331. break;
  332. case ITimerVirtual:
  333. case ITimerProfile:
  334. PspGetThreadResourceUsage(Thread, &Usage);
  335. UserTimer = &(Thread->UserTimer);
  336. CurrentCycles = Usage.UserCycles;
  337. if (Request->Type == ITimerProfile) {
  338. CurrentCycles += Usage.KernelCycles;
  339. UserTimer = &(Thread->ProfileTimer);
  340. }
  341. Request->Period = UserTimer->Period;
  342. Request->DueTime = UserTimer->DueTime;
  343. if (Request->DueTime > CurrentCycles) {
  344. Request->DueTime -= CurrentCycles;
  345. } else {
  346. Request->DueTime = 0;
  347. }
  348. break;
  349. default:
  350. ASSERT(FALSE);
  351. break;
  352. }
  353. Status = STATUS_SUCCESS;
  354. goto SysSetITimerEnd;
  355. }
  356. //
  357. // This is a set timer request.
  358. //
  359. switch (Request->Type) {
  360. case ITimerReal:
  361. if (Thread->RealTimer == NULL) {
  362. Status = PspCreateTimer(Process, NULL, &RealTimer);
  363. if (!KSUCCESS(Status)) {
  364. goto SysSetITimerEnd;
  365. }
  366. RealTimer->SignalQueueEntry.Parameters.SignalNumber = SIGNAL_TIMER;
  367. RealTimer->SignalQueueEntry.Parameters.SignalCode =
  368. SIGNAL_CODE_TIMER;
  369. Thread->RealTimer = RealTimer;
  370. }
  371. //
  372. // Set the new real timer. The due time in the request is always
  373. // relative, so convert it to absolute and back.
  374. //
  375. CurrentTime = HlQueryTimeCounter();
  376. KeAcquireQueuedLock(Process->QueuedLock);
  377. DueTime = Request->DueTime;
  378. if (DueTime != 0) {
  379. DueTime += CurrentTime;
  380. }
  381. Status = PspSetTimer(Process,
  382. Thread->RealTimer,
  383. &DueTime,
  384. &(Request->Period));
  385. KeReleaseQueuedLock(Process->QueuedLock);
  386. if (!KSUCCESS(Status)) {
  387. goto SysSetITimerEnd;
  388. }
  389. if (DueTime > CurrentTime) {
  390. DueTime -= CurrentTime;
  391. } else {
  392. DueTime = 0;
  393. }
  394. break;
  395. case ITimerVirtual:
  396. case ITimerProfile:
  397. PspGetThreadResourceUsage(Thread, &Usage);
  398. UserTimer = &(Thread->UserTimer);
  399. CurrentCycles = Usage.UserCycles;
  400. if (Request->Type == ITimerProfile) {
  401. CurrentCycles += Usage.KernelCycles;
  402. UserTimer = &(Thread->ProfileTimer);
  403. }
  404. DueTime = Request->DueTime;
  405. if (DueTime != 0) {
  406. DueTime += CurrentCycles;
  407. }
  408. OldPeriod = UserTimer->Period;
  409. Request->DueTime = UserTimer->Period;
  410. if (Request->DueTime > CurrentCycles) {
  411. Request->DueTime -= CurrentCycles;
  412. } else {
  413. Request->DueTime = 0;
  414. }
  415. UserTimer->DueTime = DueTime;
  416. UserTimer->Period = Request->Period;
  417. Request->Period = OldPeriod;
  418. break;
  419. default:
  420. ASSERT(FALSE);
  421. Status = STATUS_INVALID_PARAMETER;
  422. goto SysSetITimerEnd;
  423. }
  424. Status = STATUS_SUCCESS;
  425. SysSetITimerEnd:
  426. return Status;
  427. }
  428. VOID
  429. PsEvaluateRuntimeTimers (
  430. PKTHREAD Thread
  431. )
  432. /*++
  433. Routine Description:
  434. This routine checks the runtime timers for expiration on the current thread.
  435. Arguments:
  436. Thread - Supplies a pointer to the current thread.
  437. Return Value:
  438. None.
  439. --*/
  440. {
  441. ULONGLONG CurrentCycles;
  442. RESOURCE_USAGE Usage;
  443. //
  444. // If they're both zero, return.
  445. //
  446. if ((Thread->UserTimer.DueTime | Thread->ProfileTimer.DueTime) == 0) {
  447. return;
  448. }
  449. //
  450. // Potentially expire the user timer. This read can never tear since user
  451. // mode can't sneak in and run a bit more.
  452. //
  453. if ((Thread->UserTimer.DueTime != 0) &&
  454. (Thread->ResourceUsage.UserCycles >= Thread->UserTimer.DueTime)) {
  455. PspExpireRuntimeTimer(Thread,
  456. &(Thread->UserTimer),
  457. SIGNAL_EXECUTION_TIMER_EXPIRED,
  458. Thread->ResourceUsage.UserCycles);
  459. }
  460. //
  461. // Potentially expire the profiling timer. The kernel time might tear, so
  462. // do a torn read and if it succeeds, do a legit read. If the torn read
  463. // results in a false negative then the timer will be a little late, but
  464. // will expire on the next check.
  465. //
  466. if ((Thread->ProfileTimer.DueTime != 0) &&
  467. ((Thread->ResourceUsage.UserCycles +
  468. Thread->ResourceUsage.KernelCycles) >=
  469. Thread->ProfileTimer.DueTime)) {
  470. PspGetThreadResourceUsage(Thread, &Usage);
  471. CurrentCycles = Usage.UserCycles + Usage.KernelCycles;
  472. if (CurrentCycles >= Thread->ProfileTimer.DueTime) {
  473. PspExpireRuntimeTimer(Thread,
  474. &(Thread->ProfileTimer),
  475. SIGNAL_PROFILE_TIMER,
  476. CurrentCycles);
  477. }
  478. }
  479. return;
  480. }
  481. VOID
  482. PspDestroyProcessTimers (
  483. PKPROCESS Process
  484. )
  485. /*++
  486. Routine Description:
  487. This routine cleans up any timers a process may have. This routine assumes
  488. the process lock is already held.
  489. Arguments:
  490. Process - Supplies a pointer to the process.
  491. Return Value:
  492. None.
  493. --*/
  494. {
  495. PPROCESS_TIMER Timer;
  496. while (LIST_EMPTY(&(Process->TimerList)) == FALSE) {
  497. Timer = LIST_VALUE(Process->TimerList.Next, PROCESS_TIMER, ListEntry);
  498. LIST_REMOVE(&(Timer->ListEntry));
  499. //
  500. // Cancel the timer and flush the DPC to ensure that the reference
  501. // count is up to date. Then release the reference. This will either
  502. // clean up the object right away or the work item will run on its
  503. // own time.
  504. //
  505. KeCancelTimer(Timer->Timer);
  506. if (!KSUCCESS(KeCancelDpc(Timer->Dpc))) {
  507. KeFlushDpc(Timer->Dpc);
  508. }
  509. PspProcessTimerReleaseReference(Timer);
  510. }
  511. return;
  512. }
  513. //
  514. // --------------------------------------------------------- Internal Functions
  515. //
  516. KSTATUS
  517. PspCreateTimer (
  518. PKPROCESS Process,
  519. PKTHREAD Thread,
  520. PPROCESS_TIMER *Timer
  521. )
  522. /*++
  523. Routine Description:
  524. This routine attempts to create and add a new process timer.
  525. Arguments:
  526. Process - Supplies a pointer to the process that owns the timer.
  527. Thread - Supplies an optional pointer to the thread to be signaled when the
  528. timer expires.
  529. Timer - Supplies a pointer where a pointer to the new timer is returned on
  530. success.
  531. Return Value:
  532. Status code.
  533. --*/
  534. {
  535. PPROCESS_TIMER PreviousTimer;
  536. PPROCESS_TIMER ProcessTimer;
  537. KSTATUS Status;
  538. Status = PspCreateProcessTimer(Process, Thread, &ProcessTimer);
  539. if (!KSUCCESS(Status)) {
  540. goto CreateTimerEnd;
  541. }
  542. ProcessTimer->SignalQueueEntry.Parameters.SignalCode = SIGNAL_CODE_TIMER;
  543. //
  544. // Insert this timer in the process. Assign the timer the ID of the
  545. // last timer in the list plus one.
  546. //
  547. KeAcquireQueuedLock(Process->QueuedLock);
  548. if (LIST_EMPTY(&(Process->TimerList)) != FALSE) {
  549. ProcessTimer->TimerNumber = 1;
  550. } else {
  551. PreviousTimer = LIST_VALUE(Process->TimerList.Previous,
  552. PROCESS_TIMER,
  553. ListEntry);
  554. ProcessTimer->TimerNumber = PreviousTimer->TimerNumber + 1;
  555. }
  556. INSERT_BEFORE(&(ProcessTimer->ListEntry), &(Process->TimerList));
  557. KeReleaseQueuedLock(Process->QueuedLock);
  558. CreateTimerEnd:
  559. *Timer = ProcessTimer;
  560. return Status;
  561. }
  562. KSTATUS
  563. PspSetTimer (
  564. PKPROCESS Process,
  565. PPROCESS_TIMER Timer,
  566. PULONGLONG DueTime,
  567. PULONGLONG Period
  568. )
  569. /*++
  570. Routine Description:
  571. This routine attempts to arm a process timer. This routien assumes the
  572. process lock is already held.
  573. Arguments:
  574. Process - Supplies a pointer to the process that owns the timer.
  575. Timer - Supplies a pointer where a pointer to the new timer is returned on
  576. success.
  577. DueTime - Supplies the new due time in time counter ticks. Returns the
  578. previous due time.
  579. Period - Supplies the new interval in time counter ticks. Returns the
  580. previous interval.
  581. Return Value:
  582. Status code.
  583. --*/
  584. {
  585. ULONGLONG OriginalDueTime;
  586. ULONGLONG OriginalPeriod;
  587. KSTATUS Status;
  588. ASSERT(KeIsQueuedLockHeld(Process->QueuedLock) != FALSE);
  589. OriginalDueTime = KeGetTimerDueTime(Timer->Timer);
  590. OriginalPeriod = Timer->Interval;
  591. if (Timer->DueTime != 0) {
  592. KeCancelTimer(Timer->Timer);
  593. }
  594. Timer->DueTime = *DueTime;
  595. Timer->Interval = *Period;
  596. if (Timer->DueTime != 0) {
  597. Status = KeQueueTimer(Timer->Timer,
  598. TimerQueueSoftWake,
  599. Timer->DueTime,
  600. Timer->Interval,
  601. 0,
  602. Timer->Dpc);
  603. if (!KSUCCESS(Status)) {
  604. goto SetTimerEnd;
  605. }
  606. }
  607. Status = STATUS_SUCCESS;
  608. SetTimerEnd:
  609. *DueTime = OriginalDueTime;
  610. *Period = OriginalPeriod;
  611. return Status;
  612. }
  613. KSTATUS
  614. PspCreateProcessTimer (
  615. PKPROCESS Process,
  616. PKTHREAD Thread,
  617. PPROCESS_TIMER *Timer
  618. )
  619. /*++
  620. Routine Description:
  621. This routine attempts to create a new process timer.
  622. Arguments:
  623. Process - Supplies a pointer to the process that owns the timer.
  624. Thread - Supplies an optional pointer to the thread to be signaled when the
  625. timer expires.
  626. Timer - Supplies a pointer where a pointer to the new timer is returned on
  627. success.
  628. Return Value:
  629. STATUS_SUCCESS always.
  630. --*/
  631. {
  632. PPROCESS_TIMER NewTimer;
  633. KSTATUS Status;
  634. ASSERT(KeGetRunLevel() == RunLevelLow);
  635. Status = STATUS_INSUFFICIENT_RESOURCES;
  636. NewTimer = MmAllocateNonPagedPool(sizeof(PROCESS_TIMER),
  637. PROCESS_TIMER_ALLOCATION_TAG);
  638. if (NewTimer == NULL) {
  639. goto CreateProcessTimerEnd;
  640. }
  641. RtlZeroMemory(NewTimer, sizeof(PROCESS_TIMER));
  642. NewTimer->Process = Process;
  643. NewTimer->Thread = Thread;
  644. NewTimer->ReferenceCount = 1;
  645. NewTimer->Timer = KeCreateTimer(PROCESS_TIMER_ALLOCATION_TAG);
  646. if (NewTimer->Timer == NULL) {
  647. goto CreateProcessTimerEnd;
  648. }
  649. NewTimer->Dpc = KeCreateDpc(PspProcessTimerDpcRoutine, NewTimer);
  650. if (NewTimer->Dpc == NULL) {
  651. goto CreateProcessTimerEnd;
  652. }
  653. NewTimer->WorkItem = KeCreateWorkItem(NULL,
  654. WorkPriorityNormal,
  655. PspProcessTimerWorkRoutine,
  656. NewTimer,
  657. PROCESS_TIMER_ALLOCATION_TAG);
  658. if (NewTimer->WorkItem == NULL) {
  659. goto CreateProcessTimerEnd;
  660. }
  661. NewTimer->SignalQueueEntry.CompletionRoutine =
  662. PspProcessTimerSignalCompletion;
  663. //
  664. // Take a reference on the process to avoid a situation where the
  665. // process is destroyed before the work item gets around to running. Do the
  666. // same for the thread if it is present.
  667. //
  668. ObAddReference(Process);
  669. if (Thread != NULL) {
  670. ObAddReference(Thread);
  671. }
  672. Status = STATUS_SUCCESS;
  673. CreateProcessTimerEnd:
  674. if (!KSUCCESS(Status)) {
  675. if (NewTimer != NULL) {
  676. if (NewTimer->Timer != NULL) {
  677. KeDestroyTimer(NewTimer->Timer);
  678. }
  679. if (NewTimer->Dpc != NULL) {
  680. KeDestroyDpc(NewTimer->Dpc);
  681. }
  682. if (NewTimer->WorkItem != NULL) {
  683. KeDestroyWorkItem(NewTimer->WorkItem);
  684. }
  685. MmFreeNonPagedPool(NewTimer);
  686. NewTimer = NULL;
  687. }
  688. }
  689. *Timer = NewTimer;
  690. return Status;
  691. }
  692. VOID
  693. PspProcessTimerAddReference (
  694. PPROCESS_TIMER Timer
  695. )
  696. /*++
  697. Routine Description:
  698. This routine adds a reference to a process timer.
  699. Arguments:
  700. Timer - Supplies a pointer to the timer.
  701. Return Value:
  702. None.
  703. --*/
  704. {
  705. RtlAtomicAdd32(&(Timer->ReferenceCount), 1);
  706. return;
  707. }
  708. VOID
  709. PspProcessTimerReleaseReference (
  710. PPROCESS_TIMER Timer
  711. )
  712. /*++
  713. Routine Description:
  714. This routine releases a reference on a process timer.
  715. Arguments:
  716. Timer - Supplies a pointer to the timer.
  717. Return Value:
  718. None.
  719. --*/
  720. {
  721. if (RtlAtomicAdd32(&(Timer->ReferenceCount), -1) == 1) {
  722. PspDestroyProcessTimer(Timer);
  723. }
  724. return;
  725. }
  726. VOID
  727. PspDestroyProcessTimer (
  728. PPROCESS_TIMER Timer
  729. )
  730. /*++
  731. Routine Description:
  732. This routine destroys a process timer.
  733. Arguments:
  734. Timer - Supplies a pointer to the timer to destroy.
  735. Return Value:
  736. None.
  737. --*/
  738. {
  739. KeDestroyTimer(Timer->Timer);
  740. KeDestroyDpc(Timer->Dpc);
  741. KeDestroyWorkItem(Timer->WorkItem);
  742. ObReleaseReference(Timer->Process);
  743. if (Timer->Thread != NULL) {
  744. ObReleaseReference(Timer->Thread);
  745. }
  746. MmFreeNonPagedPool(Timer);
  747. return;
  748. }
  749. VOID
  750. PspFlushProcessTimer (
  751. PKPROCESS Process,
  752. PPROCESS_TIMER Timer
  753. )
  754. /*++
  755. Routine Description:
  756. This routine flushes a process timer to the point where the reference
  757. count is prepared for anyone about to release a reference, and the signal
  758. is either queued or cancelled.
  759. Arguments:
  760. Process - Supplies a pointer to the process that owns the timer.
  761. Timer - Supplies a pointer to the timer to cancel/flush.
  762. Return Value:
  763. None.
  764. --*/
  765. {
  766. //
  767. // After the timer's cancelled, the DPC is queued or it isn't going to be.
  768. //
  769. KeCancelTimer(Timer->Timer);
  770. //
  771. // Cancelling or flushing the DPC means that either the work item is queued
  772. // or isn't going to be.
  773. //
  774. if (!KSUCCESS(KeCancelDpc(Timer->Dpc))) {
  775. KeFlushDpc(Timer->Dpc);
  776. }
  777. //
  778. // After the work queue's flushed, either the signal is queued or it isn't
  779. // going to be.
  780. //
  781. KeFlushWorkQueue(NULL);
  782. //
  783. // Attempt to cancel the signal to prevent signals from coming in way
  784. // after the timer was deleted.
  785. //
  786. PspCancelQueuedSignal(Process, &(Timer->SignalQueueEntry));
  787. return;
  788. }
  789. VOID
  790. PspProcessTimerDpcRoutine (
  791. PDPC Dpc
  792. )
  793. /*++
  794. Routine Description:
  795. This routine implements the DPC routine that fires when a process timer
  796. expires. It queues the work item.
  797. Arguments:
  798. Dpc - Supplies a pointer to the DPC that is running.
  799. Return Value:
  800. None.
  801. --*/
  802. {
  803. KSTATUS Status;
  804. PPROCESS_TIMER Timer;
  805. //
  806. // Increment the number of expirations, and queue the work item if this was
  807. // the first one.
  808. //
  809. Timer = (PPROCESS_TIMER)(Dpc->UserData);
  810. if (RtlAtomicAdd32(&(Timer->ExpirationCount), 1) == 0) {
  811. //
  812. // Increment the reference count to ensure this structure doesn't go
  813. // away while the signal is queued. Anybody trying to make the structure
  814. // go away needs to flush the DPC before decrementing their referecne
  815. // to ensure this gets a chance to run.
  816. //
  817. PspProcessTimerAddReference(Timer);
  818. Status = KeQueueWorkItem(Timer->WorkItem);
  819. ASSERT(KSUCCESS(Status));
  820. }
  821. return;
  822. }
  823. VOID
  824. PspProcessTimerWorkRoutine (
  825. PVOID Parameter
  826. )
  827. /*++
  828. Routine Description:
  829. This routine implements the process timer expiration work routine.
  830. Arguments:
  831. Parameter - Supplies a pointer to the process timer.
  832. Return Value:
  833. None.
  834. --*/
  835. {
  836. ULONG ExpirationCount;
  837. PPROCESS_TIMER Timer;
  838. Timer = (PPROCESS_TIMER)Parameter;
  839. //
  840. // Read the current expiration count to determine how to set the overflow
  841. // count.
  842. //
  843. ExpirationCount = RtlAtomicOr32(&(Timer->ExpirationCount), 0);
  844. ASSERT(ExpirationCount != 0);
  845. Timer->OverflowCount = ExpirationCount - 1;
  846. Timer->SignalQueueEntry.Parameters.FromU.OverflowCount =
  847. Timer->OverflowCount;
  848. if (Timer->Thread != NULL) {
  849. PsSignalThread(Timer->Thread,
  850. Timer->SignalQueueEntry.Parameters.SignalNumber,
  851. &(Timer->SignalQueueEntry),
  852. FALSE);
  853. } else {
  854. PsSignalProcess(Timer->Process,
  855. Timer->SignalQueueEntry.Parameters.SignalNumber,
  856. &(Timer->SignalQueueEntry));
  857. }
  858. return;
  859. }
  860. VOID
  861. PspProcessTimerSignalCompletion (
  862. PSIGNAL_QUEUE_ENTRY SignalQueueEntry
  863. )
  864. /*++
  865. Routine Description:
  866. This routine is called when a process timer's signal was successfully
  867. completed in usermode.
  868. Arguments:
  869. SignalQueueEntry - Supplies a pointer to the signal queue entry that was
  870. successfully sent to user mode.
  871. Return Value:
  872. None.
  873. --*/
  874. {
  875. ULONG ExpirationCount;
  876. ULONG OverflowCount;
  877. PPROCESS_TIMER Timer;
  878. Timer = PARENT_STRUCTURE(SignalQueueEntry, PROCESS_TIMER, SignalQueueEntry);
  879. //
  880. // Slam a zero into the overflow count.
  881. //
  882. OverflowCount = Timer->OverflowCount;
  883. Timer->OverflowCount = 0;
  884. //
  885. // Subtract off the overflow count (plus one for the original non-overflow
  886. // expiration) from the expiration count.
  887. //
  888. OverflowCount += 1;
  889. ExpirationCount = RtlAtomicAdd32(&(Timer->ExpirationCount), -OverflowCount);
  890. ASSERT(ExpirationCount >= OverflowCount);
  891. //
  892. // If new intervals came in already, re-queue the work item immediately,
  893. // as the DPC is never going to.
  894. //
  895. if (ExpirationCount - OverflowCount != 0) {
  896. KeQueueWorkItem(Timer->WorkItem);
  897. //
  898. // Release the reference, until the next DPC runs all parties are done
  899. // touching this memory.
  900. //
  901. } else {
  902. PspProcessTimerReleaseReference(Timer);
  903. }
  904. return;
  905. }
  906. VOID
  907. PspExpireRuntimeTimer (
  908. PKTHREAD Thread,
  909. PRUNTIME_TIMER Timer,
  910. ULONG Signal,
  911. ULONGLONG CurrentTime
  912. )
  913. /*++
  914. Routine Description:
  915. This routine is called when a runtime timer expires.
  916. Arguments:
  917. Thread - Supplies a pointer to the current thread.
  918. Timer - Supplies a pointer to the thread's runtime timer that expired.
  919. Signal - Supplies the signal to send the current process.
  920. CurrentTime - Supplies the current user or user/kernel time, for rearming
  921. of periodic timers.
  922. Return Value:
  923. None.
  924. --*/
  925. {
  926. ULONGLONG NextTime;
  927. //
  928. // Fire off a signal to the process as a whole.
  929. //
  930. PsSignalProcess(Thread->OwningProcess, Signal, NULL);
  931. //
  932. // Rearm the timer if it's periodic.
  933. //
  934. if (Timer->Period != 0) {
  935. NextTime = Timer->DueTime + Timer->Period;
  936. while ((NextTime > Timer->DueTime) && (NextTime <= CurrentTime)) {
  937. NextTime += Timer->Period;
  938. }
  939. if (NextTime <= Timer->DueTime) {
  940. NextTime = 0;
  941. }
  942. Timer->DueTime = NextTime;
  943. //
  944. // This was a one-shot timer. Disable it now.
  945. //
  946. } else {
  947. Timer->DueTime = 0;
  948. }
  949. return;
  950. }