2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obwait.c
5 * PURPOSE: Handles Waiting on Objects
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Thomas Weidenmueller (w3seek@reactos.org)
10 /* INCLUDES ******************************************************************/
16 /* FUNCTIONS *****************************************************************/
19 * @name NtWaitForMultipleObjects
22 * The NtWaitForMultipleObjects routine <FILLMEIN>
39 * @return STATUS_SUCCESS or appropriate error value.
46 NtWaitForMultipleObjects(IN ULONG ObjectCount
,
47 IN PHANDLE HandleArray
,
48 IN WAIT_TYPE WaitType
,
50 IN PLARGE_INTEGER TimeOut OPTIONAL
)
52 PKWAIT_BLOCK WaitBlockArray
;
53 HANDLE Handles
[MAXIMUM_WAIT_OBJECTS
], KernelHandle
;
54 PVOID Objects
[MAXIMUM_WAIT_OBJECTS
];
55 PVOID WaitObjects
[MAXIMUM_WAIT_OBJECTS
];
56 ULONG i
, ReferencedObjects
, j
;
57 KPROCESSOR_MODE PreviousMode
;
58 LARGE_INTEGER SafeTimeOut
;
60 PHANDLE_TABLE_ENTRY HandleEntry
;
61 POBJECT_HEADER ObjectHeader
;
62 PHANDLE_TABLE HandleTable
;
63 ACCESS_MASK GrantedAccess
;
68 /* Check for valid Object Count */
69 if ((ObjectCount
> MAXIMUM_WAIT_OBJECTS
) || !(ObjectCount
))
72 return STATUS_INVALID_PARAMETER_1
;
75 /* Check for valid Wait Type */
76 if ((WaitType
!= WaitAll
) && (WaitType
!= WaitAny
))
79 return STATUS_INVALID_PARAMETER_3
;
83 PreviousMode
= ExGetPreviousMode();
86 /* Probe for user mode */
87 if (PreviousMode
!= KernelMode
)
89 /* Check if we have a timeout */
92 /* Make a local copy of the timeout on the stack */
93 SafeTimeOut
= ProbeForReadLargeInteger(TimeOut
);
94 TimeOut
= &SafeTimeOut
;
97 /* Probe all the handles */
98 ProbeForRead(HandleArray
,
99 ObjectCount
* sizeof(HANDLE
),
104 * Make a copy so we don't have to guard with SEH later and keep
105 * track of what objects we referenced if dereferencing pointers
108 RtlCopyMemory(Handles
,
110 ObjectCount
* sizeof(HANDLE
));
112 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) //ExSystemExceptionFilter()
114 /* Cover up for kernel mode */
115 if (PreviousMode
== KernelMode
)
117 /* But don't fail silently */
118 DbgPrint("Mon dieu! Covering up for BAD driver passing invalid pointer (0x%p)! Hon hon hon!\n", HandleArray
);
121 /* Return the exception code */
122 _SEH2_YIELD(return _SEH2_GetExceptionCode());
126 /* Check if we can use the internal Wait Array */
127 if (ObjectCount
> THREAD_WAIT_OBJECTS
)
129 /* Allocate from Pool */
130 WaitBlockArray
= ExAllocatePoolWithTag(NonPagedPool
,
137 return STATUS_INSUFFICIENT_RESOURCES
;
142 /* No need for the array */
143 WaitBlockArray
= NULL
;
146 /* Enter a critical region since we'll play with handles */
148 KeEnterCriticalRegion();
152 ReferencedObjects
= 0;
155 /* Use the right Executive Handle */
156 if (ObpIsKernelHandle(Handles
[i
], PreviousMode
))
158 /* Use the System Handle Table and decode */
159 HandleTable
= ObpKernelHandleTable
;
160 KernelHandle
= ObKernelHandleToHandle(Handles
[i
]);
162 /* Get a pointer to it */
163 HandleEntry
= ExMapHandleToPointer(HandleTable
, KernelHandle
);
167 /* Use the Process' Handle table and get the Ex Handle */
168 HandleTable
= PsGetCurrentProcess()->ObjectTable
;
170 /* Get a pointer to it */
171 HandleEntry
= ExMapHandleToPointer(HandleTable
, Handles
[i
]);
174 /* Check if we have an entry */
177 /* Fail, handle is invalid */
178 Status
= STATUS_INVALID_HANDLE
;
179 DPRINT1("Invalid handle %p passed to NtWaitForMultipleObjects\n", Handles
[i
]);
183 /* Check for synchronize access */
184 GrantedAccess
= HandleEntry
->GrantedAccess
;
185 if ((PreviousMode
!= KernelMode
) && (!(GrantedAccess
& SYNCHRONIZE
)))
187 /* Unlock the entry and fail */
188 ExUnlockHandleTableEntry(HandleTable
, HandleEntry
);
189 DPRINT1("Handle does not have SYNCHRONIZE access\n");
190 Status
= STATUS_ACCESS_DENIED
;
194 /* Get the Object Header */
195 ObjectHeader
= ObpGetHandleObject(HandleEntry
);
197 /* Get default Object */
198 DefaultObject
= ObjectHeader
->Type
->DefaultObject
;
200 /* Check if it's the internal offset */
201 if (IsPointerOffset(DefaultObject
))
203 /* Increase reference count */
204 InterlockedIncrementSizeT(&ObjectHeader
->PointerCount
);
207 /* Save the Object and Wait Object, this is a relative offset */
208 Objects
[i
] = &ObjectHeader
->Body
;
209 WaitObjects
[i
] = (PVOID
)((ULONG_PTR
)&ObjectHeader
->Body
+
210 (ULONG_PTR
)DefaultObject
);
214 /* This is our internal Object */
217 WaitObjects
[i
] = DefaultObject
;
220 /* Unlock the Handle Table Entry */
221 ExUnlockHandleTableEntry(HandleTable
, HandleEntry
);
225 } while (i
< ObjectCount
);
227 /* For a Waitall, we can't have the same object more then once */
228 if (WaitType
== WaitAll
)
230 /* Clear the main loop variable */
236 /* Check the current and forward object */
237 for (j
= i
+ 1; j
< ObjectCount
; j
++)
239 /* Make sure they don't match */
240 if (WaitObjects
[i
] == WaitObjects
[j
])
243 Status
= STATUS_INVALID_PARAMETER_MIX
;
244 DPRINT1("Passed a duplicate object to NtWaitForMultipleObjects\n");
251 } while (i
< ObjectCount
);
254 /* Now we can finally wait. Always use SEH since it can raise an exception */
257 /* We're done playing with handles */
259 KeLeaveCriticalRegion();
261 /* Do the kernel wait */
262 Status
= KeWaitForMultipleObjects(ObjectCount
,
271 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_MUTANT_LIMIT_EXCEEDED
) ?
272 EXCEPTION_EXECUTE_HANDLER
:
273 EXCEPTION_CONTINUE_SEARCH
)
275 /* Get the exception code */
276 Status
= _SEH2_GetExceptionCode();
281 /* First derefence */
282 while (ReferencedObjects
)
284 /* Decrease the number of objects */
287 /* Check if we had a valid object in this position */
288 if (Objects
[ReferencedObjects
])
291 ObDereferenceObject(Objects
[ReferencedObjects
]);
295 /* Free wait block array */
296 if (WaitBlockArray
) ExFreePoolWithTag(WaitBlockArray
, TAG_WAIT
);
298 /* Re-enable APCs if needed */
299 if (LockInUse
) KeLeaveCriticalRegion();
306 * @name NtWaitForMultipleObjects32
309 * The NtWaitForMultipleObjects32 routine <FILLMEIN>
326 * @return STATUS_SUCCESS or appropriate error value.
333 NtWaitForMultipleObjects32(IN ULONG ObjectCount
,
335 IN WAIT_TYPE WaitType
,
336 IN BOOLEAN Alertable
,
337 IN PLARGE_INTEGER TimeOut OPTIONAL
)
340 return NtWaitForMultipleObjects(ObjectCount
,
348 * @name NtWaitForSingleObject
351 * The NtWaitForSingleObject routine <FILLMEIN>
353 * @param ObjectHandle
362 * @return STATUS_SUCCESS or appropriate error value.
369 NtWaitForSingleObject(IN HANDLE ObjectHandle
,
370 IN BOOLEAN Alertable
,
371 IN PLARGE_INTEGER TimeOut OPTIONAL
)
373 PVOID Object
, WaitableObject
;
374 KPROCESSOR_MODE PreviousMode
;
375 LARGE_INTEGER SafeTimeOut
;
378 /* Check if we came with a timeout from user mode */
379 PreviousMode
= ExGetPreviousMode();
380 if ((TimeOut
) && (PreviousMode
!= KernelMode
))
382 /* Enter SEH for proving */
385 /* Make a copy on the stack */
386 SafeTimeOut
= ProbeForReadLargeInteger(TimeOut
);
387 TimeOut
= &SafeTimeOut
;
389 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
391 /* Return the exception code */
392 _SEH2_YIELD(return _SEH2_GetExceptionCode());
398 Status
= ObReferenceObjectByHandle(ObjectHandle
,
404 if (NT_SUCCESS(Status
))
406 /* Get the Waitable Object */
407 WaitableObject
= OBJECT_TO_OBJECT_HEADER(Object
)->Type
->DefaultObject
;
409 /* Is it an offset for internal objects? */
410 if (IsPointerOffset(WaitableObject
))
412 /* Turn it into a pointer */
413 WaitableObject
= (PVOID
)((ULONG_PTR
)Object
+
414 (ULONG_PTR
)WaitableObject
);
417 /* SEH this since it can also raise an exception */
420 /* Ask the kernel to do the wait */
421 Status
= KeWaitForSingleObject(WaitableObject
,
427 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_MUTANT_LIMIT_EXCEEDED
) ?
428 EXCEPTION_EXECUTE_HANDLER
:
429 EXCEPTION_CONTINUE_SEARCH
)
431 /* Get the exception code */
432 Status
= _SEH2_GetExceptionCode();
436 /* Dereference the Object */
437 ObDereferenceObject(Object
);
441 DPRINT1("Failed to reference the handle with status 0x%x\n", Status
);
444 /* Return the status */
449 * @name NtSignalAndWaitForSingleObject
452 * The NtSignalAndWaitForSingleObject routine <FILLMEIN>
454 * @param ObjectHandleToSignal
457 * @param WaitableObjectHandle
466 * @return STATUS_SUCCESS or appropriate error value.
473 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal
,
474 IN HANDLE WaitableObjectHandle
,
475 IN BOOLEAN Alertable
,
476 IN PLARGE_INTEGER TimeOut OPTIONAL
)
478 KPROCESSOR_MODE PreviousMode
;
480 PVOID SignalObj
, WaitObj
, WaitableObject
;
481 LARGE_INTEGER SafeTimeOut
;
482 OBJECT_HANDLE_INFORMATION HandleInfo
;
485 /* Check if we came with a timeout from user mode */
486 PreviousMode
= ExGetPreviousMode();
487 if ((TimeOut
) && (PreviousMode
!= KernelMode
))
489 /* Enter SEH for probing */
492 /* Make a copy on the stack */
493 SafeTimeOut
= ProbeForReadLargeInteger(TimeOut
);
494 TimeOut
= &SafeTimeOut
;
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
498 /* Return the exception code */
499 _SEH2_YIELD(return _SEH2_GetExceptionCode());
504 /* Start by getting the signal object*/
505 Status
= ObReferenceObjectByHandle(ObjectHandleToSignal
,
511 if (!NT_SUCCESS(Status
)) return Status
;
513 /* Now get the wait object */
514 Status
= ObReferenceObjectByHandle(WaitableObjectHandle
,
520 if (!NT_SUCCESS(Status
))
522 /* Failed to reference the wait object */
523 ObDereferenceObject(SignalObj
);
527 /* Get the real waitable object */
528 WaitableObject
= OBJECT_TO_OBJECT_HEADER(WaitObj
)->Type
->DefaultObject
;
530 /* Handle internal offset */
531 if (IsPointerOffset(WaitableObject
))
533 /* Get real pointer */
534 WaitableObject
= (PVOID
)((ULONG_PTR
)WaitObj
+
535 (ULONG_PTR
)WaitableObject
);
538 /* Check Signal Object Type */
539 Type
= OBJECT_TO_OBJECT_HEADER(SignalObj
)->Type
;
540 if (Type
== ExEventObjectType
)
542 /* Check if we came from user-mode without the right access */
543 if ((PreviousMode
!= KernelMode
) &&
544 !(HandleInfo
.GrantedAccess
& EVENT_MODIFY_STATE
))
546 /* Fail: lack of rights */
547 Status
= STATUS_ACCESS_DENIED
;
552 KeSetEvent(SignalObj
, EVENT_INCREMENT
, TRUE
);
554 else if (Type
== ExMutantObjectType
)
556 /* This can raise an exception */
559 /* Release the mutant */
560 KeReleaseMutant(SignalObj
, MUTANT_INCREMENT
, FALSE
, TRUE
);
562 _SEH2_EXCEPT(((_SEH2_GetExceptionCode() == STATUS_ABANDONED
) ||
563 (_SEH2_GetExceptionCode() == STATUS_MUTANT_NOT_OWNED
)) ?
564 EXCEPTION_EXECUTE_HANDLER
:
565 EXCEPTION_CONTINUE_SEARCH
)
567 /* Get the exception code */
568 Status
= _SEH2_GetExceptionCode();
572 else if (Type
== ExSemaphoreObjectType
)
574 /* Check if we came from user-mode without the right access */
575 if ((PreviousMode
!= KernelMode
) &&
576 !(HandleInfo
.GrantedAccess
& SEMAPHORE_MODIFY_STATE
))
578 /* Fail: lack of rights */
579 Status
= STATUS_ACCESS_DENIED
;
583 /* This can raise an exception*/
586 /* Release the semaphore */
587 KeReleaseSemaphore(SignalObj
, SEMAPHORE_INCREMENT
, 1, TRUE
);
589 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_SEMAPHORE_LIMIT_EXCEEDED
) ?
590 EXCEPTION_EXECUTE_HANDLER
:
591 EXCEPTION_CONTINUE_SEARCH
)
593 /* Get the exception code */
594 Status
= _SEH2_GetExceptionCode();
600 /* This isn't a valid object to be waiting on */
601 Status
= STATUS_OBJECT_TYPE_MISMATCH
;
604 /* Make sure we didn't fail */
605 if (NT_SUCCESS(Status
))
607 /* SEH this since it can also raise an exception */
610 /* Perform the wait now */
611 Status
= KeWaitForSingleObject(WaitableObject
,
617 _SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_MUTANT_LIMIT_EXCEEDED
) ?
618 EXCEPTION_EXECUTE_HANDLER
:
619 EXCEPTION_CONTINUE_SEARCH
)
621 /* Get the exception code */
622 Status
= _SEH2_GetExceptionCode();
627 /* We're done here, dereference both objects */
629 ObDereferenceObject(SignalObj
);
630 ObDereferenceObject(WaitObj
);