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.

387 lines
11 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. //
  120. // If the APC mode is user mode, then acquire the thread APC queue lock
  121. // to ensure that no further APCs are queued after a possible setting of
  122. // the thread APC queueable state.
  123. //
  124. if (ApcMode == UserMode) {
  125. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  126. if (IsListEmpty(&Thread->ApcState.ApcListHead[ApcMode])) {
  127. KeReleaseInStackQueuedSpinLock(&LockHandle);
  128. return NULL;
  129. }
  130. } else {
  131. if (IsListEmpty(&Thread->ApcState.ApcListHead[ApcMode])) {
  132. return NULL;
  133. } else {
  134. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  135. }
  136. }
  137. //
  138. // Get address of first APC in the list and check if the list is
  139. // empty or contains entries that should be flushed. If entries
  140. // should be flushed, then scan the list of APC objects and set their
  141. // inserted state to FALSE.
  142. //
  143. FirstEntry = Thread->ApcState.ApcListHead[ApcMode].Flink;
  144. if (FirstEntry == &Thread->ApcState.ApcListHead[ApcMode]) {
  145. FirstEntry = (PLIST_ENTRY)NULL;
  146. } else {
  147. RemoveEntryList(&Thread->ApcState.ApcListHead[ApcMode]);
  148. NextEntry = FirstEntry;
  149. do {
  150. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  151. Apc->Inserted = FALSE;
  152. NextEntry = NextEntry->Flink;
  153. } while (NextEntry != FirstEntry);
  154. //
  155. // Reinitialize the header so the current thread may safely attach
  156. // to another process.
  157. //
  158. InitializeListHead(&Thread->ApcState.ApcListHead[ApcMode]);
  159. }
  160. //
  161. // Unlock the thread APC queue lock, lower IRQL to its previous value,
  162. // and return address of the first entry in list of APC objects that
  163. // were flushed.
  164. //
  165. KeReleaseInStackQueuedSpinLock(&LockHandle);
  166. return FirstEntry;
  167. }
  168. BOOLEAN
  169. KeInsertQueueApc (
  170. IN PRKAPC Apc,
  171. IN PVOID SystemArgument1,
  172. IN PVOID SystemArgument2,
  173. IN KPRIORITY Increment
  174. )
  175. /*++
  176. Routine Description:
  177. This function inserts an APC object into the APC queue specifed by the
  178. thread and processor mode fields of the APC object. If the APC object
  179. is already in an APC queue or APC queuing is disabled, then no operation
  180. is performed. Otherwise the APC object is inserted in the specified queue
  181. and appropriate scheduling decisions are made.
  182. Arguments:
  183. Apc - Supplies a pointer to a control object of type APC.
  184. SystemArgument1, SystemArgument2 - Supply a set of two arguments that
  185. contain untyped data provided by the executive.
  186. Increment - Supplies the priority increment that is to be applied if
  187. queuing the APC causes a thread wait to be satisfied.
  188. Return Value:
  189. If the APC object is already in an APC queue or APC queuing is disabled,
  190. then a value of FALSE is returned. Otherwise a value of TRUE is returned.
  191. --*/
  192. {
  193. BOOLEAN Inserted;
  194. KLOCK_QUEUE_HANDLE LockHandle;
  195. PRKTHREAD Thread;
  196. ASSERT_APC(Apc);
  197. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  198. //
  199. // Raise IRQL to SYNCH_LEVEL and acquire the thread APC queue lock.
  200. //
  201. Thread = Apc->Thread;
  202. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  203. //
  204. // If APC queuing is disabled or the APC is already inserted, then set
  205. // inserted to FALSE. Otherwise, set the system parameter values in the
  206. // APC object, inser the APC in the thread APC queue, and set inserted to
  207. // true.
  208. //
  209. if ((Thread->ApcQueueable == FALSE) ||
  210. (Apc->Inserted == TRUE)) {
  211. Inserted = FALSE;
  212. } else {
  213. Apc->Inserted = TRUE;
  214. Apc->SystemArgument1 = SystemArgument1;
  215. Apc->SystemArgument2 = SystemArgument2;
  216. KiInsertQueueApc(Apc, Increment);
  217. Inserted = TRUE;
  218. }
  219. //
  220. // Unlock the thread APC queue lock, exit the scheduler, and return
  221. // whether the APC was inserted.
  222. //
  223. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  224. KiExitDispatcher(LockHandle.OldIrql);
  225. return Inserted;
  226. }
  227. BOOLEAN
  228. KeRemoveQueueApc (
  229. IN PKAPC Apc
  230. )
  231. /*++
  232. Routine Description:
  233. This function removes an APC object from an APC queue. If the APC object
  234. is not in an APC queue, then no operation is performed. Otherwise the
  235. APC object is removed from its current queue and its inserted state is
  236. set FALSE.
  237. Arguments:
  238. Apc - Supplies a pointer to a control object of type APC.
  239. Return Value:
  240. If the APC object is not in an APC queue, then a value of FALSE is returned.
  241. Otherwise a value of TRUE is returned.
  242. --*/
  243. {
  244. PKAPC_STATE ApcState;
  245. BOOLEAN Inserted;
  246. KLOCK_QUEUE_HANDLE LockHandle;
  247. PRKTHREAD Thread;
  248. ASSERT_APC(Apc);
  249. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  250. //
  251. // Raise IRQL to SYNCH_LEVEL and acquire the thread APC queue lock.
  252. //
  253. Thread = Apc->Thread;
  254. KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
  255. //
  256. // If the APC object is in an APC queue, then remove it from the queue
  257. // and set its inserted state to FALSE. If the queue becomes empty, set
  258. // the APC pending state to FALSE.
  259. //
  260. Inserted = Apc->Inserted;
  261. if (Inserted != FALSE) {
  262. Apc->Inserted = FALSE;
  263. ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];
  264. KiLockDispatcherDatabaseAtSynchLevel();
  265. if (RemoveEntryList(&Apc->ApcListEntry) != FALSE) {
  266. if (Apc->ApcMode == KernelMode) {
  267. ApcState->KernelApcPending = FALSE;
  268. } else {
  269. ApcState->UserApcPending = FALSE;
  270. }
  271. }
  272. KiUnlockDispatcherDatabaseFromSynchLevel();
  273. }
  274. //
  275. // Release the thread APC queue lock, lower IRQL to its previous value,
  276. // and return whether an APC object was removed from the APC queue.
  277. //
  278. KeReleaseInStackQueuedSpinLock(&LockHandle);
  279. return Inserted;
  280. }