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)
9 /* INCLUDES *****************************************************************/
15 /* INTERNAL TYPES *************************************************************/
17 #define NUM_KEY_HASH_BUCKETS 23
18 typedef struct _EX_KEYED_EVENT
23 LIST_ENTRY WaitListHead
;
24 LIST_ENTRY ReleaseListHead
;
25 } HashTable
[NUM_KEY_HASH_BUCKETS
];
26 } EX_KEYED_EVENT
, *PEX_KEYED_EVENT
;
28 /* GLOBALS *******************************************************************/
30 PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent
;
31 POBJECT_TYPE ExKeyedEventObjectType
;
34 GENERIC_MAPPING ExpKeyedEventMapping
=
36 STANDARD_RIGHTS_READ
| KEYEDEVENT_WAIT
,
37 STANDARD_RIGHTS_WRITE
| KEYEDEVENT_WAKE
,
38 STANDARD_RIGHTS_EXECUTE
,
42 /* FUNCTIONS *****************************************************************/
44 _IRQL_requires_max_(APC_LEVEL
)
48 ExpInitializeKeyedEventImplementation(VOID
)
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");
55 OBJECT_ATTRIBUTES ObjectAttributes
;
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
;
64 /* Create the keyed event object type */
65 Status
= ObCreateObjectType(&TypeName
,
66 &ObjectTypeInitializer
,
68 &ExKeyedEventObjectType
);
69 if (!NT_SUCCESS(Status
)) return FALSE
;
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
,
77 if (NT_SUCCESS(Status
))
79 /* Take a reference so we can get rid of the handle */
80 Status
= ObReferenceObjectByHandle(EventHandle
,
81 KEYEDEVENT_ALL_ACCESS
,
82 ExKeyedEventObjectType
,
84 (PVOID
*)&ExpCritSecOutOfMemoryEvent
,
95 ExpInitializeKeyedEvent(
96 _Out_ PEX_KEYED_EVENT KeyedEvent
)
100 /* Loop all hash buckets */
101 for (i
= 0; i
< NUM_KEY_HASH_BUCKETS
; i
++)
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
);
110 _IRQL_requires_max_(APC_LEVEL
)
113 ExpReleaseOrWaitForKeyedEvent(
114 _Inout_ PEX_KEYED_EVENT KeyedEvent
,
115 _In_ PVOID KeyedWaitValue
,
116 _In_ BOOLEAN Alertable
,
117 _In_ PLARGE_INTEGER Timeout
,
118 _In_ BOOLEAN Release
)
120 PETHREAD Thread
, CurrentThread
;
121 PEPROCESS CurrentProcess
;
122 PLIST_ENTRY ListEntry
, WaitListHead1
, WaitListHead2
;
125 PVOID PreviousKeyedWaitValue
;
127 /* Get the current process */
128 CurrentProcess
= PsGetCurrentProcess();
130 /* Calculate the hash index */
131 HashIndex
= (ULONG_PTR
)KeyedWaitValue
>> 5;
132 HashIndex
^= (ULONG_PTR
)CurrentProcess
>> 6;
133 HashIndex
%= NUM_KEY_HASH_BUCKETS
;
136 KeEnterCriticalRegion();
137 ExAcquirePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
139 /* Get the lists for search and wait, depending on whether
140 we want to wait for the event or signal it */
143 WaitListHead1
= &KeyedEvent
->HashTable
[HashIndex
].WaitListHead
;
144 WaitListHead2
= &KeyedEvent
->HashTable
[HashIndex
].ReleaseListHead
;
148 WaitListHead1
= &KeyedEvent
->HashTable
[HashIndex
].ReleaseListHead
;
149 WaitListHead2
= &KeyedEvent
->HashTable
[HashIndex
].WaitListHead
;
152 /* loop the first wait list */
153 ListEntry
= WaitListHead1
->Flink
;
154 while (ListEntry
!= WaitListHead1
)
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
;
163 /* Check if this thread is a correct waiter */
164 if ((Thread
->Tcb
.Process
== &CurrentProcess
->Pcb
) &&
165 (Thread
->KeyedWaitValue
== KeyedWaitValue
))
167 /* Remove the thread from the list */
168 RemoveEntryList(&Thread
->KeyedWaitChain
);
170 /* Initialize the list entry to show that it was removed */
171 InitializeListHead(&Thread
->KeyedWaitChain
);
173 /* Wake the thread */
174 KeReleaseSemaphore(&Thread
->KeyedWaitSemaphore
,
180 /* Unlock the list. After this it is not safe to access Thread */
181 ExReleasePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
182 KeLeaveCriticalRegion();
184 return STATUS_SUCCESS
;
188 /* Get the current thread */
189 CurrentThread
= PsGetCurrentThread();
191 /* Set the wait key and remember the old value */
192 PreviousKeyedWaitValue
= CurrentThread
->KeyedWaitValue
;
193 CurrentThread
->KeyedWaitValue
= KeyedWaitValue
;
195 /* Initialize the wait semaphore */
196 KeInitializeSemaphore(&CurrentThread
->KeyedWaitSemaphore
, 0, 1);
198 /* Insert the current thread into the secondary wait list */
199 InsertTailList(WaitListHead2
, &CurrentThread
->KeyedWaitChain
);
201 /* Unlock the list */
202 ExReleasePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
203 KeLeaveCriticalRegion();
205 /* Wait for the keyed wait semaphore */
206 Status
= KeWaitForSingleObject(&CurrentThread
->KeyedWaitSemaphore
,
212 /* Check if the wait was aborted or timed out */
213 if (Status
!= STATUS_SUCCESS
)
215 /* Lock the lists to make sure no one else messes with the entry */
216 KeEnterCriticalRegion();
217 ExAcquirePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
219 /* Check if the wait list entry is still in the list */
220 if (!IsListEmpty(&CurrentThread
->KeyedWaitChain
))
222 /* Remove the thread from the list */
223 RemoveEntryList(&CurrentThread
->KeyedWaitChain
);
224 InitializeListHead(&CurrentThread
->KeyedWaitChain
);
227 /* Unlock the list */
228 ExReleasePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
229 KeLeaveCriticalRegion();
232 /* Restore the previous KeyedWaitValue, since this is a union member */
233 CurrentThread
->KeyedWaitValue
= PreviousKeyedWaitValue
;
238 _IRQL_requires_max_(APC_LEVEL
)
241 ExpWaitForKeyedEvent(
242 _Inout_ PEX_KEYED_EVENT KeyedEvent
,
243 _In_ PVOID KeyedWaitValue
,
244 _In_ BOOLEAN Alertable
,
245 _In_ PLARGE_INTEGER Timeout
)
247 /* Call the generic internal function */
248 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent
,
255 _IRQL_requires_max_(APC_LEVEL
)
258 ExpReleaseKeyedEvent(
259 _Inout_ PEX_KEYED_EVENT KeyedEvent
,
260 _In_ PVOID KeyedWaitValue
,
261 _In_ BOOLEAN Alertable
,
262 _In_ PLARGE_INTEGER Timeout
)
264 /* Call the generic internal function */
265 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent
,
272 _IRQL_requires_max_(PASSIVE_LEVEL
)
276 _Out_ PHANDLE OutHandle
,
277 _In_ ACCESS_MASK AccessMask
,
278 _In_ POBJECT_ATTRIBUTES ObjectAttributes
,
281 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
282 PEX_KEYED_EVENT KeyedEvent
;
283 HANDLE KeyedEventHandle
;
289 /* We don't support any flags yet */
290 return STATUS_INVALID_PARAMETER
;
293 /* Create the object */
294 Status
= ObCreateObject(PreviousMode
,
295 ExKeyedEventObjectType
,
299 sizeof(EX_KEYED_EVENT
),
302 (PVOID
*)&KeyedEvent
);
304 /* Check for success */
305 if (!NT_SUCCESS(Status
)) return Status
;
307 /* Initialize the keyed event */
308 ExpInitializeKeyedEvent(KeyedEvent
);
311 Status
= ObInsertObject(KeyedEvent
,
318 /* Check for success (ObInsertObject dereferences!) */
319 if (!NT_SUCCESS(Status
)) return Status
;
321 if (PreviousMode
!= KernelMode
)
323 /* Enter SEH for return */
326 /* Return the handle to the caller */
327 ProbeForWrite(OutHandle
, sizeof(HANDLE
), sizeof(HANDLE
));
328 *OutHandle
= KeyedEventHandle
;
330 _SEH2_EXCEPT(ExSystemExceptionFilter())
332 /* Get the exception code */
333 Status
= _SEH2_GetExceptionCode();
336 ObCloseHandle(KeyedEventHandle
, PreviousMode
);
342 *OutHandle
= KeyedEventHandle
;
349 _IRQL_requires_max_(PASSIVE_LEVEL
)
353 _Out_ PHANDLE OutHandle
,
354 _In_ ACCESS_MASK AccessMask
,
355 _In_ POBJECT_ATTRIBUTES ObjectAttributes
)
357 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
358 HANDLE KeyedEventHandle
;
361 /* Open the object */
362 Status
= ObOpenObjectByName(ObjectAttributes
,
363 ExKeyedEventObjectType
,
370 /* Check for success */
371 if (!NT_SUCCESS(Status
)) return Status
;
373 /* Enter SEH for return */
374 if (PreviousMode
!= KernelMode
)
378 /* Return the handle to the caller */
379 ProbeForWrite(OutHandle
, sizeof(HANDLE
), sizeof(HANDLE
));
380 *OutHandle
= KeyedEventHandle
;
382 _SEH2_EXCEPT(ExSystemExceptionFilter())
384 /* Get the exception code */
385 Status
= _SEH2_GetExceptionCode();
388 ObCloseHandle(KeyedEventHandle
, PreviousMode
);
394 *OutHandle
= KeyedEventHandle
;
401 _IRQL_requires_max_(PASSIVE_LEVEL
)
405 _In_opt_ HANDLE Handle
,
407 _In_ BOOLEAN Alertable
,
408 _In_opt_ PLARGE_INTEGER Timeout
)
410 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
411 PEX_KEYED_EVENT KeyedEvent
;
413 LARGE_INTEGER TimeoutCopy
;
415 /* Key must always be two-byte aligned */
416 if ((ULONG_PTR
)Key
& 1)
418 return STATUS_INVALID_PARAMETER_1
;
421 /* Check if the caller passed a timeout value and this is from user mode */
422 if ((Timeout
!= NULL
) && (PreviousMode
!= KernelMode
))
426 ProbeForRead(Timeout
, sizeof(*Timeout
), 1);
427 TimeoutCopy
= *Timeout
;
428 Timeout
= &TimeoutCopy
;
430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
432 _SEH2_YIELD(return _SEH2_GetExceptionCode());
437 /* Check if the caller provided a handle */
440 /* Get the keyed event object */
441 Status
= ObReferenceObjectByHandle(Handle
,
443 ExKeyedEventObjectType
,
448 /* Check for success */
449 if (!NT_SUCCESS(Status
)) return Status
;
453 /* Use the default keyed event for low memory critical sections */
454 KeyedEvent
= ExpCritSecOutOfMemoryEvent
;
458 Status
= ExpWaitForKeyedEvent(KeyedEvent
, Key
, Alertable
, Timeout
);
460 /* Dereference the keyed event */
461 ObDereferenceObject(KeyedEvent
);
463 /* Return the status */
467 _IRQL_requires_max_(PASSIVE_LEVEL
)
471 _In_opt_ HANDLE Handle
,
473 _In_ BOOLEAN Alertable
,
474 _In_opt_ PLARGE_INTEGER Timeout
)
476 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
477 PEX_KEYED_EVENT KeyedEvent
;
479 LARGE_INTEGER TimeoutCopy
;
481 /* Key must always be two-byte aligned */
482 if ((ULONG_PTR
)Key
& 1)
484 return STATUS_INVALID_PARAMETER_1
;
487 /* Check if the caller passed a timeout value and this is from user mode */
488 if ((Timeout
!= NULL
) && (PreviousMode
!= KernelMode
))
492 ProbeForRead(Timeout
, sizeof(*Timeout
), 1);
493 TimeoutCopy
= *Timeout
;
494 Timeout
= &TimeoutCopy
;
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
498 _SEH2_YIELD(return _SEH2_GetExceptionCode());
503 /* Check if the caller provided a handle */
506 /* Get the keyed event object */
507 Status
= ObReferenceObjectByHandle(Handle
,
509 ExKeyedEventObjectType
,
514 /* Check for success */
515 if (!NT_SUCCESS(Status
)) return Status
;
519 /* Use the default keyed event for low memory critical sections */
520 KeyedEvent
= ExpCritSecOutOfMemoryEvent
;
524 Status
= ExpReleaseKeyedEvent(KeyedEvent
, Key
, Alertable
, Timeout
);
526 /* Dereference the keyed event */
527 ObDereferenceObject(KeyedEvent
);
529 /* Return the status */