cd0ae105cca22566cb082f93cd38f715094908b6
[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 BOOLEAN
55 INIT_FUNCTION
56 NTAPI
57 ExpInitializeKeyedEventImplementation(VOID)
58 {
59 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer = {0};
60 UNICODE_STRING TypeName = RTL_CONSTANT_STRING(L"KeyedEvent");
61 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\KernelObjects\\CritSecOutOfMemoryEvent");
62 NTSTATUS Status;
63 HANDLE EventHandle;
64 OBJECT_ATTRIBUTES ObjectAttributes;
65
66 /* Set up the object type initializer */
67 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
68 ObjectTypeInitializer.GenericMapping = ExpKeyedEventMapping;
69 ObjectTypeInitializer.PoolType = PagedPool;
70 ObjectTypeInitializer.ValidAccessMask = EVENT_ALL_ACCESS;
71 ObjectTypeInitializer.UseDefaultObject = TRUE;
72
73 /* Create the keyed event object type */
74 Status = ObCreateObjectType(&TypeName,
75 &ObjectTypeInitializer,
76 NULL,
77 &ExKeyedEventObjectType);
78 if (!NT_SUCCESS(Status)) return FALSE;
79
80 /* Create the out of memory event for critical sections */
81 InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_PERMANENT, NULL, NULL);
82 Status = ZwCreateKeyedEvent(&EventHandle,
83 EVENT_ALL_ACCESS,
84 &ObjectAttributes,
85 0);
86 if (NT_SUCCESS(Status))
87 {
88 /* Take a reference so we can get rid of the handle */
89 Status = ObReferenceObjectByHandle(EventHandle,
90 EVENT_ALL_ACCESS,
91 ExKeyedEventObjectType,
92 KernelMode,
93 (PVOID*)&ExpCritSecOutOfMemoryEvent,
94 NULL);
95 ZwClose(EventHandle);
96 return TRUE;
97 }
98
99 return FALSE;
100 }
101
102 VOID
103 NTAPI
104 ExpInitializeKeyedEvent(
105 _Out_ PEX_KEYED_EVENT KeyedEvent)
106 {
107 ULONG i;
108
109 /* Loop all hash buckets */
110 for (i = 0; i < NUM_KEY_HASH_BUCKETS; i++)
111 {
112 /* Initialize the mutex and the wait lists */
113 ExInitializePushLock(&KeyedEvent->HashTable[i].Lock);
114 InitializeListHead(&KeyedEvent->HashTable[i].WaitListHead);
115 InitializeListHead(&KeyedEvent->HashTable[i].ReleaseListHead);
116 }
117 }
118
119 NTSTATUS
120 NTAPI
121 ExpReleaseOrWaitForKeyedEvent(
122 _Inout_ PEX_KEYED_EVENT KeyedEvent,
123 _In_ PVOID KeyedWaitValue,
124 _In_ BOOLEAN Alertable,
125 _In_ PLARGE_INTEGER Timeout,
126 _In_ BOOLEAN Release)
127 {
128 PETHREAD Thread, CurrentThread;
129 PKPROCESS CurrentProcess;
130 PLIST_ENTRY ListEntry, WaitListHead1, WaitListHead2;
131 NTSTATUS Status;
132 ULONG_PTR HashIndex;
133
134 /* Get the current process */
135 CurrentProcess = KeGetCurrentProcess();
136
137 /* Calculate the hash index */
138 HashIndex = (ULONG_PTR)KeyedWaitValue >> 5;
139 HashIndex ^= (ULONG_PTR)CurrentProcess >> 6;
140 HashIndex %= NUM_KEY_HASH_BUCKETS;
141
142 /* Lock the lists */
143 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
144
145 /* Get the lists for search and wait, depending on whether
146 we want to wait for the event or signal it */
147 if (Release)
148 {
149 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
150 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
151 }
152 else
153 {
154 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead;
155 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].WaitListHead;
156 }
157
158 /* loop the first wait list */
159 ListEntry = WaitListHead1->Flink;
160 while (ListEntry != WaitListHead1)
161 {
162 Thread = CONTAINING_RECORD(ListEntry, ETHREAD, KeyedWaitChain);
163
164 /* Check if this thread is a correct waiter */
165 if ((Thread->Tcb.Process == CurrentProcess) &&
166 (Thread->KeyedWaitValue == KeyedWaitValue))
167 {
168 /* Remove the thread from the list */
169 RemoveEntryList(&Thread->KeyedWaitChain);
170
171 /* Wake the thread */
172 KeReleaseSemaphore(&Thread->KeyedWaitSemaphore, 0, 1, FALSE);
173
174 /* Unlock the lists */
175 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
176
177 return STATUS_SUCCESS;
178 }
179 }
180
181 /* Get the current thread */
182 CurrentThread = PsGetCurrentThread();
183
184 /* Set the wait key */
185 CurrentThread->KeyedWaitValue = KeyedWaitValue;
186
187 /* Initialize the wait semaphore */
188 KeInitializeSemaphore(&CurrentThread->KeyedWaitSemaphore, 0, 1);
189
190 /* Insert the current thread into the secondary wait list */
191 InsertTailList(WaitListHead2, &CurrentThread->KeyedWaitChain);
192
193 /* Unlock the lists */
194 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock);
195
196 /* Wait for the keyed wait semaphore */
197 Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore,
198 WrKeyedEvent,
199 KernelMode,
200 Alertable,
201 Timeout);
202
203 return STATUS_SUCCESS;
204 }
205
206 NTSTATUS
207 NTAPI
208 ExpWaitForKeyedEvent(
209 _Inout_ PEX_KEYED_EVENT KeyedEvent,
210 _In_ PVOID KeyedWaitValue,
211 _In_ BOOLEAN Alertable,
212 _In_ PLARGE_INTEGER Timeout)
213 {
214 /* Call the generic internal function */
215 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
216 KeyedWaitValue,
217 Alertable,
218 Timeout,
219 FALSE);
220 }
221
222 NTSTATUS
223 NTAPI
224 ExpReleaseKeyedEvent(
225 _Inout_ PEX_KEYED_EVENT KeyedEvent,
226 _In_ PVOID KeyedWaitValue,
227 _In_ BOOLEAN Alertable,
228 _In_ PLARGE_INTEGER Timeout)
229 {
230 /* Call the generic internal function */
231 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent,
232 KeyedWaitValue,
233 Alertable,
234 Timeout,
235 TRUE);
236 }
237
238 NTSTATUS
239 NTAPI
240 NtCreateKeyedEvent(
241 _Out_ PHANDLE OutHandle,
242 _In_ ACCESS_MASK AccessMask,
243 _In_ POBJECT_ATTRIBUTES ObjectAttributes,
244 _In_ ULONG Flags)
245 {
246 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
247 PEX_KEYED_EVENT KeyedEvent;
248 HANDLE KeyedEventHandle;
249 NTSTATUS Status;
250
251 /* Check flags */
252 if (Flags != 0)
253 {
254 /* We don't support any flags yet */
255 return STATUS_INVALID_PARAMETER;
256 }
257
258 /* Create the object */
259 Status = ObCreateObject(PreviousMode,
260 ExKeyedEventObjectType,
261 ObjectAttributes,
262 PreviousMode,
263 NULL,
264 sizeof(EX_KEYED_EVENT),
265 0,
266 0,
267 (PVOID*)&KeyedEvent);
268
269 /* Check for success */
270 if (!NT_SUCCESS(Status)) return Status;
271
272 /* Initalize the keyed event */
273 ExpInitializeKeyedEvent(KeyedEvent);
274
275 /* Insert it */
276 Status = ObInsertObject(KeyedEvent,
277 NULL,
278 AccessMask,
279 0,
280 NULL,
281 &KeyedEventHandle);
282
283 /* Check for success (ObInsertObject dereferences!) */
284 if (!NT_SUCCESS(Status)) return Status;
285
286 if (PreviousMode != KernelMode)
287 {
288 /* Enter SEH for return */
289 _SEH2_TRY
290 {
291 /* Return the handle to the caller */
292 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
293 *OutHandle = KeyedEventHandle;
294 }
295 _SEH2_EXCEPT(ExSystemExceptionFilter())
296 {
297 /* Get the exception code */
298 Status = _SEH2_GetExceptionCode();
299
300 /* Cleanup */
301 ObCloseHandle(KeyedEventHandle, PreviousMode);
302 }
303 _SEH2_END;
304 }
305 else
306 {
307 *OutHandle = KeyedEventHandle;
308 }
309
310 /* Return Status */
311 return Status;
312 }
313
314 NTSTATUS
315 NTAPI
316 NtOpenKeyedEvent(
317 _Out_ PHANDLE OutHandle,
318 _In_ ACCESS_MASK AccessMask,
319 _In_ POBJECT_ATTRIBUTES ObjectAttributes)
320 {
321 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
322 HANDLE KeyedEventHandle;
323 NTSTATUS Status;
324
325 /* Open the object */
326 Status = ObOpenObjectByName(ObjectAttributes,
327 ExKeyedEventObjectType,
328 PreviousMode,
329 NULL,
330 AccessMask,
331 NULL,
332 &KeyedEventHandle);
333
334 /* Check for success */
335 if (!NT_SUCCESS(Status)) return Status;
336
337 /* Enter SEH for return */
338 if (PreviousMode != KernelMode)
339 {
340 _SEH2_TRY
341 {
342 /* Return the handle to the caller */
343 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE));
344 *OutHandle = KeyedEventHandle;
345 }
346 _SEH2_EXCEPT(ExSystemExceptionFilter())
347 {
348 /* Get the exception code */
349 Status = _SEH2_GetExceptionCode();
350 }
351 _SEH2_END;
352 }
353 else
354 {
355 *OutHandle = KeyedEventHandle;
356 }
357
358 /* Return status */
359 return Status;
360 }
361
362 NTSTATUS
363 NTAPI
364 NtWaitForKeyedEvent(
365 _In_ HANDLE Handle,
366 _In_ PVOID Key,
367 _In_ BOOLEAN Alertable,
368 _In_ PLARGE_INTEGER Timeout)
369 {
370 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
371 PEX_KEYED_EVENT KeyedEvent;
372 NTSTATUS Status;
373
374 /* Check if the caller provided a handle */
375 if (Handle != NULL)
376 {
377 /* Get the keyed event object */
378 Status = ObReferenceObjectByHandle(Handle,
379 EVENT_MODIFY_STATE,
380 ExKeyedEventObjectType,
381 PreviousMode,
382 (PVOID*)&KeyedEvent,
383 NULL);
384
385 /* Check for success */
386 if (!NT_SUCCESS(Status)) return Status;
387 }
388 else
389 {
390 /* Use the default keyed event for low memory critical sections */
391 KeyedEvent = ExpCritSecOutOfMemoryEvent;
392 }
393
394 /* Do the wait */
395 Status = ExpWaitForKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
396
397 /* Dereference the keyed event */
398 ObDereferenceObject(KeyedEvent);
399
400 /* Return the status */
401 return Status;
402 }
403
404 NTSTATUS
405 NTAPI
406 NtReleaseKeyedEvent(
407 _In_ HANDLE Handle,
408 _In_ PVOID Key,
409 _In_ BOOLEAN Alertable,
410 _In_ PLARGE_INTEGER Timeout)
411 {
412 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
413 PEX_KEYED_EVENT KeyedEvent;
414 NTSTATUS Status;
415
416 /* Check if the caller provided a handle */
417 if (Handle != NULL)
418 {
419 /* Get the keyed event object */
420 Status = ObReferenceObjectByHandle(Handle,
421 EVENT_MODIFY_STATE,
422 ExKeyedEventObjectType,
423 PreviousMode,
424 (PVOID*)&KeyedEvent,
425 NULL);
426
427 /* Check for success */
428 if (!NT_SUCCESS(Status)) return Status;
429 }
430 else
431 {
432 /* Use the default keyed event for low memory critical sections */
433 KeyedEvent = ExpCritSecOutOfMemoryEvent;
434 }
435
436 /* Do the wait */
437 Status = ExpReleaseKeyedEvent(KeyedEvent, Key, Alertable, Timeout);
438
439 /* Dereference the keyed event */
440 ObDereferenceObject(KeyedEvent);
441
442 /* Return the status */
443 return Status;
444 }
445
446 /* EOF */