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