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