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.

379 lines
10 KiB

  1. /*++
  2. Copyright (c) 1989-1994 Microsoft Corporation
  3. Module Name:
  4. apcobj.c
  5. Abstract:
  6. This module implements the kernel APC object. Functions are provided
  7. to initialize, flush, insert, and remove APC objects.
  8. Author:
  9. David N. Cutler (davec) 5-Mar-1989
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "ki.h"
  15. //
  16. // The following assert macro is used to check that an input apc is
  17. // really a kapc and not something else, like deallocated pool.
  18. //
  19. #define ASSERT_APC(E) { \
  20. ASSERT((E)->Type == ApcObject); \
  21. }
  22. VOID
  23. KeInitializeApc (
  24. IN PRKAPC Apc,
  25. IN PRKTHREAD Thread,
  26. IN KAPC_ENVIRONMENT Environment,
  27. IN PKKERNEL_ROUTINE KernelRoutine,
  28. IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
  29. IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
  30. IN KPROCESSOR_MODE ApcMode OPTIONAL,
  31. IN PVOID NormalContext OPTIONAL
  32. )
  33. /*++
  34. Routine Description:
  35. This function initializes a kernel APC object. The thread, kernel
  36. routine, and optionally a normal routine, processor mode, and normal
  37. context parameter are stored in the APC object.
  38. Arguments:
  39. Apc - Supplies a pointer to a control object of type APC.
  40. Thread - Supplies a pointer to a dispatcher object of type thread.
  41. Environment - Supplies the environment in which the APC will execute.
  42. Valid values for this parameter are: OriginalApcEnvironment,
  43. AttachedApcEnvironment, CurrentApcEnvironment, or InsertApcEnvironment
  44. KernelRoutine - Supplies a pointer to a function that is to be
  45. executed at IRQL APC_LEVEL in kernel mode.
  46. RundownRoutine - Supplies an optional pointer to a function that is to be
  47. called if the APC is in a thread's APC queue when the thread terminates.
  48. NormalRoutine - Supplies an optional pointer to a function that is
  49. to be executed at IRQL 0 in the specified processor mode. If this
  50. parameter is not specified, then the ProcessorMode and NormalContext
  51. parameters are ignored.
  52. ApcMode - Supplies the processor mode in which the function specified
  53. by the NormalRoutine parameter is to be executed.
  54. NormalContext - Supplies a pointer to an arbitrary data structure which is
  55. to be passed to the function specified by the NormalRoutine parameter.
  56. Return Value:
  57. None.
  58. --*/
  59. {
  60. ASSERT(Environment <= InsertApcEnvironment);
  61. //
  62. // Initialize standard control object header.
  63. //
  64. Apc->Type = ApcObject;
  65. Apc->Size = sizeof(KAPC);
  66. //
  67. // Initialize the APC environment, thread address, kernel routine address,
  68. // rundown routine address, normal routine address, processor mode, and
  69. // normal context parameter. If the normal routine address is null, then
  70. // the processor mode is defaulted to KernelMode and the APC is a special
  71. // APC. Otherwise, the processor mode is taken from the argument list.
  72. //
  73. if (Environment == CurrentApcEnvironment) {
  74. Apc->ApcStateIndex = Thread->ApcStateIndex;
  75. } else {
  76. ASSERT((Environment <= Thread->ApcStateIndex) || (Environment == InsertApcEnvironment));
  77. Apc->ApcStateIndex = (CCHAR)Environment;
  78. }
  79. Apc->Thread = Thread;
  80. Apc->KernelRoutine = KernelRoutine;
  81. Apc->RundownRoutine = RundownRoutine;
  82. Apc->NormalRoutine = NormalRoutine;
  83. if (ARGUMENT_PRESENT(NormalRoutine)) {
  84. Apc->ApcMode = ApcMode;
  85. Apc->NormalContext = NormalContext;
  86. } else {
  87. Apc->ApcMode = KernelMode;
  88. Apc->NormalContext = NIL;
  89. }
  90. Apc->Inserted = FALSE;
  91. return;
  92. }
  93. PLIST_ENTRY
  94. KeFlushQueueApc (
  95. IN PKTHREAD Thread,
  96. IN KPROCESSOR_MODE ApcMode
  97. )
  98. /*++
  99. Routine Description:
  100. This function flushes the APC queue selected by the specified processor
  101. mode for the specified thread. An APC queue is flushed by removing the
  102. listhead from the list, scanning the APC entries in the list, setting
  103. their inserted variables to FALSE, and then returning the address of the
  104. doubly linked list as the function value.
  105. Arguments:
  106. Thread - Supplies a pointer to a dispatcher object of type thread.
  107. ApcMode - Supplies the processor mode of the APC queue that is to
  108. be flushed.
  109. Return Value:
  110. The address of the first entry in the list of APC objects that were flushed
  111. from the specified APC queue.
  112. --*/
  113. {
  114. PKAPC Apc;
  115. PLIST_ENTRY FirstEntry;
  116. KLOCK_QUEUE_HANDLE LockHandle;
  117. PLIST_ENTRY NextEntry;
  118. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  119. if (IsListEmpty(&Thread->ApcState.ApcListHead[ApcMode])) {
  120. return NULL;
  121. }
  122. //
  123. // Raise IRQL to SYNCH_LEVEL, acquire the thread APC queue lock, and lock
  124. // the dispatcher database.
  125. //
  126. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  127. KiLockDispatcherDatabaseAtSynchLevel();
  128. //
  129. // Get address of first APC in the list and check if the list is
  130. // empty or contains entries that should be flushed. If entries
  131. // should be flushed, then scan the list of APC objects and set their
  132. // inserted state to FALSE.
  133. //
  134. FirstEntry = Thread->ApcState.ApcListHead[ApcMode].Flink;
  135. if (FirstEntry == &Thread->ApcState.ApcListHead[ApcMode]) {
  136. FirstEntry = (PLIST_ENTRY)NULL;
  137. } else {
  138. RemoveEntryList(&Thread->ApcState.ApcListHead[ApcMode]);
  139. NextEntry = FirstEntry;
  140. do {
  141. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  142. Apc->Inserted = FALSE;
  143. NextEntry = NextEntry->Flink;
  144. } while (NextEntry != FirstEntry);
  145. //
  146. // Reinitialize the header so the current thread may safely attach
  147. // to another process.
  148. //
  149. InitializeListHead(&Thread->ApcState.ApcListHead[ApcMode]);
  150. }
  151. //
  152. // Unlock the dispatcher database from SYNCH_LEVEL, unlock the thread APC
  153. // queue lock and lower IRQL to its previous value, and return address of
  154. // first entry in list of APC objects that were flushed.
  155. //
  156. KiUnlockDispatcherDatabaseFromSynchLevel();
  157. KeReleaseInStackQueuedSpinLock(&LockHandle);
  158. return FirstEntry;
  159. }
  160. BOOLEAN
  161. KeInsertQueueApc (
  162. IN PRKAPC Apc,
  163. IN PVOID SystemArgument1,
  164. IN PVOID SystemArgument2,
  165. IN KPRIORITY Increment
  166. )
  167. /*++
  168. Routine Description:
  169. This function inserts an APC object into the APC queue specifed by the
  170. thread and processor mode fields of the APC object. If the APC object
  171. is already in an APC queue or APC queuing is disabled, then no operation
  172. is performed. Otherwise the APC object is inserted in the specified queue
  173. and appropriate scheduling decisions are made.
  174. Arguments:
  175. Apc - Supplies a pointer to a control object of type APC.
  176. SystemArgument1, SystemArgument2 - Supply a set of two arguments that
  177. contain untyped data provided by the executive.
  178. Increment - Supplies the priority increment that is to be applied if
  179. queuing the APC causes a thread wait to be satisfied.
  180. Return Value:
  181. If the APC object is already in an APC queue or APC queuing is disabled,
  182. then a value of FALSE is returned. Otherwise a value of TRUE is returned.
  183. --*/
  184. {
  185. BOOLEAN Inserted;
  186. KLOCK_QUEUE_HANDLE LockHandle;
  187. KIRQL OldIrql;
  188. PRKTHREAD Thread;
  189. ASSERT_APC(Apc);
  190. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  191. //
  192. // Raise IRQL to SYNCH_LEVEL, acquire the thread APC queue lock, and lock
  193. // the dispatcher database.
  194. //
  195. Thread = Apc->Thread;
  196. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  197. KiLockDispatcherDatabaseAtSynchLevel();
  198. //
  199. // If APC queuing is disabled, then set inserted to FALSE. Else save
  200. // system parameter values in APC object, and attempt to queue APC.
  201. //
  202. if (Thread->ApcQueueable == FALSE) {
  203. Inserted = FALSE;
  204. } else {
  205. Apc->SystemArgument1 = SystemArgument1;
  206. Apc->SystemArgument2 = SystemArgument2;
  207. Inserted = KiInsertQueueApc(Apc, Increment);
  208. }
  209. //
  210. // Unlock the dispatcher database from SYNCH_LEVEL, unlock the thread APC
  211. // queue lock and lower IRQL to its previous value, and return whether the
  212. // APC was inserted.
  213. //
  214. KiUnlockDispatcherDatabaseFromSynchLevel();
  215. KeReleaseInStackQueuedSpinLock(&LockHandle);
  216. return Inserted;
  217. }
  218. BOOLEAN
  219. KeRemoveQueueApc (
  220. IN PKAPC Apc
  221. )
  222. /*++
  223. Routine Description:
  224. This function removes an APC object from an APC queue. If the APC object
  225. is not in an APC queue, then no operation is performed. Otherwise the
  226. APC object is removed from its current queue and its inserted state is
  227. set FALSE.
  228. Arguments:
  229. Apc - Supplies a pointer to a control object of type APC.
  230. Return Value:
  231. If the APC object is not in an APC queue, then a value of FALSE is returned.
  232. Otherwise a value of TRUE is returned.
  233. --*/
  234. {
  235. PKAPC_STATE ApcState;
  236. BOOLEAN Inserted;
  237. KLOCK_QUEUE_HANDLE LockHandle;
  238. PRKTHREAD Thread;
  239. ASSERT_APC(Apc);
  240. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  241. //
  242. // Raise IRQL to SYNCH_LEVEL, acquire the thread APC queue lock, and lock
  243. // the dispatcher database.
  244. //
  245. Thread = Apc->Thread;
  246. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  247. KiLockDispatcherDatabaseAtSynchLevel();
  248. //
  249. // If the APC object is in an APC queue, then remove it from the queue
  250. // and set its inserted state to FALSE. If the queue becomes empty, set
  251. // the APC pending state to FALSE.
  252. //
  253. Inserted = Apc->Inserted;
  254. if (Inserted != FALSE) {
  255. Apc->Inserted = FALSE;
  256. ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];
  257. RemoveEntryList(&Apc->ApcListEntry);
  258. if (IsListEmpty(&ApcState->ApcListHead[Apc->ApcMode]) != FALSE) {
  259. if (Apc->ApcMode == KernelMode) {
  260. ApcState->KernelApcPending = FALSE;
  261. } else {
  262. ApcState->UserApcPending = FALSE;
  263. }
  264. }
  265. }
  266. //
  267. // Unlock the dispatcher database from SYNCH_LEVEL, unlock the thread APC
  268. // queue lock and lower IRQL to its previous value, and return whether an
  269. // APC object was removed from the APC queue.
  270. //
  271. KiUnlockDispatcherDatabaseFromSynchLevel();
  272. KeReleaseInStackQueuedSpinLock(&LockHandle);
  273. return Inserted;
  274. }