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.

617 lines
15 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. eventobj.c
  5. Abstract:
  6. This module implements the kernel event objects. Functions are
  7. provided to initialize, pulse, read, reset, and set event objects.
  8. Author:
  9. David N. Cutler (davec) 27-Feb-1989
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "ki.h"
  15. #pragma alloc_text (PAGE, KeInitializeEventPair)
  16. #undef KeClearEvent
  17. //
  18. // The following assert macro is used to check that an input event is
  19. // really a kernel event and not something else, like deallocated pool.
  20. //
  21. #define ASSERT_EVENT(E) { \
  22. ASSERT((E)->Header.Type == NotificationEvent || \
  23. (E)->Header.Type == SynchronizationEvent); \
  24. }
  25. //
  26. // The following assert macro is used to check that an input event is
  27. // really a kernel event pair and not something else, like deallocated
  28. // pool.
  29. //
  30. #define ASSERT_EVENT_PAIR(E) { \
  31. ASSERT((E)->Type == EventPairObject); \
  32. }
  33. #undef KeInitializeEvent
  34. VOID
  35. KeInitializeEvent (
  36. IN PRKEVENT Event,
  37. IN EVENT_TYPE Type,
  38. IN BOOLEAN State
  39. )
  40. /*++
  41. Routine Description:
  42. This function initializes a kernel event object. The initial signal
  43. state of the object is set to the specified value.
  44. Arguments:
  45. Event - Supplies a pointer to a dispatcher object of type event.
  46. Type - Supplies the type of event; NotificationEvent or
  47. SynchronizationEvent.
  48. State - Supplies the initial signal state of the event object.
  49. Return Value:
  50. None.
  51. --*/
  52. {
  53. //
  54. // Initialize standard dispatcher object header, set initial signal
  55. // state of event object, and set the type of event object.
  56. //
  57. Event->Header.Type = (UCHAR)Type;
  58. Event->Header.Size = sizeof(KEVENT) / sizeof(LONG);
  59. Event->Header.SignalState = State;
  60. InitializeListHead(&Event->Header.WaitListHead);
  61. return;
  62. }
  63. VOID
  64. KeInitializeEventPair (
  65. IN PKEVENT_PAIR EventPair
  66. )
  67. /*++
  68. Routine Description:
  69. This function initializes a kernel event pair object. A kernel event
  70. pair object contains two separate synchronization event objects that
  71. are used to provide a fast interprocess synchronization capability.
  72. Arguments:
  73. EventPair - Supplies a pointer to a control object of type event pair.
  74. Return Value:
  75. None.
  76. --*/
  77. {
  78. //
  79. // Initialize the type and size of the event pair object and initialize
  80. // the two event object as synchronization events with an initial state
  81. // of FALSE.
  82. //
  83. EventPair->Type = (USHORT)EventPairObject;
  84. EventPair->Size = sizeof(KEVENT_PAIR);
  85. KeInitializeEvent(&EventPair->EventLow, SynchronizationEvent, FALSE);
  86. KeInitializeEvent(&EventPair->EventHigh, SynchronizationEvent, FALSE);
  87. return;
  88. }
  89. VOID
  90. KeClearEvent (
  91. IN PRKEVENT Event
  92. )
  93. /*++
  94. Routine Description:
  95. This function clears the signal state of an event object.
  96. Arguments:
  97. Event - Supplies a pointer to a dispatcher object of type event.
  98. Return Value:
  99. None.
  100. --*/
  101. {
  102. ASSERT_EVENT(Event);
  103. //
  104. // Clear signal state of event object.
  105. //
  106. Event->Header.SignalState = 0;
  107. return;
  108. }
  109. LONG
  110. KePulseEvent (
  111. IN PRKEVENT Event,
  112. IN KPRIORITY Increment,
  113. IN BOOLEAN Wait
  114. )
  115. /*++
  116. Routine Description:
  117. This function atomically sets the signal state of an event object to
  118. Signaled, attempts to satisfy as many Waits as possible, and then resets
  119. the signal state of the event object to Not-Signaled. The previous signal
  120. state of the event object is returned as the function value.
  121. Arguments:
  122. Event - Supplies a pointer to a dispatcher object of type event.
  123. Increment - Supplies the priority increment that is to be applied
  124. if setting the event causes a Wait to be satisfied.
  125. Wait - Supplies a boolean value that signifies whether the call to
  126. KePulseEvent will be immediately followed by a call to one of the
  127. kernel Wait functions.
  128. Return Value:
  129. The previous signal state of the event object.
  130. --*/
  131. {
  132. KIRQL OldIrql;
  133. LONG OldState;
  134. PRKTHREAD Thread;
  135. ASSERT_EVENT(Event);
  136. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  137. //
  138. // Raise IRQL to dispatcher level and lock dispatcher database.
  139. //
  140. KiLockDispatcherDatabase(&OldIrql);
  141. //
  142. // If the current state of the event object is Not-Signaled and
  143. // the wait queue is not empty, then set the state of the event
  144. // to Signaled, satisfy as many Waits as possible, and then reset
  145. // the state of the event to Not-Signaled.
  146. //
  147. OldState = Event->Header.SignalState;
  148. if ((OldState == 0) && (IsListEmpty(&Event->Header.WaitListHead) == FALSE)) {
  149. Event->Header.SignalState = 1;
  150. KiWaitTest(Event, Increment);
  151. }
  152. Event->Header.SignalState = 0;
  153. //
  154. // If the value of the Wait argument is TRUE, then return to the
  155. // caller with IRQL raised and the dispatcher database locked. Else
  156. // release the dispatcher database lock and lower IRQL to the
  157. // previous value.
  158. //
  159. if (Wait != FALSE) {
  160. Thread = KeGetCurrentThread();
  161. Thread->WaitIrql = OldIrql;
  162. Thread->WaitNext = Wait;
  163. } else {
  164. KiUnlockDispatcherDatabase(OldIrql);
  165. }
  166. //
  167. // Return previous signal state of event object.
  168. //
  169. return OldState;
  170. }
  171. LONG
  172. KeReadStateEvent (
  173. IN PRKEVENT Event
  174. )
  175. /*++
  176. Routine Description:
  177. This function reads the current signal state of an event object.
  178. Arguments:
  179. Event - Supplies a pointer to a dispatcher object of type event.
  180. Return Value:
  181. The current signal state of the event object.
  182. --*/
  183. {
  184. ASSERT_EVENT(Event);
  185. //
  186. // Return current signal state of event object.
  187. //
  188. return Event->Header.SignalState;
  189. }
  190. LONG
  191. KeResetEvent (
  192. IN PRKEVENT Event
  193. )
  194. /*++
  195. Routine Description:
  196. This function resets the signal state of an event object to
  197. Not-Signaled. The previous state of the event object is returned
  198. as the function value.
  199. Arguments:
  200. Event - Supplies a pointer to a dispatcher object of type event.
  201. Return Value:
  202. The previous signal state of the event object.
  203. --*/
  204. {
  205. KIRQL OldIrql;
  206. LONG OldState;
  207. ASSERT_EVENT(Event);
  208. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  209. //
  210. // Raise IRQL to dispatcher level and lock dispatcher database.
  211. //
  212. KiLockDispatcherDatabase(&OldIrql);
  213. //
  214. // Capture the current signal state of event object and then reset
  215. // the state of the event object to Not-Signaled.
  216. //
  217. OldState = Event->Header.SignalState;
  218. Event->Header.SignalState = 0;
  219. //
  220. // Unlock the dispatcher database and lower IRQL to its previous
  221. // value.
  222. KiUnlockDispatcherDatabase(OldIrql);
  223. //
  224. // Return previous signal state of event object.
  225. //
  226. return OldState;
  227. }
  228. LONG
  229. KeSetEvent (
  230. IN PRKEVENT Event,
  231. IN KPRIORITY Increment,
  232. IN BOOLEAN Wait
  233. )
  234. /*++
  235. Routine Description:
  236. This function sets the signal state of an event object to Signaled
  237. and attempts to satisfy as many Waits as possible. The previous
  238. signal state of the event object is returned as the function value.
  239. Arguments:
  240. Event - Supplies a pointer to a dispatcher object of type event.
  241. Increment - Supplies the priority increment that is to be applied
  242. if setting the event causes a Wait to be satisfied.
  243. Wait - Supplies a boolean value that signifies whether the call to
  244. KePulseEvent will be immediately followed by a call to one of the
  245. kernel Wait functions.
  246. Return Value:
  247. The previous signal state of the event object.
  248. --*/
  249. {
  250. KIRQL OldIrql;
  251. LONG OldState;
  252. PRKTHREAD Thread;
  253. PRKWAIT_BLOCK WaitBlock;
  254. ASSERT_EVENT(Event);
  255. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  256. //
  257. // Collect call data.
  258. //
  259. #if defined(_COLLECT_SET_EVENT_CALLDATA_)
  260. RECORD_CALL_DATA(&KiSetEventCallData);
  261. #endif
  262. //
  263. // Raise IRQL to dispatcher level and lock dispatcher database.
  264. //
  265. KiLockDispatcherDatabase(&OldIrql);
  266. //
  267. // If the wait list is empty, then set the state of the event to signaled.
  268. // Otherwise, check if the wait can be satisfied immediately.
  269. //
  270. OldState = Event->Header.SignalState;
  271. if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
  272. Event->Header.SignalState = 1;
  273. } else {
  274. //
  275. // If the event is a notification event or the wait is not a wait any,
  276. // then set the state of the event to signaled and attempt to satisfy
  277. // as many waits as possible. Otherwise, the wait can be satisfied by
  278. // directly unwaiting the thread.
  279. //
  280. WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink,
  281. KWAIT_BLOCK,
  282. WaitListEntry);
  283. if ((Event->Header.Type == NotificationEvent) ||
  284. (WaitBlock->WaitType != WaitAny)) {
  285. if (OldState == 0) {
  286. Event->Header.SignalState = 1;
  287. KiWaitTest(Event, Increment);
  288. }
  289. } else {
  290. KiUnwaitThread(WaitBlock->Thread,
  291. (NTSTATUS)WaitBlock->WaitKey,
  292. Increment,
  293. NULL);
  294. }
  295. }
  296. //
  297. // If the value of the Wait argument is TRUE, then return to the
  298. // caller with IRQL raised and the dispatcher database locked. Else
  299. // release the dispatcher database lock and lower IRQL to its
  300. // previous value.
  301. //
  302. if (Wait != FALSE) {
  303. Thread = KeGetCurrentThread();
  304. Thread->WaitNext = Wait;
  305. Thread->WaitIrql = OldIrql;
  306. } else {
  307. KiUnlockDispatcherDatabase(OldIrql);
  308. }
  309. //
  310. // Return previous signal state of event object.
  311. //
  312. return OldState;
  313. }
  314. VOID
  315. KeSetEventBoostPriority (
  316. IN PRKEVENT Event,
  317. IN PRKTHREAD *Thread OPTIONAL
  318. )
  319. /*++
  320. Routine Description:
  321. This function conditionally sets the signal state of an event object
  322. to Signaled, and attempts to unwait the first waiter, and optionally
  323. returns the thread address of the unwatied thread.
  324. N.B. This function can only be called with synchronization events.
  325. It is assumed that the waiter is NEVER waiting on multiple
  326. objects.
  327. Arguments:
  328. Event - Supplies a pointer to a dispatcher object of type event.
  329. Thread - Supplies an optional pointer to a variable that receives
  330. the address of the thread that is awakened.
  331. Return Value:
  332. None.
  333. --*/
  334. {
  335. PKTHREAD CurrentThread;
  336. KIRQL OldIrql;
  337. KPRIORITY Priority;
  338. PKWAIT_BLOCK WaitBlock;
  339. PRKTHREAD WaitThread;
  340. ASSERT(Event->Header.Type == SynchronizationEvent);
  341. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  342. //
  343. // Raise IRQL to dispatcher level and lock dispatcher database.
  344. //
  345. CurrentThread = KeGetCurrentThread();
  346. KiLockDispatcherDatabase(&OldIrql);
  347. //
  348. // If the the wait list is not empty, then satisfy the wait of the
  349. // first thread in the wait list. Otherwise, set the signal state
  350. // of the event object.
  351. //
  352. if (IsListEmpty(&Event->Header.WaitListHead) != FALSE) {
  353. Event->Header.SignalState = 1;
  354. } else {
  355. //
  356. // Get the address of the first wait block in the event list.
  357. // If the wait is a wait any, then set the state of the event
  358. // to signaled and attempt to satisfy as many waits as possible.
  359. // Otherwise, unwait the first thread and apply an appropriate
  360. // priority boost to help prevent lock convoys from forming.
  361. //
  362. // N.B. Internal calls to this function for resource and fast
  363. // mutex boosts NEVER call with a possibility of having
  364. // a wait type of WaitAll. Calls from the NT service to
  365. // set event and boost priority are restricted as to the
  366. // event type, but not the wait type.
  367. //
  368. WaitBlock = CONTAINING_RECORD(Event->Header.WaitListHead.Flink,
  369. KWAIT_BLOCK,
  370. WaitListEntry);
  371. if (WaitBlock->WaitType == WaitAll) {
  372. Event->Header.SignalState = 1;
  373. KiWaitTest(Event, EVENT_INCREMENT);
  374. } else {
  375. //
  376. // Get the address of the waiting thread and return the address
  377. // if requested.
  378. //
  379. WaitThread = WaitBlock->Thread;
  380. if (ARGUMENT_PRESENT(Thread)) {
  381. *Thread = WaitThread;
  382. }
  383. //
  384. // If the current thread has received an unusual boost (most
  385. // likely when it acquired the lock associated with the event
  386. // being set), then remove the boost.
  387. //
  388. CurrentThread->Priority -= CurrentThread->PriorityDecrement;
  389. CurrentThread->PriorityDecrement = 0;
  390. //
  391. // If the priority of the waiting thread is less than or equal
  392. // to the priority of the current thread and the waiting thread
  393. // priority is less than the time critical priority bound and
  394. // boosts are not disabled for the waiting thread, then boost
  395. // the priority of the waiting thread to the minimum of the
  396. // priority of the current thread priority plus one and the time
  397. // critical bound minus one. This boost will be taken away at
  398. // quantum end.
  399. //
  400. if ((WaitThread->Priority <= CurrentThread->Priority) &&
  401. (WaitThread->Priority < TIME_CRITICAL_PRIORITY_BOUND) &&
  402. (WaitThread->DisableBoost == FALSE)) {
  403. WaitThread->Priority -= WaitThread->PriorityDecrement;
  404. Priority = min(CurrentThread->Priority + 1,
  405. TIME_CRITICAL_PRIORITY_BOUND - 1);
  406. WaitThread->PriorityDecrement = (SCHAR)(Priority - WaitThread->Priority);
  407. WaitThread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
  408. WaitThread->Priority = (SCHAR)Priority;
  409. }
  410. //
  411. // Make sure the thread has a quantum that is appropriate for
  412. // lock ownership.
  413. //
  414. if (WaitThread->Quantum < (WAIT_QUANTUM_DECREMENT * 4)) {
  415. WaitThread->Quantum = WAIT_QUANTUM_DECREMENT * 4;
  416. }
  417. //
  418. // Unlink the thread from the appropriate wait queues, set
  419. // the wait completion status, charge quantum for the wait,
  420. // and ready the thread for execution.
  421. //
  422. KiUnlinkThread(WaitThread, STATUS_SUCCESS);
  423. WaitThread->Quantum -= WAIT_QUANTUM_DECREMENT;
  424. KiReadyThread(WaitThread);
  425. }
  426. }
  427. //
  428. // Unlock dispatcher database lock and lower IRQL to its previous
  429. // value.
  430. //
  431. KiUnlockDispatcherDatabase(OldIrql);
  432. return;
  433. }