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 /* GLOBALS *******************************************************************/ 29 30 PEX_KEYED_EVENT ExpCritSecOutOfMemoryEvent; 31 POBJECT_TYPE ExKeyedEventObjectType; 32 33 static 34 GENERIC_MAPPING ExpKeyedEventMapping = 35 { 36 STANDARD_RIGHTS_READ | KEYEDEVENT_WAIT, 37 STANDARD_RIGHTS_WRITE | KEYEDEVENT_WAKE, 38 STANDARD_RIGHTS_EXECUTE, 39 KEYEDEVENT_ALL_ACCESS 40 }; 41 42 /* FUNCTIONS *****************************************************************/ 43 44 _IRQL_requires_max_(APC_LEVEL) 45 CODE_SEG("INIT") 46 BOOLEAN 47 NTAPI 48 ExpInitializeKeyedEventImplementation(VOID) 49 { 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"); 53 NTSTATUS Status; 54 HANDLE EventHandle; 55 OBJECT_ATTRIBUTES ObjectAttributes; 56 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; 63 64 /* Create the keyed event object type */ 65 Status = ObCreateObjectType(&TypeName, 66 &ObjectTypeInitializer, 67 NULL, 68 &ExKeyedEventObjectType); 69 if (!NT_SUCCESS(Status)) return FALSE; 70 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, 75 &ObjectAttributes, 76 0); 77 if (NT_SUCCESS(Status)) 78 { 79 /* Take a reference so we can get rid of the handle */ 80 Status = ObReferenceObjectByHandle(EventHandle, 81 KEYEDEVENT_ALL_ACCESS, 82 ExKeyedEventObjectType, 83 KernelMode, 84 (PVOID*)&ExpCritSecOutOfMemoryEvent, 85 NULL); 86 ZwClose(EventHandle); 87 return TRUE; 88 } 89 90 return FALSE; 91 } 92 93 VOID 94 NTAPI 95 ExpInitializeKeyedEvent( 96 _Out_ PEX_KEYED_EVENT KeyedEvent) 97 { 98 ULONG i; 99 100 /* Loop all hash buckets */ 101 for (i = 0; i < NUM_KEY_HASH_BUCKETS; i++) 102 { 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); 107 } 108 } 109 110 _IRQL_requires_max_(APC_LEVEL) 111 NTSTATUS 112 NTAPI 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) 119 { 120 PETHREAD Thread, CurrentThread; 121 PEPROCESS CurrentProcess; 122 PLIST_ENTRY ListEntry, WaitListHead1, WaitListHead2; 123 NTSTATUS Status; 124 ULONG_PTR HashIndex; 125 PVOID PreviousKeyedWaitValue; 126 127 /* Get the current process */ 128 CurrentProcess = PsGetCurrentProcess(); 129 130 /* Calculate the hash index */ 131 HashIndex = (ULONG_PTR)KeyedWaitValue >> 5; 132 HashIndex ^= (ULONG_PTR)CurrentProcess >> 6; 133 HashIndex %= NUM_KEY_HASH_BUCKETS; 134 135 /* Lock the lists */ 136 KeEnterCriticalRegion(); 137 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock); 138 139 /* Get the lists for search and wait, depending on whether 140 we want to wait for the event or signal it */ 141 if (Release) 142 { 143 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].WaitListHead; 144 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead; 145 } 146 else 147 { 148 WaitListHead1 = &KeyedEvent->HashTable[HashIndex].ReleaseListHead; 149 WaitListHead2 = &KeyedEvent->HashTable[HashIndex].WaitListHead; 150 } 151 152 /* loop the first wait list */ 153 ListEntry = WaitListHead1->Flink; 154 while (ListEntry != WaitListHead1) 155 { 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; 162 163 /* Check if this thread is a correct waiter */ 164 if ((Thread->Tcb.Process == &CurrentProcess->Pcb) && 165 (Thread->KeyedWaitValue == KeyedWaitValue)) 166 { 167 /* Remove the thread from the list */ 168 RemoveEntryList(&Thread->KeyedWaitChain); 169 170 /* Initialize the list entry to show that it was removed */ 171 InitializeListHead(&Thread->KeyedWaitChain); 172 173 /* Wake the thread */ 174 KeReleaseSemaphore(&Thread->KeyedWaitSemaphore, 175 IO_NO_INCREMENT, 176 1, 177 FALSE); 178 Thread = NULL; 179 180 /* Unlock the list. After this it is not safe to access Thread */ 181 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock); 182 KeLeaveCriticalRegion(); 183 184 return STATUS_SUCCESS; 185 } 186 } 187 188 /* Get the current thread */ 189 CurrentThread = PsGetCurrentThread(); 190 191 /* Set the wait key and remember the old value */ 192 PreviousKeyedWaitValue = CurrentThread->KeyedWaitValue; 193 CurrentThread->KeyedWaitValue = KeyedWaitValue; 194 195 /* Initialize the wait semaphore */ 196 KeInitializeSemaphore(&CurrentThread->KeyedWaitSemaphore, 0, 1); 197 198 /* Insert the current thread into the secondary wait list */ 199 InsertTailList(WaitListHead2, &CurrentThread->KeyedWaitChain); 200 201 /* Unlock the list */ 202 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock); 203 KeLeaveCriticalRegion(); 204 205 /* Wait for the keyed wait semaphore */ 206 Status = KeWaitForSingleObject(&CurrentThread->KeyedWaitSemaphore, 207 WrKeyedEvent, 208 KernelMode, 209 Alertable, 210 Timeout); 211 212 /* Check if the wait was aborted or timed out */ 213 if (Status != STATUS_SUCCESS) 214 { 215 /* Lock the lists to make sure no one else messes with the entry */ 216 KeEnterCriticalRegion(); 217 ExAcquirePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock); 218 219 /* Check if the wait list entry is still in the list */ 220 if (!IsListEmpty(&CurrentThread->KeyedWaitChain)) 221 { 222 /* Remove the thread from the list */ 223 RemoveEntryList(&CurrentThread->KeyedWaitChain); 224 InitializeListHead(&CurrentThread->KeyedWaitChain); 225 } 226 227 /* Unlock the list */ 228 ExReleasePushLockExclusive(&KeyedEvent->HashTable[HashIndex].Lock); 229 KeLeaveCriticalRegion(); 230 } 231 232 /* Restore the previous KeyedWaitValue, since this is a union member */ 233 CurrentThread->KeyedWaitValue = PreviousKeyedWaitValue; 234 235 return Status; 236 } 237 238 _IRQL_requires_max_(APC_LEVEL) 239 NTSTATUS 240 NTAPI 241 ExpWaitForKeyedEvent( 242 _Inout_ PEX_KEYED_EVENT KeyedEvent, 243 _In_ PVOID KeyedWaitValue, 244 _In_ BOOLEAN Alertable, 245 _In_ PLARGE_INTEGER Timeout) 246 { 247 /* Call the generic internal function */ 248 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent, 249 KeyedWaitValue, 250 Alertable, 251 Timeout, 252 FALSE); 253 } 254 255 _IRQL_requires_max_(APC_LEVEL) 256 NTSTATUS 257 NTAPI 258 ExpReleaseKeyedEvent( 259 _Inout_ PEX_KEYED_EVENT KeyedEvent, 260 _In_ PVOID KeyedWaitValue, 261 _In_ BOOLEAN Alertable, 262 _In_ PLARGE_INTEGER Timeout) 263 { 264 /* Call the generic internal function */ 265 return ExpReleaseOrWaitForKeyedEvent(KeyedEvent, 266 KeyedWaitValue, 267 Alertable, 268 Timeout, 269 TRUE); 270 } 271 272 _IRQL_requires_max_(PASSIVE_LEVEL) 273 NTSTATUS 274 NTAPI 275 NtCreateKeyedEvent( 276 _Out_ PHANDLE OutHandle, 277 _In_ ACCESS_MASK AccessMask, 278 _In_ POBJECT_ATTRIBUTES ObjectAttributes, 279 _In_ ULONG Flags) 280 { 281 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 282 PEX_KEYED_EVENT KeyedEvent; 283 HANDLE KeyedEventHandle; 284 NTSTATUS Status; 285 286 /* Check flags */ 287 if (Flags != 0) 288 { 289 /* We don't support any flags yet */ 290 return STATUS_INVALID_PARAMETER; 291 } 292 293 /* Create the object */ 294 Status = ObCreateObject(PreviousMode, 295 ExKeyedEventObjectType, 296 ObjectAttributes, 297 PreviousMode, 298 NULL, 299 sizeof(EX_KEYED_EVENT), 300 0, 301 0, 302 (PVOID*)&KeyedEvent); 303 304 /* Check for success */ 305 if (!NT_SUCCESS(Status)) return Status; 306 307 /* Initialize the keyed event */ 308 ExpInitializeKeyedEvent(KeyedEvent); 309 310 /* Insert it */ 311 Status = ObInsertObject(KeyedEvent, 312 NULL, 313 AccessMask, 314 0, 315 NULL, 316 &KeyedEventHandle); 317 318 /* Check for success (ObInsertObject dereferences!) */ 319 if (!NT_SUCCESS(Status)) return Status; 320 321 if (PreviousMode != KernelMode) 322 { 323 /* Enter SEH for return */ 324 _SEH2_TRY 325 { 326 /* Return the handle to the caller */ 327 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE)); 328 *OutHandle = KeyedEventHandle; 329 } 330 _SEH2_EXCEPT(ExSystemExceptionFilter()) 331 { 332 /* Get the exception code */ 333 Status = _SEH2_GetExceptionCode(); 334 335 /* Cleanup */ 336 ObCloseHandle(KeyedEventHandle, PreviousMode); 337 } 338 _SEH2_END; 339 } 340 else 341 { 342 *OutHandle = KeyedEventHandle; 343 } 344 345 /* Return Status */ 346 return Status; 347 } 348 349 _IRQL_requires_max_(PASSIVE_LEVEL) 350 NTSTATUS 351 NTAPI 352 NtOpenKeyedEvent( 353 _Out_ PHANDLE OutHandle, 354 _In_ ACCESS_MASK AccessMask, 355 _In_ POBJECT_ATTRIBUTES ObjectAttributes) 356 { 357 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 358 HANDLE KeyedEventHandle; 359 NTSTATUS Status; 360 361 /* Open the object */ 362 Status = ObOpenObjectByName(ObjectAttributes, 363 ExKeyedEventObjectType, 364 PreviousMode, 365 NULL, 366 AccessMask, 367 NULL, 368 &KeyedEventHandle); 369 370 /* Check for success */ 371 if (!NT_SUCCESS(Status)) return Status; 372 373 /* Enter SEH for return */ 374 if (PreviousMode != KernelMode) 375 { 376 _SEH2_TRY 377 { 378 /* Return the handle to the caller */ 379 ProbeForWrite(OutHandle, sizeof(HANDLE), sizeof(HANDLE)); 380 *OutHandle = KeyedEventHandle; 381 } 382 _SEH2_EXCEPT(ExSystemExceptionFilter()) 383 { 384 /* Get the exception code */ 385 Status = _SEH2_GetExceptionCode(); 386 387 /* Cleanup */ 388 ObCloseHandle(KeyedEventHandle, PreviousMode); 389 } 390 _SEH2_END; 391 } 392 else 393 { 394 *OutHandle = KeyedEventHandle; 395 } 396 397 /* Return status */ 398 return Status; 399 } 400 401 _IRQL_requires_max_(PASSIVE_LEVEL) 402 NTSTATUS 403 NTAPI 404 NtWaitForKeyedEvent( 405 _In_opt_ HANDLE Handle, 406 _In_ PVOID Key, 407 _In_ BOOLEAN Alertable, 408 _In_opt_ PLARGE_INTEGER Timeout) 409 { 410 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 411 PEX_KEYED_EVENT KeyedEvent; 412 NTSTATUS Status; 413 LARGE_INTEGER TimeoutCopy; 414 415 /* Key must always be two-byte aligned */ 416 if ((ULONG_PTR)Key & 1) 417 { 418 return STATUS_INVALID_PARAMETER_1; 419 } 420 421 /* Check if the caller passed a timeout value and this is from user mode */ 422 if ((Timeout != NULL) && (PreviousMode != KernelMode)) 423 { 424 _SEH2_TRY 425 { 426 ProbeForRead(Timeout, sizeof(*Timeout), 1); 427 TimeoutCopy = *Timeout; 428 Timeout = &TimeoutCopy; 429 } 430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 431 { 432 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 433 } 434 _SEH2_END; 435 } 436 437 /* Check if the caller provided a handle */ 438 if (Handle != NULL) 439 { 440 /* Get the keyed event object */ 441 Status = ObReferenceObjectByHandle(Handle, 442 KEYEDEVENT_WAIT, 443 ExKeyedEventObjectType, 444 PreviousMode, 445 (PVOID*)&KeyedEvent, 446 NULL); 447 448 /* Check for success */ 449 if (!NT_SUCCESS(Status)) return Status; 450 } 451 else 452 { 453 /* Use the default keyed event for low memory critical sections */ 454 KeyedEvent = ExpCritSecOutOfMemoryEvent; 455 } 456 457 /* Do the wait */ 458 Status = ExpWaitForKeyedEvent(KeyedEvent, Key, Alertable, Timeout); 459 460 if (Handle != NULL) 461 { 462 /* Dereference the keyed event */ 463 ObDereferenceObject(KeyedEvent); 464 } 465 466 /* Return the status */ 467 return Status; 468 } 469 470 _IRQL_requires_max_(PASSIVE_LEVEL) 471 NTSTATUS 472 NTAPI 473 NtReleaseKeyedEvent( 474 _In_opt_ HANDLE Handle, 475 _In_ PVOID Key, 476 _In_ BOOLEAN Alertable, 477 _In_opt_ PLARGE_INTEGER Timeout) 478 { 479 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 480 PEX_KEYED_EVENT KeyedEvent; 481 NTSTATUS Status; 482 LARGE_INTEGER TimeoutCopy; 483 484 /* Key must always be two-byte aligned */ 485 if ((ULONG_PTR)Key & 1) 486 { 487 return STATUS_INVALID_PARAMETER_1; 488 } 489 490 /* Check if the caller passed a timeout value and this is from user mode */ 491 if ((Timeout != NULL) && (PreviousMode != KernelMode)) 492 { 493 _SEH2_TRY 494 { 495 ProbeForRead(Timeout, sizeof(*Timeout), 1); 496 TimeoutCopy = *Timeout; 497 Timeout = &TimeoutCopy; 498 } 499 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 500 { 501 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 502 } 503 _SEH2_END; 504 } 505 506 /* Check if the caller provided a handle */ 507 if (Handle != NULL) 508 { 509 /* Get the keyed event object */ 510 Status = ObReferenceObjectByHandle(Handle, 511 KEYEDEVENT_WAKE, 512 ExKeyedEventObjectType, 513 PreviousMode, 514 (PVOID*)&KeyedEvent, 515 NULL); 516 517 /* Check for success */ 518 if (!NT_SUCCESS(Status)) return Status; 519 } 520 else 521 { 522 /* Use the default keyed event for low memory critical sections */ 523 KeyedEvent = ExpCritSecOutOfMemoryEvent; 524 } 525 526 /* Do the wait */ 527 Status = ExpReleaseKeyedEvent(KeyedEvent, Key, Alertable, Timeout); 528 529 if (Handle != NULL) 530 { 531 /* Dereference the keyed event */ 532 ObDereferenceObject(KeyedEvent); 533 } 534 535 /* Return the status */ 536 return Status; 537 } 538 539 /* EOF */ 540