- Fix broken loop, this fixes the WINE thread wait tests
[reactos.git] / reactos / ntoskrnl / ob / wait.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ob/wait.c
5 * PURPOSE: Handles Waiting on Objects
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #define TAG_WAIT TAG('W', 'a', 'i', 't')
18
19 /* FUNCTIONS *****************************************************************/
20
21 NTSTATUS
22 STDCALL
23 NtWaitForMultipleObjects(IN ULONG ObjectCount,
24 IN PHANDLE HandleArray,
25 IN WAIT_TYPE WaitType,
26 IN BOOLEAN Alertable,
27 IN PLARGE_INTEGER TimeOut OPTIONAL)
28 {
29 PKWAIT_BLOCK WaitBlockArray = NULL;
30 HANDLE Handles[MAXIMUM_WAIT_OBJECTS];
31 PVOID Objects[MAXIMUM_WAIT_OBJECTS];
32 PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS];
33 ULONG i = 0, ReferencedObjects = 0, j;
34 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
35 LARGE_INTEGER SafeTimeOut;
36 BOOLEAN LockInUse;
37 PHANDLE_TABLE_ENTRY HandleEntry;
38 POBJECT_HEADER ObjectHeader;
39 PHANDLE_TABLE HandleTable;
40 ACCESS_MASK GrantedAccess;
41 PVOID DefaultObject;
42 NTSTATUS Status = STATUS_SUCCESS;
43
44 DPRINT("NtWaitForMultipleObjects(ObjectCount %lu HandleArray[] %x, Alertable %d, "
45 "TimeOut %x)\n", ObjectCount, HandleArray, Alertable, TimeOut);
46
47 /* Enter a critical region since we'll play with handles */
48 LockInUse = TRUE;
49 KeEnterCriticalRegion();
50
51 /* Check for valid Object Count */
52 if ((ObjectCount > MAXIMUM_WAIT_OBJECTS) || !ObjectCount)
53 {
54 Status = STATUS_INVALID_PARAMETER_1;
55 DPRINT1("No object count, or too many objects\n");
56 goto Quickie;
57 }
58
59 /* Check for valid Wait Type */
60 if ((WaitType != WaitAll) && (WaitType != WaitAny))
61 {
62 Status = STATUS_INVALID_PARAMETER_3;
63 DPRINT1("Invalid wait type\n");
64 goto Quickie;
65 }
66
67 /* Capture arguments */
68 _SEH_TRY
69 {
70 if(PreviousMode != KernelMode)
71 {
72 ProbeForRead(HandleArray,
73 ObjectCount * sizeof(HANDLE),
74 sizeof(HANDLE));
75
76 if(TimeOut)
77 {
78 ProbeForRead(TimeOut,
79 sizeof(LARGE_INTEGER),
80 sizeof(ULONG));
81
82 /* Make a local copy of the timeout on the stack */
83 SafeTimeOut = *TimeOut;
84 TimeOut = &SafeTimeOut;
85 }
86 }
87
88 /*
89 * Make a copy so we don't have to guard with SEH later and keep
90 * track of what objects we referenced if dereferencing pointers
91 * suddenly fails
92 */
93 RtlCopyMemory(Handles,
94 HandleArray,
95 ObjectCount * sizeof(HANDLE));
96 }
97 _SEH_HANDLE
98 {
99 Status = _SEH_GetExceptionCode();
100 }
101 _SEH_END;
102
103 if(!NT_SUCCESS(Status)) goto Quickie;
104
105 /* Check if we can use the internal Wait Array */
106 if (ObjectCount > THREAD_WAIT_OBJECTS)
107 {
108 /* Allocate from Pool */
109 WaitBlockArray = ExAllocatePoolWithTag(NonPagedPool,
110 ObjectCount * sizeof(KWAIT_BLOCK),
111 TAG_WAIT);
112 }
113
114 /* Start the loop */
115 do
116 {
117 /* Use the right Executive Handle */
118 if(ObIsKernelHandle(Handles[i], PreviousMode))
119 {
120 /* Use the System Handle Table and decode */
121 HandleTable = ObpKernelHandleTable;
122 Handles[i] = ObKernelHandleToHandle(Handles[i]);
123 }
124 else
125 {
126 /* Use the Process' Handle table and get the Ex Handle */
127 HandleTable = PsGetCurrentProcess()->ObjectTable;
128 }
129
130 /* Get a pointer to it */
131 if (!(HandleEntry = ExMapHandleToPointer(HandleTable, Handles[i])))
132 {
133 DPRINT1("Invalid handle\n");
134 Status = STATUS_INVALID_HANDLE;
135 goto Quickie;
136 }
137
138 /* Check for synchronize access */
139 GrantedAccess = HandleEntry->u2.GrantedAccess;
140 if ((PreviousMode != KernelMode) && (!(GrantedAccess & SYNCHRONIZE)))
141 {
142 /* Unlock the entry and fail */
143 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
144 Status = STATUS_ACCESS_DENIED;
145 DPRINT1("Handle doesn't have SYNCH access\n");
146 goto Quickie;
147 }
148
149 /* Get the Object Header */
150 ObjectHeader = EX_HTE_TO_HDR(HandleEntry);
151
152 /* Get default Object */
153 DefaultObject = ObjectHeader->Type->DefaultObject;
154
155 /* Check if it's the internal offset */
156 if ((LONG_PTR)DefaultObject >= 0)
157 {
158 /* Increase reference count */
159 InterlockedIncrement(&ObjectHeader->PointerCount);
160 ReferencedObjects++;
161
162 /* Save the Object and Wait Object, this is a relative offset */
163 Objects[i] = &ObjectHeader->Body;
164 WaitObjects[i] = (PVOID)((ULONG_PTR)&ObjectHeader->Body +
165 (ULONG_PTR)DefaultObject);
166 }
167 else
168 {
169 /* This is our internal Object */
170 ReferencedObjects++;
171 Objects[i] = NULL;
172 WaitObjects[i] = DefaultObject;
173 }
174
175 /* Unlock the Handle Table Entry */
176 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
177
178 /* Keep looping */
179 i++;
180 } while (i < ObjectCount);
181
182 /* For a Waitall, we can't have the same object more then once */
183 if (WaitType == WaitAll)
184 {
185 /* Clear the main loop variable */
186 i = 0;
187
188 /* Start the loop */
189 do
190 {
191 /* Check the current and forward object */
192 for (j = i + 1; j < ObjectCount; j++)
193 {
194 /* Make sure they don't match */
195 if (WaitObjects[i] == WaitObjects[j])
196 {
197 /* Fail */
198 Status = STATUS_INVALID_PARAMETER_MIX;
199 DPRINT1("Objects duplicated with WaitAll\n");
200 goto Quickie;
201 }
202 }
203
204 /* Keep looping */
205 i++;
206 } while (i < ObjectCount);
207 }
208
209 /* Now we can finally wait. Use SEH since it can raise an exception */
210 _SEH_TRY
211 {
212 /* We're done playing with handles */
213 LockInUse = FALSE;
214 KeLeaveCriticalRegion();
215
216 /* Do the kernel wait */
217 Status = KeWaitForMultipleObjects(ObjectCount,
218 WaitObjects,
219 WaitType,
220 UserRequest,
221 PreviousMode,
222 Alertable,
223 TimeOut,
224 WaitBlockArray);
225 }
226 _SEH_HANDLE
227 {
228 Status = _SEH_GetExceptionCode();
229 }
230 _SEH_END;
231
232 Quickie:
233 /* First derefence */
234 while (ReferencedObjects)
235 {
236 ReferencedObjects--;
237 if (Objects[ReferencedObjects])
238 {
239 ObDereferenceObject(Objects[ReferencedObjects]);
240 }
241 }
242
243 /* Free wait block array */
244 if (WaitBlockArray) ExFreePool(WaitBlockArray);
245
246 /* Re-enable APCs if needed */
247 if (LockInUse) KeLeaveCriticalRegion();
248
249 /* Return status */
250 DPRINT1("Returning: %x\n", Status);
251 return Status;
252 }
253
254 /*
255 * @implemented
256 */
257 NTSTATUS
258 STDCALL
259 NtWaitForSingleObject(IN HANDLE ObjectHandle,
260 IN BOOLEAN Alertable,
261 IN PLARGE_INTEGER TimeOut OPTIONAL)
262 {
263 PVOID Object, WaitableObject;
264 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
265 LARGE_INTEGER SafeTimeOut;
266 NTSTATUS Status = STATUS_SUCCESS;
267
268 DPRINT("NtWaitForSingleObject(ObjectHandle %x, Alertable %d, TimeOut %x)\n",
269 ObjectHandle,Alertable,TimeOut);
270
271 /* Capture timeout */
272 if(TimeOut && PreviousMode != KernelMode)
273 {
274 _SEH_TRY
275 {
276 ProbeForRead(TimeOut,
277 sizeof(LARGE_INTEGER),
278 sizeof(ULONG));
279 /* Make a copy on the stack */
280 SafeTimeOut = *TimeOut;
281 TimeOut = &SafeTimeOut;
282 }
283 _SEH_HANDLE
284 {
285 Status = _SEH_GetExceptionCode();
286 }
287 _SEH_END;
288
289 if(!NT_SUCCESS(Status)) return Status;
290 }
291
292 /* Get the Object */
293 Status = ObReferenceObjectByHandle(ObjectHandle,
294 SYNCHRONIZE,
295 NULL,
296 PreviousMode,
297 &Object,
298 NULL);
299 if (NT_SUCCESS(Status))
300 {
301 /* Get the Waitable Object */
302 WaitableObject = BODY_TO_HEADER(Object)->Type->DefaultObject;
303
304 /* Is it an offset for internal objects? */
305 if ((LONG_PTR)WaitableObject >= 0)
306 {
307 /* Turn it into a pointer */
308 WaitableObject = (PVOID)((ULONG_PTR)Object +
309 (ULONG_PTR)WaitableObject);
310 }
311
312 /* Now wait. Also SEH this since it can also raise an exception */
313 _SEH_TRY
314 {
315 Status = KeWaitForSingleObject(WaitableObject,
316 UserRequest,
317 PreviousMode,
318 Alertable,
319 TimeOut);
320 }
321 _SEH_HANDLE
322 {
323 Status = _SEH_GetExceptionCode();
324 }
325 _SEH_END;
326
327 /* Dereference the Object */
328 ObDereferenceObject(Object);
329 }
330
331 /* Return the status */
332 return Status;
333 }
334
335 NTSTATUS
336 STDCALL
337 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,
338 IN HANDLE WaitableObjectHandle,
339 IN BOOLEAN Alertable,
340 IN PLARGE_INTEGER TimeOut OPTIONAL)
341 {
342 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
343 POBJECT_TYPE Type;
344 PVOID SignalObj;
345 PVOID WaitObj;
346 PVOID WaitableObject;
347 LARGE_INTEGER SafeTimeOut;
348 OBJECT_HANDLE_INFORMATION HandleInfo;
349 NTSTATUS Status = STATUS_SUCCESS;
350
351 /* Capture timeout */
352 DPRINT("NtSignalAndWaitForSingleObject\n");
353 if(TimeOut && PreviousMode != KernelMode)
354 {
355 _SEH_TRY
356 {
357 ProbeForRead(TimeOut,
358 sizeof(LARGE_INTEGER),
359 sizeof(ULONG));
360 /* Make a copy on the stack */
361 SafeTimeOut = *TimeOut;
362 TimeOut = &SafeTimeOut;
363 }
364 _SEH_HANDLE
365 {
366 Status = _SEH_GetExceptionCode();
367 }
368 _SEH_END;
369
370 if(!NT_SUCCESS(Status)) return Status;
371 }
372
373 /* Start by getting the signal object*/
374 Status = ObReferenceObjectByHandle(ObjectHandleToSignal,
375 0,
376 NULL,
377 PreviousMode,
378 &SignalObj,
379 &HandleInfo);
380 if (!NT_SUCCESS(Status))
381 {
382 return Status;
383 }
384
385 /* Now get the wait object */
386 Status = ObReferenceObjectByHandle(WaitableObjectHandle,
387 SYNCHRONIZE,
388 NULL,
389 PreviousMode,
390 &WaitObj,
391 NULL);
392 if (!NT_SUCCESS(Status))
393 {
394 ObDereferenceObject(SignalObj);
395 return Status;
396 }
397
398 /* Get the real waitable object */
399 WaitableObject = BODY_TO_HEADER(WaitObj)->Type->DefaultObject;
400
401 /* Handle internal offset */
402 if ((LONG_PTR)WaitableObject >= 0)
403 {
404 /* Get real pointer */
405 WaitableObject = (PVOID)((ULONG_PTR)WaitObj +
406 (ULONG_PTR)WaitableObject);
407 }
408
409 /* Check Signal Object Type */
410 Type = BODY_TO_HEADER(WaitObj)->Type;
411 if (Type == ExEventObjectType)
412 {
413 /* Set the Event */
414 /* FIXME: Check permissions */
415 KeSetEvent(SignalObj, EVENT_INCREMENT, TRUE);
416 }
417 else if (Type == ExMutantObjectType)
418 {
419 /* Release the Mutant. This can raise an exception*/
420 _SEH_TRY
421 {
422 KeReleaseMutant(SignalObj, MUTANT_INCREMENT, FALSE, TRUE);
423 }
424 _SEH_HANDLE
425 {
426 Status = _SEH_GetExceptionCode();
427 goto Quickie;
428 }
429 _SEH_END;
430 }
431 else if (Type == ExSemaphoreObjectType)
432 {
433 /* Release the Semaphore. This can raise an exception*/
434 /* FIXME: Check permissions */
435 _SEH_TRY
436 {
437 KeReleaseSemaphore(SignalObj, SEMAPHORE_INCREMENT, 1, TRUE);
438 }
439 _SEH_HANDLE
440 {
441 Status = _SEH_GetExceptionCode();
442 goto Quickie;
443 }
444 _SEH_END;
445 }
446 else
447 {
448 Status = STATUS_OBJECT_TYPE_MISMATCH;
449 DPRINT1("Waiting on invalid object type\n");
450 goto Quickie;
451 }
452
453 /* Now wait. Also SEH this since it can also raise an exception */
454 _SEH_TRY
455 {
456 Status = KeWaitForSingleObject(WaitableObject,
457 UserRequest,
458 PreviousMode,
459 Alertable,
460 TimeOut);
461 }
462 _SEH_HANDLE
463 {
464 Status = _SEH_GetExceptionCode();
465 }
466 _SEH_END;
467
468 /* We're done here */
469 Quickie:
470 ObDereferenceObject(SignalObj);
471 ObDereferenceObject(WaitObj);
472 return Status;
473 }
474
475 /* EOF */