1
0

workitem.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387
  1. /*++
  2. Copyright (c) 2012 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. workitem.c
  9. Abstract:
  10. This module handles kernel work items.
  11. Author:
  12. Evan Green 12-Sep-2012
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "kep.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // Define work item crash codes.
  26. //
  27. #define WORK_ITEM_CRASH_MODIFY_QUEUED_ITEM 0x1
  28. #define WORK_ITEM_CRASH_BAD_QUEUE_STATE 0x2
  29. //
  30. // Work item flags.
  31. //
  32. //
  33. // This bit is set when the work item is actively in a queue. It cannot be
  34. // used directly to prevent double-queuing, as it is subject to multiprocessor
  35. // races if used that way.
  36. //
  37. #define WORK_ITEM_FLAG_QUEUED 0x00000001
  38. //
  39. // This bit is set if the work item can be added to a queue or destroyed at
  40. // dispatch level. It is automatically inherited from the queue flags if the
  41. // queue is set to support dispatch level.
  42. //
  43. #define WORK_ITEM_FLAG_SUPPORT_DISPATCH_LEVEL 0x00000002
  44. //
  45. // ------------------------------------------------------ Data Type Definitions
  46. //
  47. /*++
  48. Structure Description:
  49. This structure defines a work queue.
  50. Members:
  51. State - Stoers a pointer to the current work queue state.
  52. Lock - Stores either a pointer to a queued lock or a spin lock protecting
  53. the work item list, depending on whether the queue needs to accept
  54. work items at dispatch level.
  55. WorkItemListHead - Stores the head of the list of work items to execute.
  56. WorkItemCount - Stores the number of work items currently queued.
  57. Event - Stores a pointer to the event used to kick the work item threads
  58. into action.
  59. Flags - Stores a bitfield of flags governing the behavior of the work
  60. queue. See WORK_QUEUE_FLAG_* definitions.
  61. CurrentThreadCount - Stores the number of threads that are alive and
  62. processing (or waiting on) the work queue.
  63. Name - Stores a pointer to a string containing the name of the worker
  64. threads.
  65. --*/
  66. struct _WORK_QUEUE {
  67. volatile WORK_QUEUE_STATE State;
  68. union {
  69. PQUEUED_LOCK QueuedLock;
  70. KSPIN_LOCK SpinLock;
  71. } Lock;
  72. LIST_ENTRY WorkItemListHead;
  73. UINTN WorkItemCount;
  74. PKEVENT Event;
  75. ULONG Flags;
  76. volatile ULONG CurrentThreadCount;
  77. PSTR Name;
  78. };
  79. /*++
  80. Structure Description:
  81. This structure defines a work item, to be performed by a worker thread at
  82. low level.
  83. Members:
  84. ListEntry - Stores pointers to the next and previous work items in the
  85. work queue. The work queue is sorted by priority.
  86. ReferenceCount - Stores the reference count of the work item.
  87. Queue - Stores a pointer to the queue this work item was or will be
  88. put on.
  89. Event - Stores a pointer to an event that is signaled when the work item
  90. completes.
  91. Routine - Stores the worker routine.
  92. Parameter - Stores the parameter to pass to the worker routine.
  93. Priority - Stores the priority of the work item.
  94. Flags - Stores a pointer to internal flags used by the operating system.
  95. Do not modify these directly. See WORK_ITEM_FLAG_* definitions.
  96. --*/
  97. struct _WORK_ITEM {
  98. LIST_ENTRY ListEntry;
  99. UINTN ReferenceCount;
  100. PWORK_QUEUE Queue;
  101. PKEVENT Event;
  102. PWORK_ITEM_ROUTINE Routine;
  103. PVOID Parameter;
  104. WORK_PRIORITY Priority;
  105. ULONG Flags;
  106. };
  107. //
  108. // ----------------------------------------------- Internal Function Prototypes
  109. //
  110. VOID
  111. KepWorkerThread (
  112. );
  113. VOID
  114. KepDestroyWorkQueue (
  115. PWORK_QUEUE Queue
  116. );
  117. VOID
  118. KepWorkItemAddReference (
  119. PWORK_ITEM WorkItem
  120. );
  121. VOID
  122. KepWorkItemReleaseReference (
  123. PWORK_ITEM WorkItem
  124. );
  125. //
  126. // -------------------------------------------------------------------- Globals
  127. //
  128. //
  129. // Store a pointer to the system work queue.
  130. //
  131. PWORK_QUEUE KeSystemWorkQueue = NULL;
  132. //
  133. // ------------------------------------------------------------------ Functions
  134. //
  135. KERNEL_API
  136. PWORK_QUEUE
  137. KeCreateWorkQueue (
  138. ULONG Flags,
  139. PCSTR Name
  140. )
  141. /*++
  142. Routine Description:
  143. This routine creates a new work queue.
  144. Arguments:
  145. Flags - Supplies a bitfield of flags governing the behavior of the work
  146. queue. See WORK_QUEUE_FLAG_* definitions.
  147. Name - Supplies an optional pointer to the name of the worker threads
  148. created. A copy of this memory will be made. This should only be used
  149. for debugging, as text may be added to the end of the name supplied
  150. here to the actual worker thread names.
  151. Return Value:
  152. Returns a pointer to the new work queue on success.
  153. NULL on failure.
  154. --*/
  155. {
  156. ULONG NameSize;
  157. BOOL NonPaged;
  158. PWORK_QUEUE Queue;
  159. KSTATUS Status;
  160. //
  161. // Parse the flags.
  162. //
  163. NonPaged = FALSE;
  164. if ((Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  165. NonPaged = TRUE;
  166. }
  167. //
  168. // Create and initialize the work queue structure.
  169. //
  170. if (NonPaged != FALSE) {
  171. Queue = MmAllocateNonPagedPool(sizeof(WORK_QUEUE), KE_ALLOCATION_TAG);
  172. } else {
  173. Queue = MmAllocatePagedPool(sizeof(WORK_QUEUE), KE_ALLOCATION_TAG);
  174. }
  175. if (Queue == NULL) {
  176. Status = STATUS_INSUFFICIENT_RESOURCES;
  177. goto CreateWorkQueueEnd;
  178. }
  179. RtlZeroMemory(Queue, sizeof(WORK_QUEUE));
  180. //
  181. // Create a copy of the name, if supplied.
  182. //
  183. if (Name != NULL) {
  184. NameSize = RtlStringLength(Name) + 1;
  185. Queue->Name = MmAllocatePagedPool(NameSize, KE_ALLOCATION_TAG);
  186. if (Queue->Name == NULL) {
  187. Status = STATUS_INSUFFICIENT_RESOURCES;
  188. goto CreateWorkQueueEnd;
  189. }
  190. RtlStringCopy(Queue->Name, Name, NameSize);
  191. }
  192. if (NonPaged != FALSE) {
  193. KeInitializeSpinLock(&(Queue->Lock.SpinLock));
  194. } else {
  195. Queue->Lock.QueuedLock = KeCreateQueuedLock();
  196. if (Queue->Lock.QueuedLock == NULL) {
  197. Status = STATUS_INSUFFICIENT_RESOURCES;
  198. goto CreateWorkQueueEnd;
  199. }
  200. }
  201. INITIALIZE_LIST_HEAD(&(Queue->WorkItemListHead));
  202. Queue->Event = KeCreateEvent(NULL);
  203. if (Queue->Event == NULL) {
  204. Status = STATUS_INSUFFICIENT_RESOURCES;
  205. goto CreateWorkQueueEnd;
  206. }
  207. Queue->Flags = Flags;
  208. Queue->State = WorkQueueStateOpen;
  209. //
  210. // Create a worker thread.
  211. //
  212. Status = PsCreateKernelThread(KepWorkerThread, Queue, Name);
  213. if (!KSUCCESS(Status)) {
  214. goto CreateWorkQueueEnd;
  215. }
  216. Status = STATUS_SUCCESS;
  217. CreateWorkQueueEnd:
  218. if (!KSUCCESS(Status)) {
  219. if (Queue != NULL) {
  220. if (Queue->Name != NULL) {
  221. MmFreePagedPool(Queue->Name);
  222. }
  223. if (NonPaged == FALSE) {
  224. if (Queue->Lock.QueuedLock != NULL) {
  225. KeDestroyQueuedLock(Queue->Lock.QueuedLock);
  226. }
  227. }
  228. if (Queue->Event != NULL) {
  229. KeDestroyEvent(Queue->Event);
  230. }
  231. if (NonPaged != FALSE) {
  232. MmFreeNonPagedPool(Queue);
  233. } else {
  234. MmFreePagedPool(Queue);
  235. }
  236. Queue = NULL;
  237. }
  238. }
  239. return Queue;
  240. }
  241. KERNEL_API
  242. VOID
  243. KeDestroyWorkQueue (
  244. PWORK_QUEUE WorkQueue
  245. )
  246. /*++
  247. Routine Description:
  248. This routine destroys a work queue. If there are items on the work queue,
  249. they will be completed.
  250. Arguments:
  251. WorkQueue - Supplies a pointer to the work queue to destroy.
  252. Return Value:
  253. None.
  254. --*/
  255. {
  256. BOOL DispatchLevel;
  257. RUNLEVEL OldRunLevel;
  258. OldRunLevel = RunLevelCount;
  259. ASSERT((WorkQueue->State != WorkQueueStateInvalid) &&
  260. (WorkQueue->State != WorkQueueStateDestroying) &&
  261. (WorkQueue->State != WorkQueueStateDestroyed));
  262. DispatchLevel = FALSE;
  263. if ((WorkQueue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  264. DispatchLevel = TRUE;
  265. }
  266. if (DispatchLevel != FALSE) {
  267. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  268. }
  269. //
  270. // Indicate to the worker threads that a transition is occurring. This
  271. // routine cannot just set the state directly to destroying because if the
  272. // thread happens to see that and deletes the queue before this routine
  273. // gets around to signalling the event, this routine will touch freed
  274. // memory. The signal event routine must be called because the queues might
  275. // be asleep from inactivity. So move to this transitory state where the
  276. // queues know to stay awake but spin waiting for the state to move to
  277. // destroying.
  278. //
  279. WorkQueue->State = WorkQueueStateWakingForDestroying;
  280. KeSignalEvent(WorkQueue->Event, SignalOptionSignalAll);
  281. //
  282. // Now that all workers are awake and spinning, let them destroy themselves.
  283. // Muah.
  284. //
  285. WorkQueue->State = WorkQueueStateDestroying;
  286. if (DispatchLevel != FALSE) {
  287. KeLowerRunLevel(OldRunLevel);
  288. }
  289. return;
  290. }
  291. KERNEL_API
  292. VOID
  293. KeFlushWorkQueue (
  294. PWORK_QUEUE WorkQueue
  295. )
  296. /*++
  297. Routine Description:
  298. This routine flushes a work queue. If there are items on the work queue,
  299. they will be completed before this routine returns.
  300. Arguments:
  301. WorkQueue - Supplies a pointer to the work queue to flush.
  302. Return Value:
  303. None.
  304. --*/
  305. {
  306. BOOL DispatchLevel;
  307. RUNLEVEL OldRunLevel;
  308. PWORK_ITEM Sentinal;
  309. ASSERT(KeGetRunLevel() <= RunLevelDispatch);
  310. OldRunLevel = RunLevelCount;
  311. if (WorkQueue == NULL) {
  312. WorkQueue = KeSystemWorkQueue;
  313. }
  314. ASSERT(WorkQueue != NULL);
  315. ASSERT((WorkQueue->State != WorkQueueStateInvalid) &&
  316. (WorkQueue->State != WorkQueueStateDestroying) &&
  317. (WorkQueue->State != WorkQueueStateDestroyed));
  318. DispatchLevel = FALSE;
  319. if ((WorkQueue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  320. DispatchLevel = TRUE;
  321. }
  322. //
  323. // Acquire the queue's lock at the appropriate run level.
  324. //
  325. if (DispatchLevel != FALSE) {
  326. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  327. KeAcquireSpinLock(&(WorkQueue->Lock.SpinLock));
  328. } else {
  329. KeAcquireQueuedLock(WorkQueue->Lock.QueuedLock);
  330. }
  331. //
  332. // If the queue is empty, then there is no sentinal to record and no work
  333. // to do.
  334. //
  335. if (LIST_EMPTY(&(WorkQueue->WorkItemListHead)) != FALSE) {
  336. Sentinal = NULL;
  337. //
  338. // Otherwise, record the last item in the work queue and signal the worker
  339. // threads.
  340. //
  341. } else {
  342. Sentinal = (PWORK_ITEM)LIST_VALUE(WorkQueue->WorkItemListHead.Previous,
  343. WORK_ITEM,
  344. ListEntry);
  345. ASSERT(Sentinal != NULL);
  346. KeSignalEvent(WorkQueue->Event, SignalOptionSignalAll);
  347. }
  348. //
  349. // Unlock the list to let work proceed.
  350. //
  351. if (DispatchLevel != FALSE) {
  352. KeReleaseSpinLock(&(WorkQueue->Lock.SpinLock));
  353. KeLowerRunLevel(OldRunLevel);
  354. } else {
  355. KeReleaseQueuedLock(WorkQueue->Lock.QueuedLock);
  356. }
  357. //
  358. // If there is a sentinal, wait on it to complete.
  359. //
  360. if (Sentinal != NULL) {
  361. KeWaitForEvent(Sentinal->Event, FALSE, WAIT_TIME_INDEFINITE);
  362. }
  363. return;
  364. }
  365. KERNEL_API
  366. PWORK_ITEM
  367. KeCreateWorkItem (
  368. PWORK_QUEUE WorkQueue,
  369. WORK_PRIORITY Priority,
  370. PWORK_ITEM_ROUTINE WorkRoutine,
  371. PVOID Parameter,
  372. ULONG AllocationTag
  373. )
  374. /*++
  375. Routine Description:
  376. This routine creates a new reusable work item.
  377. Arguments:
  378. WorkQueue - Supplies a pointer to the queue this work item will
  379. eventually be queued to. Supply NULL to use the system work queue.
  380. Priority - Supplies the work priority.
  381. WorkRoutine - Supplies the routine to execute to do the work. This routine
  382. should be prepared to take one parameter.
  383. Parameter - Supplies an optional parameter to pass to the worker routine.
  384. AllocationTag - Supplies an allocation tag to associate with the work item.
  385. Return Value:
  386. Returns a pointer to the new work item on success.
  387. NULL on failure.
  388. --*/
  389. {
  390. PWORK_ITEM NewWorkItem;
  391. BOOL NonPaged;
  392. KSTATUS Status;
  393. ASSERT(KeGetRunLevel() <= RunLevelDispatch);
  394. NewWorkItem = NULL;
  395. if ((Priority < WorkPriorityNormal) || (Priority > WorkPriorityHigh) ||
  396. (WorkRoutine == NULL)) {
  397. Status = STATUS_INVALID_PARAMETER;
  398. goto CreateWorkItemEnd;
  399. }
  400. //
  401. // If no work queue was specified, use the system work queue.
  402. //
  403. if (WorkQueue == NULL) {
  404. WorkQueue = KeSystemWorkQueue;
  405. }
  406. NonPaged = FALSE;
  407. if ((WorkQueue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  408. NonPaged = TRUE;
  409. }
  410. //
  411. // Allocate space for a work item.
  412. //
  413. if (NonPaged != FALSE) {
  414. NewWorkItem = MmAllocateNonPagedPool(sizeof(WORK_ITEM), AllocationTag);
  415. } else {
  416. NewWorkItem = MmAllocatePagedPool(sizeof(WORK_ITEM), AllocationTag);
  417. }
  418. if (NewWorkItem == NULL) {
  419. Status = STATUS_INSUFFICIENT_RESOURCES;
  420. goto CreateWorkItemEnd;
  421. }
  422. RtlZeroMemory(NewWorkItem, sizeof(WORK_ITEM));
  423. NewWorkItem->ReferenceCount = 1;
  424. //
  425. // If the work queue has to support dispatch level, then the work item
  426. // needs to as well.
  427. //
  428. if (NonPaged != FALSE) {
  429. NewWorkItem->Flags |= WORK_ITEM_FLAG_SUPPORT_DISPATCH_LEVEL;
  430. }
  431. //
  432. // Initialize the rest of the work item. With the above flag set the
  433. // destroy routine can be used if things do not work out.
  434. //
  435. KeSetWorkItemParameters(NewWorkItem, Priority, WorkRoutine, Parameter);
  436. NewWorkItem->Queue = WorkQueue;
  437. NewWorkItem->Event = KeCreateEvent(NULL);
  438. if (NewWorkItem->Event == NULL) {
  439. Status = STATUS_INSUFFICIENT_RESOURCES;
  440. goto CreateWorkItemEnd;
  441. }
  442. KeSignalEvent(NewWorkItem->Event, SignalOptionSignalAll);
  443. Status = STATUS_SUCCESS;
  444. CreateWorkItemEnd:
  445. if (!KSUCCESS(Status)) {
  446. if (NewWorkItem != NULL) {
  447. KeDestroyWorkItem(NewWorkItem);
  448. NewWorkItem = NULL;
  449. }
  450. }
  451. return NewWorkItem;
  452. }
  453. KERNEL_API
  454. VOID
  455. KeDestroyWorkItem (
  456. PWORK_ITEM WorkItem
  457. )
  458. /*++
  459. Routine Description:
  460. This routine destroys a reusable work item. If this is a work item that
  461. can re-queue itself, then the caller needs to make sure that that can no
  462. longer happen before trying to destroy the work item.
  463. Arguments:
  464. WorkItem - Supplies a pointer to the work item.
  465. Return Value:
  466. None.
  467. --*/
  468. {
  469. //
  470. // Always attempt to cancel the work item.
  471. //
  472. KeCancelWorkItem(WorkItem);
  473. KepWorkItemReleaseReference(WorkItem);
  474. return;
  475. }
  476. KERNEL_API
  477. KSTATUS
  478. KeCancelWorkItem (
  479. PWORK_ITEM WorkItem
  480. )
  481. /*++
  482. Routine Description:
  483. This routine attempts to cancel the work item. If the work item is still on
  484. its work queue then this routine will pull it off and return successfully.
  485. Otherwise the work item may have been selected to run and this routine will
  486. return that the cancel was too late. Keep in mind that "too late" may also
  487. mean "too early" if the work item was never queued.
  488. Arguments:
  489. WorkItem - Supplies a pointer to the work item to cancel.
  490. Return Value:
  491. Status code.
  492. --*/
  493. {
  494. BOOL DispatchLevel;
  495. RUNLEVEL OldRunLevel;
  496. PWORK_QUEUE Queue;
  497. KSTATUS Status;
  498. OldRunLevel = RunLevelCount;
  499. ASSERT(KeGetRunLevel() <= RunLevelDispatch);
  500. //
  501. // Quickly return "too late" if the work item is not queued. It may be
  502. // about to run or running, or it might not have been queued.
  503. //
  504. if ((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) == 0) {
  505. return STATUS_TOO_LATE;
  506. }
  507. //
  508. // If the queue is not in a state to cancel a work item. Crash.
  509. //
  510. Queue = WorkItem->Queue;
  511. if ((Queue->State != WorkQueueStateOpen) &&
  512. (Queue->State != WorkQueueStatePaused)) {
  513. KeCrashSystem(CRASH_WORK_ITEM_CORRUPTION,
  514. WORK_ITEM_CRASH_BAD_QUEUE_STATE,
  515. (UINTN)WorkItem,
  516. (UINTN)Queue,
  517. Queue->State);
  518. }
  519. //
  520. // Determine whether or not to raise to dispatch level before acquiring
  521. // the lock.
  522. //
  523. DispatchLevel = FALSE;
  524. if ((Queue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  525. DispatchLevel = TRUE;
  526. }
  527. //
  528. // Acquire the work queue lock.
  529. //
  530. if (DispatchLevel != FALSE) {
  531. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  532. KeAcquireSpinLock(&(Queue->Lock.SpinLock));
  533. } else {
  534. KeAcquireQueuedLock(Queue->Lock.QueuedLock);
  535. }
  536. //
  537. // Now that the lock is held, check again to see if the work item was
  538. // selected to run and pulled off the list.
  539. //
  540. if ((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) == 0) {
  541. WorkItem = NULL;
  542. Status = STATUS_TOO_LATE;
  543. goto CancelWorkItemEnd;
  544. }
  545. ASSERT((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) != 0);
  546. ASSERT(WorkItem->ListEntry.Next != NULL);
  547. //
  548. // Remove the work item from the queue, signal it, and return successfully.
  549. //
  550. LIST_REMOVE(&(WorkItem->ListEntry));
  551. WorkItem->ListEntry.Next = NULL;
  552. Queue->WorkItemCount -= 1;
  553. WorkItem->Flags &= ~WORK_ITEM_FLAG_QUEUED;
  554. KeSignalEvent(WorkItem->Event, SignalOptionSignalAll);
  555. Status = STATUS_SUCCESS;
  556. CancelWorkItemEnd:
  557. if (DispatchLevel != FALSE) {
  558. KeReleaseSpinLock(&(Queue->Lock.SpinLock));
  559. KeLowerRunLevel(OldRunLevel);
  560. } else {
  561. KeReleaseQueuedLock(Queue->Lock.QueuedLock);
  562. }
  563. if (WorkItem != NULL) {
  564. KepWorkItemReleaseReference(WorkItem);
  565. }
  566. return Status;
  567. }
  568. KERNEL_API
  569. VOID
  570. KeFlushWorkItem (
  571. PWORK_ITEM WorkItem
  572. )
  573. /*++
  574. Routine Description:
  575. This routine does not return until the given work item has completed.
  576. Arguments:
  577. WorkItem - Supplies a pointer to the work item.
  578. Return Value:
  579. None.
  580. --*/
  581. {
  582. KeWaitForEvent(WorkItem->Event, FALSE, WAIT_TIME_INDEFINITE);
  583. return;
  584. }
  585. KERNEL_API
  586. VOID
  587. KeSetWorkItemParameters (
  588. PWORK_ITEM WorkItem,
  589. WORK_PRIORITY Priority,
  590. PWORK_ITEM_ROUTINE WorkRoutine,
  591. PVOID Parameter
  592. )
  593. /*++
  594. Routine Description:
  595. This routine resets the parameters of a work item to the given parameters.
  596. The work item must not be queued. This routine must be called at or below
  597. dispatch level.
  598. Arguments:
  599. WorkItem - Supplies a pointer to the work item to modify.
  600. Priority - Supplies the new work priority.
  601. WorkRoutine - Supplies the routine to execute to do the work. This routine
  602. should be prepared to take one parameter.
  603. Parameter - Supplies an optional parameter to pass to the worker routine.
  604. Return Value:
  605. None.
  606. --*/
  607. {
  608. if ((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) != 0) {
  609. KeCrashSystem(CRASH_WORK_ITEM_CORRUPTION,
  610. WORK_ITEM_CRASH_MODIFY_QUEUED_ITEM,
  611. (UINTN)WorkItem,
  612. (UINTN)WorkRoutine,
  613. (UINTN)Parameter);
  614. }
  615. WorkItem->Priority = Priority;
  616. WorkItem->Routine = WorkRoutine;
  617. WorkItem->Parameter = Parameter;
  618. return;
  619. }
  620. KERNEL_API
  621. KSTATUS
  622. KeQueueWorkItem (
  623. PWORK_ITEM WorkItem
  624. )
  625. /*++
  626. Routine Description:
  627. This routine queues a work item onto the work queue for execution as soon
  628. as possible. This routine must be called from dispatch level or below.
  629. Arguments:
  630. WorkItem - Supplies a pointer to the work item to queue.
  631. Return Value:
  632. STATUS_SUCCESS on success.
  633. STATUS_RESOURCE_IN_USE if the work item is already queued.
  634. --*/
  635. {
  636. BOOL DispatchLevel;
  637. RUNLEVEL OldRunLevel;
  638. PWORK_QUEUE Queue;
  639. KSTATUS Status;
  640. OldRunLevel = RunLevelCount;
  641. ASSERT(KeGetRunLevel() <= RunLevelDispatch);
  642. if ((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) != 0) {
  643. return STATUS_RESOURCE_IN_USE;
  644. }
  645. Queue = WorkItem->Queue;
  646. if ((Queue->State != WorkQueueStateOpen) &&
  647. (Queue->State != WorkQueueStatePaused)) {
  648. KeCrashSystem(CRASH_WORK_ITEM_CORRUPTION,
  649. WORK_ITEM_CRASH_BAD_QUEUE_STATE,
  650. (UINTN)WorkItem,
  651. (UINTN)Queue,
  652. Queue->State);
  653. }
  654. //
  655. // Determine whether or not to raise to dispatch level before acquiring
  656. // the lock.
  657. //
  658. DispatchLevel = FALSE;
  659. if ((Queue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  660. DispatchLevel = TRUE;
  661. }
  662. KepWorkItemAddReference(WorkItem);
  663. //
  664. // Acquire the work queue lock.
  665. //
  666. if (DispatchLevel != FALSE) {
  667. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  668. KeAcquireSpinLock(&(Queue->Lock.SpinLock));
  669. } else {
  670. KeAcquireQueuedLock(Queue->Lock.QueuedLock);
  671. }
  672. //
  673. // Now that the lock is held, check again to see if someone else snuck in
  674. // and queued this work item.
  675. //
  676. if ((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) != 0) {
  677. Status = STATUS_RESOURCE_IN_USE;
  678. goto QueueWorkItemEnd;
  679. }
  680. //
  681. // Mark the work item as having been queued now that the lock is held.
  682. //
  683. WorkItem->Flags |= WORK_ITEM_FLAG_QUEUED;
  684. KeSignalEvent(WorkItem->Event, SignalOptionUnsignal);
  685. //
  686. // Insert high priority items on the beginning of the list, and normal items
  687. // on the end.
  688. //
  689. if (WorkItem->Priority == WorkPriorityHigh) {
  690. INSERT_AFTER(&(WorkItem->ListEntry), &(Queue->WorkItemListHead));
  691. } else {
  692. INSERT_BEFORE(&(WorkItem->ListEntry), &(Queue->WorkItemListHead));
  693. }
  694. Queue->WorkItemCount += 1;
  695. WorkItem = NULL;
  696. Status = STATUS_SUCCESS;
  697. QueueWorkItemEnd:
  698. if (DispatchLevel != FALSE) {
  699. KeReleaseSpinLock(&(Queue->Lock.SpinLock));
  700. KeLowerRunLevel(OldRunLevel);
  701. } else {
  702. KeReleaseQueuedLock(Queue->Lock.QueuedLock);
  703. }
  704. if (KSUCCESS(Status)) {
  705. //
  706. // Signal the event to kick off the worker threads.
  707. //
  708. KeSignalEvent(Queue->Event, SignalOptionSignalAll);
  709. }
  710. if (WorkItem != NULL) {
  711. KepWorkItemReleaseReference(WorkItem);
  712. }
  713. return Status;
  714. }
  715. KERNEL_API
  716. KSTATUS
  717. KeCreateAndQueueWorkItem (
  718. PWORK_QUEUE WorkQueue,
  719. WORK_PRIORITY Priority,
  720. PWORK_ITEM_ROUTINE WorkRoutine,
  721. PVOID Parameter
  722. )
  723. /*++
  724. Routine Description:
  725. This routine creates and queues a work item. This work item will get
  726. executed in a worker thread an arbitrary amount of time later. The work
  727. item will be automatically freed after the work routine is executed.
  728. Arguments:
  729. WorkQueue - Supplies a pointer to the queue this work item will
  730. eventually be queued to. Supply NULL to use the system work queue.
  731. Priority - Supplies the work priority.
  732. WorkRoutine - Supplies the routine to execute to doe the work. This
  733. routine should be prepared to take one parameter.
  734. Parameter - Supplies an optional parameter to pass to the worker routine.
  735. Return Value:
  736. STATUS_SUCCESS on success.
  737. STATUS_UNSUCCESSFUL on failure.
  738. --*/
  739. {
  740. KSTATUS Status;
  741. PWORK_ITEM WorkItem;
  742. //
  743. // Create the new work item, and set the flag to automatically free it.
  744. //
  745. WorkItem = KeCreateWorkItem(WorkQueue,
  746. Priority,
  747. WorkRoutine,
  748. Parameter,
  749. KE_WORK_ITEM_ALLOCATION_TAG);
  750. if (WorkItem == NULL) {
  751. return STATUS_UNSUCCESSFUL;
  752. }
  753. Status = KeQueueWorkItem(WorkItem);
  754. if (!KSUCCESS(Status)) {
  755. return Status;
  756. }
  757. //
  758. // Release the reference on the work item from when it was created, so that
  759. // after it runs it will automatically destroy itself.
  760. //
  761. KepWorkItemReleaseReference(WorkItem);
  762. return Status;
  763. }
  764. KSTATUS
  765. KepInitializeSystemWorkQueue (
  766. VOID
  767. )
  768. /*++
  769. Routine Description:
  770. This routine initializes the system work queue. This must happen after the
  771. Object Manager initializes.
  772. Arguments:
  773. None.
  774. Return Value:
  775. Status code.
  776. --*/
  777. {
  778. ULONG Flags;
  779. Flags = WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL;
  780. KeSystemWorkQueue = KeCreateWorkQueue(Flags, "KeWorker");
  781. if (KeSystemWorkQueue == NULL) {
  782. return STATUS_UNSUCCESSFUL;
  783. }
  784. return STATUS_SUCCESS;
  785. }
  786. //
  787. // --------------------------------------------------------- Internal Functions
  788. //
  789. VOID
  790. KepWorkerThread (
  791. PVOID Parameter
  792. )
  793. /*++
  794. Routine Description:
  795. This routine processes work items off of a work queue.
  796. Arguments:
  797. Parameter - Supplies a pointer to a parameter that in this case contains a
  798. pointer to the work queue to service.
  799. Return Value:
  800. None. Does not return.
  801. --*/
  802. {
  803. RUNLEVEL OldRunLevel;
  804. PWORK_QUEUE Queue;
  805. BOOL RaiseToDispatch;
  806. ULONG RemainingThreads;
  807. PWORK_ITEM WorkItem;
  808. OldRunLevel = RunLevelCount;
  809. Queue = (PWORK_QUEUE)Parameter;
  810. RtlAtomicAdd32(&(Queue->CurrentThreadCount), 1);
  811. RaiseToDispatch = FALSE;
  812. if ((Queue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  813. RaiseToDispatch = TRUE;
  814. }
  815. while (TRUE) {
  816. //
  817. // Wait for the event, then process work items until none are left.
  818. //
  819. KeWaitForEvent(Queue->Event, FALSE, WAIT_TIME_INDEFINITE);
  820. while (TRUE) {
  821. WorkItem = NULL;
  822. if (RaiseToDispatch != FALSE) {
  823. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  824. KeAcquireSpinLock(&(Queue->Lock.SpinLock));
  825. } else {
  826. KeAcquireQueuedLock(Queue->Lock.QueuedLock);
  827. }
  828. if (LIST_EMPTY(&(Queue->WorkItemListHead)) == FALSE) {
  829. WorkItem = LIST_VALUE(Queue->WorkItemListHead.Next,
  830. WORK_ITEM,
  831. ListEntry);
  832. LIST_REMOVE(&(WorkItem->ListEntry));
  833. WorkItem->ListEntry.Next = NULL;
  834. Queue->WorkItemCount -= 1;
  835. WorkItem->Flags &= ~WORK_ITEM_FLAG_QUEUED;
  836. } else {
  837. KeSignalEvent(Queue->Event, SignalOptionUnsignal);
  838. }
  839. if (RaiseToDispatch != FALSE) {
  840. KeReleaseSpinLock(&(Queue->Lock.SpinLock));
  841. KeLowerRunLevel(OldRunLevel);
  842. } else {
  843. KeReleaseQueuedLock(Queue->Lock.QueuedLock);
  844. }
  845. //
  846. // If there is a work item, execute it.
  847. //
  848. if (WorkItem != NULL) {
  849. WorkItem->Routine(WorkItem->Parameter);
  850. KeSignalEvent(WorkItem->Event, SignalOptionSignalAll);
  851. KepWorkItemReleaseReference(WorkItem);
  852. //
  853. // If there was no work item, stop looking.
  854. //
  855. } else {
  856. break;
  857. }
  858. //
  859. // If the work queue became paused, stop processing events.
  860. //
  861. if (Queue->State == WorkQueueStatePaused) {
  862. break;
  863. }
  864. }
  865. //
  866. // If this thread happened to catch someone else marking this queue
  867. // for destruction, politely wait for that operation to complete and
  868. // avoid destroying the queue out from under it.
  869. //
  870. while (Queue->State == WorkQueueStateWakingForDestroying) {
  871. KeYield();
  872. }
  873. if (Queue->State == WorkQueueStateDestroying) {
  874. RemainingThreads = RtlAtomicAdd32(&(Queue->CurrentThreadCount), -1);
  875. //
  876. // If this is the last thread standing, turn out the lights by
  877. // destroying the work queue.
  878. //
  879. if (RemainingThreads == 1) {
  880. Queue->State = WorkQueueStateDestroyed;
  881. KepDestroyWorkQueue(Queue);
  882. break;
  883. }
  884. }
  885. }
  886. return;
  887. }
  888. VOID
  889. KepDestroyWorkQueue (
  890. PWORK_QUEUE Queue
  891. )
  892. /*++
  893. Routine Description:
  894. This routine destroys and frees a work queue. This routine will be
  895. called automatically by the last worker thread to exit.
  896. Arguments:
  897. Queue - Supplies a pointer to the queue to destroy.
  898. Return Value:
  899. None.
  900. --*/
  901. {
  902. BOOL NonPaged;
  903. ASSERT(Queue->CurrentThreadCount == 0);
  904. NonPaged = FALSE;
  905. if ((Queue->Flags & WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  906. NonPaged = TRUE;
  907. }
  908. if (Queue->Name != NULL) {
  909. MmFreePagedPool(Queue->Name);
  910. }
  911. if ((NonPaged != FALSE) && (Queue->Lock.QueuedLock != NULL)) {
  912. KeDestroyQueuedLock(Queue->Lock.QueuedLock);
  913. }
  914. if (Queue->Event != NULL) {
  915. KeDestroyEvent(Queue->Event);
  916. }
  917. if (NonPaged != FALSE) {
  918. MmFreeNonPagedPool(Queue);
  919. } else {
  920. MmFreePagedPool(Queue);
  921. }
  922. return;
  923. }
  924. VOID
  925. KepWorkItemAddReference (
  926. PWORK_ITEM WorkItem
  927. )
  928. /*++
  929. Routine Description:
  930. This routine adds a reference to the given work item.
  931. Arguments:
  932. WorkItem - Supplies a pointer to the work item to add a reference to.
  933. Return Value:
  934. None.
  935. --*/
  936. {
  937. UINTN OldReferenceCount;
  938. OldReferenceCount = RtlAtomicAdd(&(WorkItem->ReferenceCount), 1);
  939. ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000));
  940. return;
  941. }
  942. VOID
  943. KepWorkItemReleaseReference (
  944. PWORK_ITEM WorkItem
  945. )
  946. /*++
  947. Routine Description:
  948. This routine release the reference on a work item. If the reference count
  949. drops to zero, the work item will be destroyed.
  950. Arguments:
  951. WorkItem - Supplies a pointer to the work item to release.
  952. Return Value:
  953. None.
  954. --*/
  955. {
  956. BOOL NonPaged;
  957. UINTN OldReferenceCount;
  958. OldReferenceCount = RtlAtomicAdd(&(WorkItem->ReferenceCount), -1);
  959. ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000));
  960. if (OldReferenceCount == 1) {
  961. ASSERT((WorkItem->Flags & WORK_ITEM_FLAG_QUEUED) == 0);
  962. if (WorkItem->Event != NULL) {
  963. KeDestroyEvent(WorkItem->Event);
  964. }
  965. NonPaged = FALSE;
  966. if ((WorkItem->Flags & WORK_ITEM_FLAG_SUPPORT_DISPATCH_LEVEL) != 0) {
  967. NonPaged = TRUE;
  968. }
  969. if (NonPaged != FALSE) {
  970. MmFreeNonPagedPool(WorkItem);
  971. } else {
  972. MmFreePagedPool(WorkItem);
  973. }
  974. }
  975. return;
  976. }