[NTOSKRNL]
[reactos.git] / reactos / 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 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
146
147 /* Get the lists for search and wait, depending on whether
148 we want to wait for the event or signal it */
149 if (Release)
150 {
151 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
152 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
153 }
154 else
155 {
156 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
157 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
158 }
159
160 /* loop the first wait list */
161 ListEntry = WaitListHead1->Flink;
162 while (ListEntry != WaitListHead1)
163 {
164 Thread = CONTAINING_RECORD(ListEntry, ETHREAD, KeyedWaitChain);
165
166 /* Check if this thread is a correct waiter */
167 if ((Thread->Tcb.Process == CurrentProcess) &&
168 (Thread->KeyedWaitValue == KeyedWaitValue))
169 {
170 /* Remove the thread from the list */
171 RemoveEntryList(&Thread->KeyedWaitChain);
172
173 /* Wake the thread */
174 KeReleaseSemaphore(&Thread->KeyedWaitSemaphore, 0, 1, FALSE);
175
176 /* Unlock the lists */
177 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
178
179 return STATUS_SUCCESS;
180 }
181 }
182
183 /* Get the current thread */
184 CurrentThread = PsGetCurrentThread();
185
186 /* Set the wait key */
187 CurrentThread->KeyedWaitValue = KeyedWaitValue;
188
189 /* Initialize the wait semaphore */
190 KeInitializeSemaphore(&CurrentThread->KeyedWaitSemaphore, 0, 1);
191
192 /* Insert the current thread into the secondary wait list */
193 InsertTailList(WaitListHead2, &CurrentThread->KeyedWaitChain);
194
195 /* Unlock the lists */
196 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
197
198 /* Wait for the keyed wait semaphore */
199 Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore,
200 WrKeyedEvent,
201 KernelMode,
202 Alertable,
203 Timeout);
204
205 return STATUS_SUCCESS;
206 }
207
208 _IRQL_requires_max_(APC_LEVEL)
209 NTSTATUS
210 NTAPI
211 ExpWaitForKeyedEvent(
212 _Inout_ PEX_KEYED_EVENT KeyedEvent,
213 _In_ PVOID KeyedWaitValue,
214 _In_ BOOLEAN Alertable,
215 _In_ PLARGE_INTEGER Timeout)
216 {
217 /* Call the generic internal function */
218 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
219 KeyedWaitValue,
220 Alertable,
221 Timeout,
222 FALSE);
223 }
224
225 _IRQL_requires_max_(APC_LEVEL)
226 NTSTATUS
227 NTAPI
228 ExpReleaseKeyedEvent(
229 _Inout_ PEX_KEYED_EVENT KeyedEvent,
230 _In_ PVOID KeyedWaitValue,
231 _In_ BOOLEAN Alertable,
232 _In_ PLARGE_INTEGER Timeout)
233 {
234 /* Call the generic internal function */
235 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
236 KeyedWaitValue,
237 Alertable,
238 Timeout,
239 TRUE);
240 }
241
242 _IRQL_requires_max_(APC_LEVEL)
243 NTSTATUS
244 NTAPI
245 NtCreateKeyedEvent(
246 _Out_ PHANDLE OutHandle,
247 _In_ ACCESS_MASK AccessMask,
248 _In_ POBJECT_ATTRIBUTES ObjectAttributes,
249 _In_ ULONG Flags)
250 {
251 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
252 PEX_KEYED_EVENT KeyedEvent;
253 HANDLE KeyedEventHandle;
254 NTSTATUS Status;
255
256 /* Check flags */
257 if (Flags != 0)
258 {
259 /* We don't support any flags yet */
260 return STATUS_INVALID_PARAMETER;
261 }
262
263 /* Create the object */
264 Status = ObCreateObject(PreviousMode,
265 ExKeyedEventObjectType,
266 ObjectAttributes,
267 PreviousMode,
268 NULL,
269 sizeof(EX_KEYED_EVENT),
270 0,
271 0,
272 (PVOID*)&KeyedEvent);
273
274 /* Check for success */
275 if (!NT_SUCCESS(Status)) return Status;
276
277 /* Initalize the keyed event */
278 ExpInitializeKeyedEvent(KeyedEvent);
279
280 /* Insert it */
281 Status = ObInsertObject(KeyedEvent,
282 NULL,
283 AccessMask,
284 0,
285 NULL,
286 &KeyedEventHandle);
287
288 /* Check for success (ObInsertObject dereferences!) */
289 if (!NT_SUCCESS(Status)) return Status;
290
291 if (PreviousMode != KernelMode)
292 {
293 /* Enter SEH for return */
294 _SEH2_TRY
295 {
296 /* Return the handle to the caller */
297 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
298 *OutHandle = KeyedEventHandle;
299 }
300 _SEH2_EXCEPT(ExSystemExceptionFilter())
301 {
302 /* Get the exception code */
303 Status = _SEH2_GetExceptionCode();
304
305 /* Cleanup */
306 ObCloseHandle(KeyedEventHandle, PreviousMode);
307 }
308 _SEH2_END;
309 }
310 else
311 {
312 *OutHandle = KeyedEventHandle;
313 }
314
315 /* Return Status */
316 return Status;
317 }
318
319 _IRQL_requires_max_(APC_LEVEL)
320 NTSTATUS
321 NTAPI
322 NtOpenKeyedEvent(
323 _Out_ PHANDLE OutHandle,
324 _In_ ACCESS_MASK AccessMask,
325 _In_ POBJECT_ATTRIBUTES ObjectAttributes)
326 {
327 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
328 HANDLE KeyedEventHandle;
329 NTSTATUS Status;
330
331 /* Open the object */
332 Status = ObOpenObjectByName(ObjectAttributes,
333 ExKeyedEventObjectType,
334 PreviousMode,
335 NULL,
336 AccessMask,
337 NULL,
338 &KeyedEventHandle);
339
340 /* Check for success */
341 if (!NT_SUCCESS(Status)) return Status;
342
343 /* Enter SEH for return */
344 if (PreviousMode != KernelMode)
345 {
346 _SEH2_TRY
347 {
348 /* Return the handle to the caller */
349 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
350 *OutHandle = KeyedEventHandle;
351 }
352 _SEH2_EXCEPT(ExSystemExceptionFilter())
353 {
354 /* Get the exception code */
355 Status = _SEH2_GetExceptionCode();
356 }
357 _SEH2_END;
358 }
359 else
360 {
361 *OutHandle = KeyedEventHandle;
362 }
363
364 /* Return status */
365 return Status;
366 }
367
368 _IRQL_requires_max_(APC_LEVEL)
369 NTSTATUS
370 NTAPI
371 NtWaitForKeyedEvent(
372 _In_ HANDLE Handle,
373 _In_ PVOID Key,
374 _In_ BOOLEAN Alertable,
375 _In_ PLARGE_INTEGER Timeout)
376 {
377 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
378 PEX_KEYED_EVENT KeyedEvent;
379 NTSTATUS Status;
380
381 /* Check if the caller provided a handle */
382 if (Handle != NULL)
383 {
384 /* Get the keyed event object */
385 Status = ObReferenceObjectByHandle(Handle,
386 EVENT_MODIFY_STATE,
387 ExKeyedEventObjectType,
388 PreviousMode,
389 (PVOID*)&KeyedEvent,
390 NULL);
391
392 /* Check for success */
393 if (!NT_SUCCESS(Status)) return Status;
394 }
395 else
396 {
397 /* Use the default keyed event for low memory critical sections */
398 KeyedEvent = ExpCritSecOutOfMemoryEvent;
399 }
400
401 /* Do the wait */
402 Status = ExpWaitForKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
403
404 /* Dereference the keyed event */
405 ObDereferenceObject(KeyedEvent);
406
407 /* Return the status */
408 return Status;
409 }
410
411 _IRQL_requires_max_(APC_LEVEL)
412 NTSTATUS
413 NTAPI
414 NtReleaseKeyedEvent(
415 _In_ HANDLE Handle,
416 _In_ PVOID Key,
417 _In_ BOOLEAN Alertable,
418 _In_ PLARGE_INTEGER Timeout)
419 {
420 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
421 PEX_KEYED_EVENT KeyedEvent;
422 NTSTATUS Status;
423
424 /* Check if the caller provided a handle */
425 if (Handle != NULL)
426 {
427 /* Get the keyed event object */
428 Status = ObReferenceObjectByHandle(Handle,
429 EVENT_MODIFY_STATE,
430 ExKeyedEventObjectType,
431 PreviousMode,
432 (PVOID*)&KeyedEvent,
433 NULL);
434
435 /* Check for success */
436 if (!NT_SUCCESS(Status)) return Status;
437 }
438 else
439 {
440 /* Use the default keyed event for low memory critical sections */
441 KeyedEvent = ExpCritSecOutOfMemoryEvent;
442 }
443
444 /* Do the wait */
445 Status = ExpReleaseKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
446
447 /* Dereference the keyed event */
448 ObDereferenceObject(KeyedEvent);
449
450 /* Return the status */
451 return Status;
452 }
453
454 /* EOF */