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