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
;
31 _Out_ PHANDLE OutHandle
,
32 _In_ ACCESS_MASK AccessMask
,
33 _In_ POBJECT_ATTRIBUTES ObjectAttributes
,
36 #define KeGetCurrentProcess() ((PKPROCESS)PsGetCurrentProcess())
38 /* GLOBALS *******************************************************************/
40 PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent
;
41 POBJECT_TYPE ExKeyedEventObjectType
;
44 GENERIC_MAPPING ExpKeyedEventMapping
=
46 STANDARD_RIGHTS_READ
| EVENT_QUERY_STATE
,
47 STANDARD_RIGHTS_WRITE
| EVENT_MODIFY_STATE
,
48 STANDARD_RIGHTS_EXECUTE
,
52 /* FUNCTIONS *****************************************************************/
54 _IRQL_requires_max_(APC_LEVEL
)
58 ExpInitializeKeyedEventImplementation(VOID
)
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");
65 OBJECT_ATTRIBUTES ObjectAttributes
;
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
;
74 /* Create the keyed event object type */
75 Status
= ObCreateObjectType(&TypeName
,
76 &ObjectTypeInitializer
,
78 &ExKeyedEventObjectType
);
79 if (!NT_SUCCESS(Status
)) return FALSE
;
81 /* Create the out of memory event for critical sections */
82 InitializeObjectAttributes(&ObjectAttributes
, &Name
, OBJ_PERMANENT
, NULL
, NULL
);
83 Status
= ZwCreateKeyedEvent(&EventHandle
,
87 if (NT_SUCCESS(Status
))
89 /* Take a reference so we can get rid of the handle */
90 Status
= ObReferenceObjectByHandle(EventHandle
,
92 ExKeyedEventObjectType
,
94 (PVOID
*)&ExpCritSecOutOfMemoryEvent
,
105 ExpInitializeKeyedEvent(
106 _Out_ PEX_KEYED_EVENT KeyedEvent
)
110 /* Loop all hash buckets */
111 for (i
= 0; i
< NUM_KEY_HASH_BUCKETS
; i
++)
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
);
120 _IRQL_requires_max_(APC_LEVEL
)
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
)
130 PETHREAD Thread
, CurrentThread
;
131 PKPROCESS CurrentProcess
;
132 PLIST_ENTRY ListEntry
, WaitListHead1
, WaitListHead2
;
136 /* Get the current process */
137 CurrentProcess
= KeGetCurrentProcess();
139 /* Calculate the hash index */
140 HashIndex
= (ULONG_PTR
)KeyedWaitValue
>> 5;
141 HashIndex
^= (ULONG_PTR
)CurrentProcess
>> 6;
142 HashIndex
%= NUM_KEY_HASH_BUCKETS
;
145 KeEnterCriticalRegion();
146 ExAcquirePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
148 /* Get the lists for search and wait, depending on whether
149 we want to wait for the event or signal it */
152 WaitListHead1
= &KeyedEvent
->HashTable
[HashIndex
].WaitListHead
;
153 WaitListHead2
= &KeyedEvent
->HashTable
[HashIndex
].ReleaseListHead
;
157 WaitListHead1
= &KeyedEvent
->HashTable
[HashIndex
].ReleaseListHead
;
158 WaitListHead2
= &KeyedEvent
->HashTable
[HashIndex
].WaitListHead
;
161 /* loop the first wait list */
162 ListEntry
= WaitListHead1
->Flink
;
163 while (ListEntry
!= WaitListHead1
)
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
);
171 /* Check if this thread is a correct waiter */
172 if ((Thread
->Tcb
.Process
== CurrentProcess
) &&
173 (Thread
->KeyedWaitValue
== KeyedWaitValue
))
175 /* Remove the thread from the list */
176 RemoveEntryList(&Thread
->KeyedWaitChain
);
178 /* Initialize the list entry to show that it was removed */
179 InitializeListHead(&Thread
->KeyedWaitChain
);
181 /* Wake the thread */
182 KeReleaseSemaphore(&Thread
->KeyedWaitSemaphore
, 0, 1, FALSE
);
185 /* Unlock the list. After this it is not safe to access Thread */
186 ExReleasePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
187 KeLeaveCriticalRegion();
189 return STATUS_SUCCESS
;
193 /* Get the current thread */
194 CurrentThread
= PsGetCurrentThread();
196 /* Set the wait key */
197 CurrentThread
->KeyedWaitValue
= KeyedWaitValue
;
199 /* Initialize the wait semaphore */
200 KeInitializeSemaphore(&CurrentThread
->KeyedWaitSemaphore
, 0, 1);
202 /* Insert the current thread into the secondary wait list */
203 InsertTailList(WaitListHead2
, &CurrentThread
->KeyedWaitChain
);
205 /* Unlock the list */
206 ExReleasePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
207 KeLeaveCriticalRegion();
209 /* Wait for the keyed wait semaphore */
210 Status
= KeWaitForSingleObject(&CurrentThread
->KeyedWaitSemaphore
,
216 /* Check if the wait was aborted or timed out */
217 if (Status
!= STATUS_SUCCESS
)
219 /* Lock the lists to make sure no one else messes with the entry */
220 KeEnterCriticalRegion();
221 ExAcquirePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
223 /* Check if the wait list entry is still in the list */
224 if (CurrentThread
->KeyedWaitChain
.Flink
!= &CurrentThread
->KeyedWaitChain
)
226 /* Remove the thread from the list */
227 RemoveEntryList(&CurrentThread
->KeyedWaitChain
);
230 /* Unlock the list */
231 ExReleasePushLockExclusive(&KeyedEvent
->HashTable
[HashIndex
].Lock
);
232 KeLeaveCriticalRegion();
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 /* Check if the caller passed a timeout value and this is from user mode */
416 if ((Timeout
!= NULL
) && (PreviousMode
!= KernelMode
))
420 ProbeForRead(Timeout
, sizeof(*Timeout
), 1);
421 TimeoutCopy
= *Timeout
;
422 Timeout
= &TimeoutCopy
;
424 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
426 _SEH2_YIELD(return _SEH2_GetExceptionCode());
431 /* Check if the caller provided a handle */
434 /* Get the keyed event object */
435 Status
= ObReferenceObjectByHandle(Handle
,
437 ExKeyedEventObjectType
,
442 /* Check for success */
443 if (!NT_SUCCESS(Status
)) return Status
;
447 /* Use the default keyed event for low memory critical sections */
448 KeyedEvent
= ExpCritSecOutOfMemoryEvent
;
452 Status
= ExpWaitForKeyedEvent(KeyedEvent
, Key
, Alertable
, Timeout
);
454 /* Dereference the keyed event */
455 ObDereferenceObject(KeyedEvent
);
457 /* Return the status */
461 _IRQL_requires_max_(PASSIVE_LEVEL
)
465 _In_opt_ HANDLE Handle
,
467 _In_ BOOLEAN Alertable
,
468 _In_opt_ PLARGE_INTEGER Timeout
)
470 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
471 PEX_KEYED_EVENT KeyedEvent
;
473 LARGE_INTEGER TimeoutCopy
;
475 /* Check if the caller passed a timeout value and this is from user mode */
476 if ((Timeout
!= NULL
) && (PreviousMode
!= KernelMode
))
480 ProbeForRead(Timeout
, sizeof(*Timeout
), 1);
481 TimeoutCopy
= *Timeout
;
482 Timeout
= &TimeoutCopy
;
484 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
486 _SEH2_YIELD(return _SEH2_GetExceptionCode());
491 /* Check if the caller provided a handle */
494 /* Get the keyed event object */
495 Status
= ObReferenceObjectByHandle(Handle
,
497 ExKeyedEventObjectType
,
502 /* Check for success */
503 if (!NT_SUCCESS(Status
)) return Status
;
507 /* Use the default keyed event for low memory critical sections */
508 KeyedEvent
= ExpCritSecOutOfMemoryEvent
;
512 Status
= ExpReleaseKeyedEvent(KeyedEvent
, Key
, Alertable
, Timeout
);
514 /* Dereference the keyed event */
515 ObDereferenceObject(KeyedEvent
);
517 /* Return the status */