Sync with trunk r63831.
[reactos.git] / ntoskrnl / ex / keyedevt.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/keyedevt.c
5 * PURPOSE: Support for keyed events
6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* INTERNAL TYPES *************************************************************/
16
17 #define NUM_KEY_HASH_BUCKETS 23
18 typedef struct _EX_KEYED_EVENT
19 {
20 struct
21 {
22 EX_PUSH_LOCK Lock;
23 LIST_ENTRY WaitListHead;
24 LIST_ENTRY ReleaseListHead;
25 } HashTable[NUM_KEY_HASH_BUCKETS];
26 } EX_KEYED_EVENT, *PEX_KEYED_EVENT;
27
28 NTSTATUS
29 NTAPI
30 ZwCreateKeyedEvent(
31 _Out_ PHANDLE OutHandle,
32 _In_ ACCESS_MASK AccessMask,
33 _In_ POBJECT_ATTRIBUTES ObjectAttributes,
34 _In_ ULONG Flags);
35
36 #define KeGetCurrentProcess() ((PKPROCESS)PsGetCurrentProcess())
37
38 /* GLOBALS *******************************************************************/
39
40 PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent;
41 POBJECT_TYPE ExKeyedEventObjectType;
42
43 static
44 GENERIC_MAPPING ExpKeyedEventMapping =
45 {
46 STANDARD_RIGHTS_READ | EVENT_QUERY_STATE,
47 STANDARD_RIGHTS_WRITE | EVENT_MODIFY_STATE,
48 STANDARD_RIGHTS_EXECUTE,
49 EVENT_ALL_ACCESS
50 };
51
52 /* FUNCTIONS *****************************************************************/
53
54 _IRQL_requires_max_(APC_LEVEL)
55 BOOLEAN
56 INIT_FUNCTION
57 NTAPI
58 ExpInitializeKeyedEventImplementation(VOID)
59 {
60 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer = {0};
61 UNICODE_STRING TypeName = RTL_CONSTANT_STRING(L"KeyedEvent");
62 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\KernelObjects\\CritSecOutOfMemoryEvent");
63 NTSTATUS Status;
64 HANDLE EventHandle;
65 OBJECT_ATTRIBUTES ObjectAttributes;
66
67 /* Set up the object type initializer */
68 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
69 ObjectTypeInitializer.GenericMapping = ExpKeyedEventMapping;
70 ObjectTypeInitializer.PoolType = PagedPool;
71 ObjectTypeInitializer.ValidAccessMask = EVENT_ALL_ACCESS;
72 ObjectTypeInitializer.UseDefaultObject = TRUE;
73
74 /* Create the keyed event object type */
75 Status = ObCreateObjectType(&TypeName,
76 &ObjectTypeInitializer,
77 NULL,
78 &ExKeyedEventObjectType);
79 if (!NT_SUCCESS(Status)) return FALSE;
80
81 /* Create the out of memory event for critical sections */
82 InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_PERMANENT, NULL, NULL);
83 Status = ZwCreateKeyedEvent(&EventHandle,
84 EVENT_ALL_ACCESS,
85 &ObjectAttributes,
86 0);
87 if (NT_SUCCESS(Status))
88 {
89 /* Take a reference so we can get rid of the handle */
90 Status = ObReferenceObjectByHandle(EventHandle,
91 EVENT_ALL_ACCESS,
92 ExKeyedEventObjectType,
93 KernelMode,
94 (PVOID*)&ExpCritSecOutOfMemoryEvent,
95 NULL);
96 ZwClose(EventHandle);
97 return TRUE;
98 }
99
100 return FALSE;
101 }
102
103 VOID
104 NTAPI
105 ExpInitializeKeyedEvent(
106 _Out_ PEX_KEYED_EVENT KeyedEvent)
107 {
108 ULONG i;
109
110 /* Loop all hash buckets */
111 for (i = 0; i < NUM_KEY_HASH_BUCKETS; i++)
112 {
113 /* Initialize the mutex and the wait lists */
114 ExInitializePushLock(&KeyedEvent->HashTable[i].Lock);
115 InitializeListHead(&KeyedEvent->HashTable[i].WaitListHead);
116 InitializeListHead(&KeyedEvent->HashTable[i].ReleaseListHead);
117 }
118 }
119
120 _IRQL_requires_max_(APC_LEVEL)
121 NTSTATUS
122 NTAPI
123 ExpReleaseOrWaitForKeyedEvent(
124 _Inout_ PEX_KEYED_EVENT KeyedEvent,
125 _In_ PVOID KeyedWaitValue,
126 _In_ BOOLEAN Alertable,
127 _In_ PLARGE_INTEGER Timeout,
128 _In_ BOOLEAN Release)
129 {
130 PETHREAD Thread, CurrentThread;
131 PKPROCESS CurrentProcess;
132 PLIST_ENTRY ListEntry, WaitListHead1, WaitListHead2;
133 NTSTATUS Status;
134 ULONG_PTR HashIndex;
135
136 /* Get the current process */
137 CurrentProcess = KeGetCurrentProcess();
138
139 /* Calculate the hash index */
140 HashIndex = (ULONG_PTR)KeyedWaitValue >> 5;
141 HashIndex ^= (ULONG_PTR)CurrentProcess >> 6;
142 HashIndex %= NUM_KEY_HASH_BUCKETS;
143
144 /* Lock the lists */
145 KeEnterCriticalRegion();
146 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
147
148 /* Get the lists for search and wait, depending on whether
149 we want to wait for the event or signal it */
150 if (Release)
151 {
152 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
153 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
154 }
155 else
156 {
157 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
158 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
159 }
160
161 /* loop the first wait list */
162 ListEntry = WaitListHead1->Flink;
163 while (ListEntry != WaitListHead1)
164 {
165 /* Get the waiting thread. Note that this thread cannot be terminated
166 as long as we hold the list lock, since it either needs to wait to
167 be signaled by this thread or, when the wait is aborted due to thread
168 termination, then it first needs to acquire the list lock. */
169 Thread = CONTAINING_RECORD(ListEntry, ETHREAD, KeyedWaitChain);
170
171 /* Check if this thread is a correct waiter */
172 if ((Thread->Tcb.Process == CurrentProcess) &&
173 (Thread->KeyedWaitValue == KeyedWaitValue))
174 {
175 /* Remove the thread from the list */
176 RemoveEntryList(&Thread->KeyedWaitChain);
177
178 /* Initialize the list entry to show that it was removed */
179 InitializeListHead(&Thread->KeyedWaitChain);
180
181 /* Wake the thread */
182 KeReleaseSemaphore(&Thread->KeyedWaitSemaphore, 0, 1, FALSE);
183 Thread = NULL;
184
185 /* Unlock the list. After this it is not safe to access Thread */
186 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
187 KeLeaveCriticalRegion();
188
189 return STATUS_SUCCESS;
190 }
191 }
192
193 /* Get the current thread */
194 CurrentThread = PsGetCurrentThread();
195
196 /* Set the wait key */
197 CurrentThread->KeyedWaitValue = KeyedWaitValue;
198
199 /* Initialize the wait semaphore */
200 KeInitializeSemaphore(&CurrentThread->KeyedWaitSemaphore, 0, 1);
201
202 /* Insert the current thread into the secondary wait list */
203 InsertTailList(WaitListHead2, &CurrentThread->KeyedWaitChain);
204
205 /* Unlock the list */
206 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
207 KeLeaveCriticalRegion();
208
209 /* Wait for the keyed wait semaphore */
210 Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore,
211 WrKeyedEvent,
212 KernelMode,
213 Alertable,
214 Timeout);
215
216 /* Check if the wait was aborted or timed out */
217 if (Status != STATUS_SUCCESS)
218 {
219 /* Lock the lists to make sure no one else messes with the entry */
220 KeEnterCriticalRegion();
221 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
222
223 /* Check if the wait list entry is still in the list */
224 if (CurrentThread->KeyedWaitChain.Flink != &CurrentThread->KeyedWaitChain)
225 {
226 /* Remove the thread from the list */
227 RemoveEntryList(&CurrentThread->KeyedWaitChain);
228 }
229
230 /* Unlock the list */
231 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
232 KeLeaveCriticalRegion();
233 }
234
235 return Status;
236 }
237
238 _IRQL_requires_max_(APC_LEVEL)
239 NTSTATUS
240 NTAPI
241 ExpWaitForKeyedEvent(
242 _Inout_ PEX_KEYED_EVENT KeyedEvent,
243 _In_ PVOID KeyedWaitValue,
244 _In_ BOOLEAN Alertable,
245 _In_ PLARGE_INTEGER Timeout)
246 {
247 /* Call the generic internal function */
248 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
249 KeyedWaitValue,
250 Alertable,
251 Timeout,
252 FALSE);
253 }
254
255 _IRQL_requires_max_(APC_LEVEL)
256 NTSTATUS
257 NTAPI
258 ExpReleaseKeyedEvent(
259 _Inout_ PEX_KEYED_EVENT KeyedEvent,
260 _In_ PVOID KeyedWaitValue,
261 _In_ BOOLEAN Alertable,
262 _In_ PLARGE_INTEGER Timeout)
263 {
264 /* Call the generic internal function */
265 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
266 KeyedWaitValue,
267 Alertable,
268 Timeout,
269 TRUE);
270 }
271
272 _IRQL_requires_max_(PASSIVE_LEVEL)
273 NTSTATUS
274 NTAPI
275 NtCreateKeyedEvent(
276 _Out_ PHANDLE OutHandle,
277 _In_ ACCESS_MASK AccessMask,
278 _In_ POBJECT_ATTRIBUTES ObjectAttributes,
279 _In_ ULONG Flags)
280 {
281 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
282 PEX_KEYED_EVENT KeyedEvent;
283 HANDLE KeyedEventHandle;
284 NTSTATUS Status;
285
286 /* Check flags */
287 if (Flags != 0)
288 {
289 /* We don't support any flags yet */
290 return STATUS_INVALID_PARAMETER;
291 }
292
293 /* Create the object */
294 Status = ObCreateObject(PreviousMode,
295 ExKeyedEventObjectType,
296 ObjectAttributes,
297 PreviousMode,
298 NULL,
299 sizeof(EX_KEYED_EVENT),
300 0,
301 0,
302 (PVOID*)&KeyedEvent);
303
304 /* Check for success */
305 if (!NT_SUCCESS(Status)) return Status;
306
307 /* Initialize the keyed event */
308 ExpInitializeKeyedEvent(KeyedEvent);
309
310 /* Insert it */
311 Status = ObInsertObject(KeyedEvent,
312 NULL,
313 AccessMask,
314 0,
315 NULL,
316 &KeyedEventHandle);
317
318 /* Check for success (ObInsertObject dereferences!) */
319 if (!NT_SUCCESS(Status)) return Status;
320
321 if (PreviousMode != KernelMode)
322 {
323 /* Enter SEH for return */
324 _SEH2_TRY
325 {
326 /* Return the handle to the caller */
327 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
328 *OutHandle = KeyedEventHandle;
329 }
330 _SEH2_EXCEPT(ExSystemExceptionFilter())
331 {
332 /* Get the exception code */
333 Status = _SEH2_GetExceptionCode();
334
335 /* Cleanup */
336 ObCloseHandle(KeyedEventHandle, PreviousMode);
337 }
338 _SEH2_END;
339 }
340 else
341 {
342 *OutHandle = KeyedEventHandle;
343 }
344
345 /* Return Status */
346 return Status;
347 }
348
349 _IRQL_requires_max_(PASSIVE_LEVEL)
350 NTSTATUS
351 NTAPI
352 NtOpenKeyedEvent(
353 _Out_ PHANDLE OutHandle,
354 _In_ ACCESS_MASK AccessMask,
355 _In_ POBJECT_ATTRIBUTES ObjectAttributes)
356 {
357 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
358 HANDLE KeyedEventHandle;
359 NTSTATUS Status;
360
361 /* Open the object */
362 Status = ObOpenObjectByName(ObjectAttributes,
363 ExKeyedEventObjectType,
364 PreviousMode,
365 NULL,
366 AccessMask,
367 NULL,
368 &KeyedEventHandle);
369
370 /* Check for success */
371 if (!NT_SUCCESS(Status)) return Status;
372
373 /* Enter SEH for return */
374 if (PreviousMode != KernelMode)
375 {
376 _SEH2_TRY
377 {
378 /* Return the handle to the caller */
379 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
380 *OutHandle = KeyedEventHandle;
381 }
382 _SEH2_EXCEPT(ExSystemExceptionFilter())
383 {
384 /* Get the exception code */
385 Status = _SEH2_GetExceptionCode();
386
387 /* Cleanup */
388 ObCloseHandle(KeyedEventHandle, PreviousMode);
389 }
390 _SEH2_END;
391 }
392 else
393 {
394 *OutHandle = KeyedEventHandle;
395 }
396
397 /* Return status */
398 return Status;
399 }
400
401 _IRQL_requires_max_(PASSIVE_LEVEL)
402 NTSTATUS
403 NTAPI
404 NtWaitForKeyedEvent(
405 _In_opt_ HANDLE Handle,
406 _In_ PVOID Key,
407 _In_ BOOLEAN Alertable,
408 _In_opt_ PLARGE_INTEGER Timeout)
409 {
410 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
411 PEX_KEYED_EVENT KeyedEvent;
412 NTSTATUS Status;
413 LARGE_INTEGER TimeoutCopy;
414
415 /* Check if the caller passed a timeout value and this is from user mode */
416 if ((Timeout != NULL) && (PreviousMode != KernelMode))
417 {
418 _SEH2_TRY
419 {
420 ProbeForRead(Timeout, sizeof(*Timeout), 1);
421 TimeoutCopy = *Timeout;
422 Timeout = &TimeoutCopy;
423 }
424 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
425 {
426 _SEH2_YIELD(return _SEH2_GetExceptionCode());
427 }
428 _SEH2_END;
429 }
430
431 /* Check if the caller provided a handle */
432 if (Handle != NULL)
433 {
434 /* Get the keyed event object */
435 Status = ObReferenceObjectByHandle(Handle,
436 EVENT_MODIFY_STATE,
437 ExKeyedEventObjectType,
438 PreviousMode,
439 (PVOID*)&KeyedEvent,
440 NULL);
441
442 /* Check for success */
443 if (!NT_SUCCESS(Status)) return Status;
444 }
445 else
446 {
447 /* Use the default keyed event for low memory critical sections */
448 KeyedEvent = ExpCritSecOutOfMemoryEvent;
449 }
450
451 /* Do the wait */
452 Status = ExpWaitForKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
453
454 /* Dereference the keyed event */
455 ObDereferenceObject(KeyedEvent);
456
457 /* Return the status */
458 return Status;
459 }
460
461 _IRQL_requires_max_(PASSIVE_LEVEL)
462 NTSTATUS
463 NTAPI
464 NtReleaseKeyedEvent(
465 _In_opt_ HANDLE Handle,
466 _In_ PVOID Key,
467 _In_ BOOLEAN Alertable,
468 _In_opt_ PLARGE_INTEGER Timeout)
469 {
470 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
471 PEX_KEYED_EVENT KeyedEvent;
472 NTSTATUS Status;
473 LARGE_INTEGER TimeoutCopy;
474
475 /* Check if the caller passed a timeout value and this is from user mode */
476 if ((Timeout != NULL) && (PreviousMode != KernelMode))
477 {
478 _SEH2_TRY
479 {
480 ProbeForRead(Timeout, sizeof(*Timeout), 1);
481 TimeoutCopy = *Timeout;
482 Timeout = &TimeoutCopy;
483 }
484 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
485 {
486 _SEH2_YIELD(return _SEH2_GetExceptionCode());
487 }
488 _SEH2_END;
489 }
490
491 /* Check if the caller provided a handle */
492 if (Handle != NULL)
493 {
494 /* Get the keyed event object */
495 Status = ObReferenceObjectByHandle(Handle,
496 EVENT_MODIFY_STATE,
497 ExKeyedEventObjectType,
498 PreviousMode,
499 (PVOID*)&KeyedEvent,
500 NULL);
501
502 /* Check for success */
503 if (!NT_SUCCESS(Status)) return Status;
504 }
505 else
506 {
507 /* Use the default keyed event for low memory critical sections */
508 KeyedEvent = ExpCritSecOutOfMemoryEvent;
509 }
510
511 /* Do the wait */
512 Status = ExpReleaseKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
513
514 /* Dereference the keyed event */
515 ObDereferenceObject(KeyedEvent);
516
517 /* Return the status */
518 return Status;
519 }
520
521 /* EOF */