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 /* GLOBALS *******************************************************************/
29
30 PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent;
31 POBJECT_TYPE ExKeyedEventObjectType;
32
33 static
34 GENERIC_MAPPING ExpKeyedEventMapping =
35 {
36 STANDARD_RIGHTS_READ | KEYEDEVENT_WAIT,
37 STANDARD_RIGHTS_WRITE | KEYEDEVENT_WAKE,
38 STANDARD_RIGHTS_EXECUTE,
39 KEYEDEVENT_ALL_ACCESS
40 };
41
42 /* FUNCTIONS *****************************************************************/
43
44 _IRQL_requires_max_(APC_LEVEL)
45 CODE_SEG("INIT")
46 BOOLEAN
47 NTAPI
ExpInitializeKeyedEventImplementation(VOID)48 ExpInitializeKeyedEventImplementation(VOID)
49 {
50 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer = {0};
51 UNICODE_STRING TypeName = RTL_CONSTANT_STRING(L"KeyedEvent");
52 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\KernelObjects\\CritSecOutOfMemoryEvent");
53 NTSTATUS Status;
54 HANDLE EventHandle;
55 OBJECT_ATTRIBUTES ObjectAttributes;
56
57 /* Set up the object type initializer */
58 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
59 ObjectTypeInitializer.GenericMapping = ExpKeyedEventMapping;
60 ObjectTypeInitializer.PoolType = PagedPool;
61 ObjectTypeInitializer.ValidAccessMask = KEYEDEVENT_ALL_ACCESS;
62 ObjectTypeInitializer.UseDefaultObject = TRUE;
63
64 /* Create the keyed event object type */
65 Status = ObCreateObjectType(&TypeName,
66 &ObjectTypeInitializer,
67 NULL,
68 &ExKeyedEventObjectType);
69 if (!NT_SUCCESS(Status)) return FALSE;
70
71 /* Create the out of memory event for critical sections */
72 InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_PERMANENT, NULL, NULL);
73 Status = ZwCreateKeyedEvent(&EventHandle,
74 KEYEDEVENT_ALL_ACCESS,
75 &ObjectAttributes,
76 0);
77 if (NT_SUCCESS(Status))
78 {
79 /* Take a reference so we can get rid of the handle */
80 Status = ObReferenceObjectByHandle(EventHandle,
81 KEYEDEVENT_ALL_ACCESS,
82 ExKeyedEventObjectType,
83 KernelMode,
84 (PVOID*)&ExpCritSecOutOfMemoryEvent,
85 NULL);
86 ZwClose(EventHandle);
87 return TRUE;
88 }
89
90 return FALSE;
91 }
92
93 VOID
94 NTAPI
ExpInitializeKeyedEvent(_Out_ PEX_KEYED_EVENT KeyedEvent)95 ExpInitializeKeyedEvent(
96 _Out_ PEX_KEYED_EVENT KeyedEvent)
97 {
98 ULONG i;
99
100 /* Loop all hash buckets */
101 for (i = 0; i < NUM_KEY_HASH_BUCKETS; i++)
102 {
103 /* Initialize the mutex and the wait lists */
104 ExInitializePushLock(&KeyedEvent->HashTable[i].Lock);
105 InitializeListHead(&KeyedEvent->HashTable[i].WaitListHead);
106 InitializeListHead(&KeyedEvent->HashTable[i].ReleaseListHead);
107 }
108 }
109
_IRQL_requires_max_(APC_LEVEL)110 _IRQL_requires_max_(APC_LEVEL)
111 NTSTATUS
112 NTAPI
113 ExpReleaseOrWaitForKeyedEvent(
114 _Inout_ PEX_KEYED_EVENT KeyedEvent,
115 _In_ PVOID KeyedWaitValue,
116 _In_ BOOLEAN Alertable,
117 _In_opt_ PLARGE_INTEGER Timeout,
118 _In_ BOOLEAN Release)
119 {
120 PETHREAD Thread, CurrentThread;
121 PEPROCESS CurrentProcess;
122 PLIST_ENTRY ListEntry, WaitListHead1, WaitListHead2;
123 NTSTATUS Status;
124 ULONG_PTR HashIndex;
125 PVOID PreviousKeyedWaitValue;
126
127 /* Get the current process */
128 CurrentProcess = PsGetCurrentProcess();
129
130 /* Calculate the hash index */
131 HashIndex = (ULONG_PTR)KeyedWaitValue >> 5;
132 HashIndex ^= (ULONG_PTR)CurrentProcess >> 6;
133 HashIndex %= NUM_KEY_HASH_BUCKETS;
134
135 /* Lock the lists */
136 KeEnterCriticalRegion();
137 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
138
139 /* Get the lists for search and wait, depending on whether
140 we want to wait for the event or signal it */
141 if (Release)
142 {
143 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
144 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
145 }
146 else
147 {
148 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
149 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
150 }
151
152 /* loop the first wait list */
153 ListEntry = WaitListHead1->Flink;
154 while (ListEntry != WaitListHead1)
155 {
156 /* Get the waiting thread. Note that this thread cannot be terminated
157 as long as we hold the list lock, since it either needs to wait to
158 be signaled by this thread or, when the wait is aborted due to thread
159 termination, then it first needs to acquire the list lock. */
160 Thread = CONTAINING_RECORD(ListEntry, ETHREAD, KeyedWaitChain);
161 ListEntry = ListEntry->Flink;
162
163 /* Check if this thread is a correct waiter */
164 if ((Thread->Tcb.Process == &CurrentProcess->Pcb) &&
165 (Thread->KeyedWaitValue == KeyedWaitValue))
166 {
167 /* Remove the thread from the list */
168 RemoveEntryList(&Thread->KeyedWaitChain);
169
170 /* Initialize the list entry to show that it was removed */
171 InitializeListHead(&Thread->KeyedWaitChain);
172
173 /* Wake the thread */
174 KeReleaseSemaphore(&Thread->KeyedWaitSemaphore,
175 IO_NO_INCREMENT,
176 1,
177 FALSE);
178 Thread = NULL;
179
180 /* Unlock the list. After this it is not safe to access Thread */
181 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
182 KeLeaveCriticalRegion();
183
184 return STATUS_SUCCESS;
185 }
186 }
187
188 /* Get the current thread */
189 CurrentThread = PsGetCurrentThread();
190
191 /* Set the wait key and remember the old value */
192 PreviousKeyedWaitValue = CurrentThread->KeyedWaitValue;
193 CurrentThread->KeyedWaitValue = KeyedWaitValue;
194
195 /* Initialize the wait semaphore */
196 KeInitializeSemaphore(&CurrentThread->KeyedWaitSemaphore, 0, 1);
197
198 /* Insert the current thread into the secondary wait list */
199 InsertTailList(WaitListHead2, &CurrentThread->KeyedWaitChain);
200
201 /* Unlock the list */
202 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
203 KeLeaveCriticalRegion();
204
205 /* Wait for the keyed wait semaphore */
206 Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore,
207 WrKeyedEvent,
208 KernelMode,
209 Alertable,
210 Timeout);
211
212 /* Check if the wait was aborted or timed out */
213 if (Status != STATUS_SUCCESS)
214 {
215 /* Lock the lists to make sure no one else messes with the entry */
216 KeEnterCriticalRegion();
217 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
218
219 /* Check if the wait list entry is still in the list */
220 if (!IsListEmpty(&CurrentThread->KeyedWaitChain))
221 {
222 /* Remove the thread from the list */
223 RemoveEntryList(&CurrentThread->KeyedWaitChain);
224 InitializeListHead(&CurrentThread->KeyedWaitChain);
225 }
226
227 /* Unlock the list */
228 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
229 KeLeaveCriticalRegion();
230 }
231
232 /* Restore the previous KeyedWaitValue, since this is a union member */
233 CurrentThread->KeyedWaitValue = PreviousKeyedWaitValue;
234
235 return Status;
236 }
237
_IRQL_requires_max_(APC_LEVEL)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_opt_ PLARGE_INTEGER Timeout)
246 {
247 /* Call the generic internal function */
248 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
249 KeyedWaitValue,
250 Alertable,
251 Timeout,
252 FALSE);
253 }
254
_IRQL_requires_max_(APC_LEVEL)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
_IRQL_requires_max_(PASSIVE_LEVEL)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
_IRQL_requires_max_(PASSIVE_LEVEL)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
_IRQL_requires_max_(PASSIVE_LEVEL)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 /* Key must always be two-byte aligned */
416 if ((ULONG_PTR)Key & 1)
417 {
418 return STATUS_INVALID_PARAMETER_1;
419 }
420
421 /* Check if the caller passed a timeout value and this is from user mode */
422 if ((Timeout != NULL) && (PreviousMode != KernelMode))
423 {
424 _SEH2_TRY
425 {
426 ProbeForRead(Timeout, sizeof(*Timeout), 1);
427 TimeoutCopy = *Timeout;
428 Timeout = &TimeoutCopy;
429 }
430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
431 {
432 _SEH2_YIELD(return _SEH2_GetExceptionCode());
433 }
434 _SEH2_END;
435 }
436
437 /* Check if the caller provided a handle */
438 if (Handle != NULL)
439 {
440 /* Get the keyed event object */
441 Status = ObReferenceObjectByHandle(Handle,
442 KEYEDEVENT_WAIT,
443 ExKeyedEventObjectType,
444 PreviousMode,
445 (PVOID*)&KeyedEvent,
446 NULL);
447
448 /* Check for success */
449 if (!NT_SUCCESS(Status)) return Status;
450 }
451 else
452 {
453 /* Use the default keyed event for low memory critical sections */
454 KeyedEvent = ExpCritSecOutOfMemoryEvent;
455 }
456
457 /* Do the wait */
458 Status = ExpWaitForKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
459
460 if (Handle != NULL)
461 {
462 /* Dereference the keyed event */
463 ObDereferenceObject(KeyedEvent);
464 }
465
466 /* Return the status */
467 return Status;
468 }
469
_IRQL_requires_max_(PASSIVE_LEVEL)470 _IRQL_requires_max_(PASSIVE_LEVEL)
471 NTSTATUS
472 NTAPI
473 NtReleaseKeyedEvent(
474 _In_opt_ HANDLE Handle,
475 _In_ PVOID Key,
476 _In_ BOOLEAN Alertable,
477 _In_opt_ PLARGE_INTEGER Timeout)
478 {
479 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
480 PEX_KEYED_EVENT KeyedEvent;
481 NTSTATUS Status;
482 LARGE_INTEGER TimeoutCopy;
483
484 /* Key must always be two-byte aligned */
485 if ((ULONG_PTR)Key & 1)
486 {
487 return STATUS_INVALID_PARAMETER_1;
488 }
489
490 /* Check if the caller passed a timeout value and this is from user mode */
491 if ((Timeout != NULL) && (PreviousMode != KernelMode))
492 {
493 _SEH2_TRY
494 {
495 ProbeForRead(Timeout, sizeof(*Timeout), 1);
496 TimeoutCopy = *Timeout;
497 Timeout = &TimeoutCopy;
498 }
499 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
500 {
501 _SEH2_YIELD(return _SEH2_GetExceptionCode());
502 }
503 _SEH2_END;
504 }
505
506 /* Check if the caller provided a handle */
507 if (Handle != NULL)
508 {
509 /* Get the keyed event object */
510 Status = ObReferenceObjectByHandle(Handle,
511 KEYEDEVENT_WAKE,
512 ExKeyedEventObjectType,
513 PreviousMode,
514 (PVOID*)&KeyedEvent,
515 NULL);
516
517 /* Check for success */
518 if (!NT_SUCCESS(Status)) return Status;
519 }
520 else
521 {
522 /* Use the default keyed event for low memory critical sections */
523 KeyedEvent = ExpCritSecOutOfMemoryEvent;
524 }
525
526 /* Do the wait */
527 Status = ExpReleaseKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
528
529 if (Handle != NULL)
530 {
531 /* Dereference the keyed event */
532 ObDereferenceObject(KeyedEvent);
533 }
534
535 /* Return the status */
536 return Status;
537 }
538
539 /* EOF */
540