- Update to r53061
[reactos.git] / ntoskrnl / ob / obwait.c
1 /*
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)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* FUNCTIONS *****************************************************************/
17
18 /*++
19 * @name NtWaitForMultipleObjects
20 * @implemented NT4
21 *
22 * The NtWaitForMultipleObjects routine <FILLMEIN>
23 *
24 * @param ObjectCount
25 * <FILLMEIN>
26 *
27 * @param HandleArray
28 * <FILLMEIN>
29 *
30 * @param WaitType
31 * <FILLMEIN>
32 *
33 * @param Alertable
34 * <FILLMEIN>
35 *
36 * @param TimeOut
37 * <FILLMEIN>
38 *
39 * @return STATUS_SUCCESS or appropriate error value.
40 *
41 * @remarks None.
42 *
43 *--*/
44 NTSTATUS
45 NTAPI
46 NtWaitForMultipleObjects(IN ULONG ObjectCount,
47 IN PHANDLE HandleArray,
48 IN WAIT_TYPE WaitType,
49 IN BOOLEAN Alertable,
50 IN PLARGE_INTEGER TimeOut OPTIONAL)
51 {
52 PKWAIT_BLOCK WaitBlockArray = NULL;
53 HANDLE Handles[MAXIMUM_WAIT_OBJECTS], KernelHandle;
54 PVOID Objects[MAXIMUM_WAIT_OBJECTS];
55 PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS];
56 ULONG i = 0, ReferencedObjects = 0, j;
57 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
58 LARGE_INTEGER SafeTimeOut;
59 BOOLEAN LockInUse;
60 PHANDLE_TABLE_ENTRY HandleEntry;
61 POBJECT_HEADER ObjectHeader;
62 PHANDLE_TABLE HandleTable;
63 ACCESS_MASK GrantedAccess;
64 PVOID DefaultObject;
65 NTSTATUS Status;
66 PAGED_CODE();
67
68 /* Enter a critical region since we'll play with handles */
69 LockInUse = TRUE;
70 KeEnterCriticalRegion();
71
72 /* Check for valid Object Count */
73 if ((ObjectCount > MAXIMUM_WAIT_OBJECTS) || !(ObjectCount))
74 {
75 /* Fail */
76 Status = STATUS_INVALID_PARAMETER_1;
77 goto Quickie;
78 }
79
80 /* Check for valid Wait Type */
81 if ((WaitType != WaitAll) && (WaitType != WaitAny))
82 {
83 /* Fail */
84 Status = STATUS_INVALID_PARAMETER_3;
85 goto Quickie;
86 }
87
88 /* Enter SEH */
89 _SEH2_TRY
90 {
91 /* Check if the call came from user mode */
92 if (PreviousMode != KernelMode)
93 {
94 /* Check if we have a timeout */
95 if (TimeOut)
96 {
97 /* Make a local copy of the timeout on the stack */
98 SafeTimeOut = ProbeForReadLargeInteger(TimeOut);
99 TimeOut = &SafeTimeOut;
100 }
101
102 /* Probe all the handles */
103 ProbeForRead(HandleArray,
104 ObjectCount * sizeof(HANDLE),
105 sizeof(HANDLE));
106 }
107
108 /*
109 * Make a copy so we don't have to guard with SEH later and keep
110 * track of what objects we referenced if dereferencing pointers
111 * suddenly fails
112 */
113 RtlCopyMemory(Handles,
114 HandleArray,
115 ObjectCount * sizeof(HANDLE));
116 }
117 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
118 {
119 /* Return the exception code */
120 _SEH2_YIELD(return _SEH2_GetExceptionCode());
121 }
122 _SEH2_END;
123
124 /* Check if we can use the internal Wait Array */
125 if (ObjectCount > THREAD_WAIT_OBJECTS)
126 {
127 /* Allocate from Pool */
128 WaitBlockArray = ExAllocatePoolWithTag(NonPagedPool,
129 ObjectCount *
130 sizeof(KWAIT_BLOCK),
131 TAG_WAIT);
132 if (!WaitBlockArray)
133 {
134 /* Fail */
135 Status = STATUS_INSUFFICIENT_RESOURCES;
136 goto Quickie;
137 }
138 }
139
140 /* Start the loop */
141 do
142 {
143 /* Use the right Executive Handle */
144 if (ObIsKernelHandle(Handles[i], PreviousMode))
145 {
146 /* Use the System Handle Table and decode */
147 HandleTable = ObpKernelHandleTable;
148 KernelHandle = ObKernelHandleToHandle(Handles[i]);
149
150 /* Get a pointer to it */
151 HandleEntry = ExMapHandleToPointer(HandleTable, KernelHandle);
152 }
153 else
154 {
155 /* Use the Process' Handle table and get the Ex Handle */
156 HandleTable = PsGetCurrentProcess()->ObjectTable;
157
158 /* Get a pointer to it */
159 HandleEntry = ExMapHandleToPointer(HandleTable, Handles[i]);
160 }
161
162 /* Check if we have an entry */
163 if (!HandleEntry)
164 {
165 /* Fail, handle is invalid */
166 Status = STATUS_INVALID_HANDLE;
167 goto Quickie;
168 }
169
170 /* Check for synchronize access */
171 GrantedAccess = HandleEntry->GrantedAccess;
172 if ((PreviousMode != KernelMode) && (!(GrantedAccess & SYNCHRONIZE)))
173 {
174 /* Unlock the entry and fail */
175 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
176 Status = STATUS_ACCESS_DENIED;
177 goto Quickie;
178 }
179
180 /* Get the Object Header */
181 ObjectHeader = ObpGetHandleObject(HandleEntry);
182
183 /* Get default Object */
184 DefaultObject = ObjectHeader->Type->DefaultObject;
185
186 /* Check if it's the internal offset */
187 if (IsPointerOffset(DefaultObject))
188 {
189 /* Increase reference count */
190 InterlockedIncrement(&ObjectHeader->PointerCount);
191 ReferencedObjects++;
192
193 /* Save the Object and Wait Object, this is a relative offset */
194 Objects[i] = &ObjectHeader->Body;
195 WaitObjects[i] = (PVOID)((ULONG_PTR)&ObjectHeader->Body +
196 (ULONG_PTR)DefaultObject);
197 }
198 else
199 {
200 /* This is our internal Object */
201 ReferencedObjects++;
202 Objects[i] = NULL;
203 WaitObjects[i] = DefaultObject;
204 }
205
206 /* Unlock the Handle Table Entry */
207 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
208
209 /* Keep looping */
210 i++;
211 } while (i < ObjectCount);
212
213 /* For a Waitall, we can't have the same object more then once */
214 if (WaitType == WaitAll)
215 {
216 /* Clear the main loop variable */
217 i = 0;
218
219 /* Start the loop */
220 do
221 {
222 /* Check the current and forward object */
223 for (j = i + 1; j < ObjectCount; j++)
224 {
225 /* Make sure they don't match */
226 if (WaitObjects[i] == WaitObjects[j])
227 {
228 /* Fail */
229 Status = STATUS_INVALID_PARAMETER_MIX;
230 goto Quickie;
231 }
232 }
233
234 /* Keep looping */
235 i++;
236 } while (i < ObjectCount);
237 }
238
239 /* Now we can finally wait. Use SEH since it can raise an exception */
240 _SEH2_TRY
241 {
242 /* We're done playing with handles */
243 LockInUse = FALSE;
244 KeLeaveCriticalRegion();
245
246 /* Do the kernel wait */
247 Status = KeWaitForMultipleObjects(ObjectCount,
248 WaitObjects,
249 WaitType,
250 UserRequest,
251 PreviousMode,
252 Alertable,
253 TimeOut,
254 WaitBlockArray);
255 }
256 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
257 {
258 /* Get the exception code */
259 Status = _SEH2_GetExceptionCode();
260 }
261 _SEH2_END;
262
263 Quickie:
264 /* First derefence */
265 while (ReferencedObjects)
266 {
267 /* Decrease the number of objects */
268 ReferencedObjects--;
269
270 /* Check if we had a valid object in this position */
271 if (Objects[ReferencedObjects])
272 {
273 /* Dereference it */
274 ObDereferenceObject(Objects[ReferencedObjects]);
275 }
276 }
277
278 /* Free wait block array */
279 if (WaitBlockArray) ExFreePoolWithTag(WaitBlockArray, TAG_WAIT);
280
281 /* Re-enable APCs if needed */
282 if (LockInUse) KeLeaveCriticalRegion();
283
284 /* Return status */
285 return Status;
286 }
287
288 /*++
289 * @name NtWaitForMultipleObjects32
290 * @implemented NT5.1
291 *
292 * The NtWaitForMultipleObjects32 routine <FILLMEIN>
293 *
294 * @param ObjectCount
295 * <FILLMEIN>
296 *
297 * @param HandleArray
298 * <FILLMEIN>
299 *
300 * @param WaitType
301 * <FILLMEIN>
302 *
303 * @param Alertable
304 * <FILLMEIN>
305 *
306 * @param TimeOut
307 * <FILLMEIN>
308 *
309 * @return STATUS_SUCCESS or appropriate error value.
310 *
311 * @remarks None.
312 *
313 *--*/
314 NTSTATUS
315 NTAPI
316 NtWaitForMultipleObjects32(IN ULONG ObjectCount,
317 IN PLONG Handles,
318 IN WAIT_TYPE WaitType,
319 IN BOOLEAN Alertable,
320 IN PLARGE_INTEGER TimeOut OPTIONAL)
321 {
322 /* FIXME WOW64 */
323 return NtWaitForMultipleObjects(ObjectCount,
324 (PHANDLE)Handles,
325 WaitType,
326 Alertable,
327 TimeOut);
328 }
329
330 /*++
331 * @name NtWaitForSingleObject
332 * @implemented NT4
333 *
334 * The NtWaitForSingleObject routine <FILLMEIN>
335 *
336 * @param ObjectHandle
337 * <FILLMEIN>
338 *
339 * @param Alertable
340 * <FILLMEIN>
341 *
342 * @param TimeOut
343 * <FILLMEIN>
344 *
345 * @return STATUS_SUCCESS or appropriate error value.
346 *
347 * @remarks None.
348 *
349 *--*/
350 NTSTATUS
351 NTAPI
352 NtWaitForSingleObject(IN HANDLE ObjectHandle,
353 IN BOOLEAN Alertable,
354 IN PLARGE_INTEGER TimeOut OPTIONAL)
355 {
356 PVOID Object, WaitableObject;
357 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
358 LARGE_INTEGER SafeTimeOut;
359 NTSTATUS Status;
360
361 /* Check if we came with a timeout from user mode */
362 if ((TimeOut) && (PreviousMode != KernelMode))
363 {
364 /* Enter SEH for proving */
365 _SEH2_TRY
366 {
367 /* Make a copy on the stack */
368 SafeTimeOut = ProbeForReadLargeInteger(TimeOut);
369 TimeOut = &SafeTimeOut;
370 }
371 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
372 {
373 /* Return the exception code */
374 _SEH2_YIELD(return _SEH2_GetExceptionCode());
375 }
376 _SEH2_END;
377 }
378
379 /* Get the Object */
380 Status = ObReferenceObjectByHandle(ObjectHandle,
381 SYNCHRONIZE,
382 NULL,
383 PreviousMode,
384 &Object,
385 NULL);
386 if (NT_SUCCESS(Status))
387 {
388 /* Get the Waitable Object */
389 WaitableObject = OBJECT_TO_OBJECT_HEADER(Object)->Type->DefaultObject;
390
391 /* Is it an offset for internal objects? */
392 if (IsPointerOffset(WaitableObject))
393 {
394 /* Turn it into a pointer */
395 WaitableObject = (PVOID)((ULONG_PTR)Object +
396 (ULONG_PTR)WaitableObject);
397 }
398
399 /* SEH this since it can also raise an exception */
400 _SEH2_TRY
401 {
402 /* Ask the kernel to do the wait */
403 Status = KeWaitForSingleObject(WaitableObject,
404 UserRequest,
405 PreviousMode,
406 Alertable,
407 TimeOut);
408 }
409 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
410 {
411 /* Get the exception code */
412 Status = _SEH2_GetExceptionCode();
413 }
414 _SEH2_END;
415
416 /* Dereference the Object */
417 ObDereferenceObject(Object);
418 }
419
420 /* Return the status */
421 return Status;
422 }
423
424 /*++
425 * @name NtSignalAndWaitForSingleObject
426 * @implemented NT4
427 *
428 * The NtSignalAndWaitForSingleObject routine <FILLMEIN>
429 *
430 * @param ObjectHandleToSignal
431 * <FILLMEIN>
432 *
433 * @param WaitableObjectHandle
434 * <FILLMEIN>
435 *
436 * @param Alertable
437 * <FILLMEIN>
438 *
439 * @param TimeOut
440 * <FILLMEIN>
441 *
442 * @return STATUS_SUCCESS or appropriate error value.
443 *
444 * @remarks None.
445 *
446 *--*/
447 NTSTATUS
448 NTAPI
449 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,
450 IN HANDLE WaitableObjectHandle,
451 IN BOOLEAN Alertable,
452 IN PLARGE_INTEGER TimeOut OPTIONAL)
453 {
454 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
455 POBJECT_TYPE Type;
456 PVOID SignalObj, WaitObj, WaitableObject;
457 LARGE_INTEGER SafeTimeOut;
458 OBJECT_HANDLE_INFORMATION HandleInfo;
459 NTSTATUS Status;
460
461 /* Check if we came with a timeout from user mode */
462 if ((TimeOut) && (PreviousMode != KernelMode))
463 {
464 /* Enter SEH for probing */
465 _SEH2_TRY
466 {
467 /* Make a copy on the stack */
468 SafeTimeOut = ProbeForReadLargeInteger(TimeOut);
469 TimeOut = &SafeTimeOut;
470 }
471 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
472 {
473 /* Return the exception code */
474 _SEH2_YIELD(return _SEH2_GetExceptionCode());
475 }
476 _SEH2_END;
477 }
478
479 /* Start by getting the signal object*/
480 Status = ObReferenceObjectByHandle(ObjectHandleToSignal,
481 0,
482 NULL,
483 PreviousMode,
484 &SignalObj,
485 &HandleInfo);
486 if (!NT_SUCCESS(Status)) return Status;
487
488 /* Now get the wait object */
489 Status = ObReferenceObjectByHandle(WaitableObjectHandle,
490 SYNCHRONIZE,
491 NULL,
492 PreviousMode,
493 &WaitObj,
494 NULL);
495 if (!NT_SUCCESS(Status))
496 {
497 /* Failed to reference the wait object */
498 ObDereferenceObject(SignalObj);
499 return Status;
500 }
501
502 /* Get the real waitable object */
503 WaitableObject = OBJECT_TO_OBJECT_HEADER(WaitObj)->Type->DefaultObject;
504
505 /* Handle internal offset */
506 if (IsPointerOffset(WaitableObject))
507 {
508 /* Get real pointer */
509 WaitableObject = (PVOID)((ULONG_PTR)WaitObj +
510 (ULONG_PTR)WaitableObject);
511 }
512
513 /* Check Signal Object Type */
514 Type = OBJECT_TO_OBJECT_HEADER(SignalObj)->Type;
515 if (Type == ExEventObjectType)
516 {
517 /* Check if we came from user-mode without the right access */
518 if ((PreviousMode != KernelMode) &&
519 !(HandleInfo.GrantedAccess & EVENT_MODIFY_STATE))
520 {
521 /* Fail: lack of rights */
522 Status = STATUS_ACCESS_DENIED;
523 goto Quickie;
524 }
525
526 /* Set the Event */
527 KeSetEvent(SignalObj, EVENT_INCREMENT, TRUE);
528 }
529 else if (Type == ExMutantObjectType)
530 {
531 /* This can raise an exception */
532 _SEH2_TRY
533 {
534 /* Release the mutant */
535 KeReleaseMutant(SignalObj, MUTANT_INCREMENT, FALSE, TRUE);
536 }
537 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
538 {
539 /* Get the exception code */
540 Status = _SEH2_GetExceptionCode();
541 }
542 _SEH2_END;
543 }
544 else if (Type == ExSemaphoreObjectType)
545 {
546 /* Check if we came from user-mode without the right access */
547 if ((PreviousMode != KernelMode) &&
548 !(HandleInfo.GrantedAccess & SEMAPHORE_MODIFY_STATE))
549 {
550 /* Fail: lack of rights */
551 Status = STATUS_ACCESS_DENIED;
552 goto Quickie;
553 }
554
555 /* This can raise an exception*/
556 _SEH2_TRY
557 {
558 /* Release the semaphore */
559 KeReleaseSemaphore(SignalObj, SEMAPHORE_INCREMENT, 1, TRUE);
560 }
561 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
562 {
563 /* Get the exception code */
564 Status = _SEH2_GetExceptionCode();
565 }
566 _SEH2_END;
567 }
568 else
569 {
570 /* This isn't a valid object to be waiting on */
571 Status = STATUS_OBJECT_TYPE_MISMATCH;
572 }
573
574 /* Make sure we didn't fail */
575 if (NT_SUCCESS(Status))
576 {
577 /* SEH this since it can also raise an exception */
578 _SEH2_TRY
579 {
580 /* Perform the wait now */
581 Status = KeWaitForSingleObject(WaitableObject,
582 UserRequest,
583 PreviousMode,
584 Alertable,
585 TimeOut);
586 }
587 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
588 {
589 /* Get the exception code */
590 Status = _SEH2_GetExceptionCode();
591 }
592 _SEH2_END;
593 }
594
595 /* We're done here, dereference both objects */
596 Quickie:
597 ObDereferenceObject(SignalObj);
598 ObDereferenceObject(WaitObj);
599 return Status;
600 }
601
602 /* EOF */