- Merge the remaining portion of the wlan-bringup branch
[reactos.git] / reactos / ntoskrnl / ps / state.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/state.c
5 * PURPOSE: Process Manager: Process/Thread State Control
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PRIVATE FUNCTIONS *********************************************************/
17
18 VOID
19 NTAPI
20 PspQueueApcSpecialApc(IN PKAPC Apc,
21 IN OUT PKNORMAL_ROUTINE* NormalRoutine,
22 IN OUT PVOID* NormalContext,
23 IN OUT PVOID* SystemArgument1,
24 IN OUT PVOID* SystemArgument2)
25 {
26 /* Free the APC and do nothing else */
27 ExFreePool(Apc);
28 }
29
30 NTSTATUS
31 NTAPI
32 PsResumeThread(IN PETHREAD Thread,
33 OUT PULONG PreviousCount OPTIONAL)
34 {
35 ULONG OldCount;
36 PAGED_CODE();
37
38 /* Resume the thread */
39 OldCount = KeResumeThread(&Thread->Tcb);
40
41 /* Return the count if asked */
42 if (PreviousCount) *PreviousCount = OldCount;
43 return STATUS_SUCCESS;
44 }
45
46 NTSTATUS
47 NTAPI
48 PsSuspendThread(IN PETHREAD Thread,
49 OUT PULONG PreviousCount OPTIONAL)
50 {
51 NTSTATUS Status;
52 ULONG OldCount = 0;
53 PAGED_CODE();
54
55 /* Guard with SEH because KeSuspendThread can raise an exception */
56 _SEH2_TRY
57 {
58 /* Check if we're suspending ourselves */
59 if (Thread == PsGetCurrentThread())
60 {
61 /* Do the suspend */
62 OldCount = KeSuspendThread(&Thread->Tcb);
63
64 /* We are done */
65 Status = STATUS_SUCCESS;
66 }
67 else
68 {
69 /* Acquire rundown */
70 if (ExAcquireRundownProtection(&Thread->RundownProtect))
71 {
72 /* Make sure the thread isn't terminating */
73 if (Thread->Terminated)
74 {
75 /* Fail */
76 Status = STATUS_THREAD_IS_TERMINATING;
77 }
78 else
79 {
80 /* Otherwise, do the suspend */
81 OldCount = KeSuspendThread(&Thread->Tcb);
82
83 /* Check if it terminated during the suspend */
84 if (Thread->Terminated)
85 {
86 /* Wake it back up and fail */
87 KeForceResumeThread(&Thread->Tcb);
88 Status = STATUS_THREAD_IS_TERMINATING;
89 OldCount = 0;
90 }
91 }
92
93 /* Release rundown protection */
94 ExReleaseRundownProtection(&Thread->RundownProtect);
95
96 /* We are done */
97 Status = STATUS_SUCCESS;
98 }
99 else
100 {
101 /* Thread is terminating */
102 Status = STATUS_THREAD_IS_TERMINATING;
103 }
104 }
105 }
106 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
107 {
108 /* Get the exception code */
109 Status = _SEH2_GetExceptionCode();
110
111 /* Don't fail if we merely couldn't write the handle back */
112 if (Status != STATUS_SUSPEND_COUNT_EXCEEDED) Status = STATUS_SUCCESS;
113 }
114 _SEH2_END;
115
116 /* Write back the previous count */
117 if (PreviousCount) *PreviousCount = OldCount;
118 return Status;
119 }
120
121 NTSTATUS
122 NTAPI
123 PsResumeProcess(IN PEPROCESS Process)
124 {
125 PETHREAD Thread;
126 PAGED_CODE();
127
128 /* Lock the Process */
129 if (!ExAcquireRundownProtection(&Process->RundownProtect))
130 {
131 /* Process is terminating */
132 return STATUS_PROCESS_IS_TERMINATING;
133 }
134
135 /* Get the first thread */
136 Thread = PsGetNextProcessThread(Process, NULL);
137 while (Thread)
138 {
139 /* Resume it */
140 KeResumeThread(&Thread->Tcb);
141
142 /* Move to the next thread */
143 Thread = PsGetNextProcessThread(Process, Thread);
144 }
145
146 /* Unlock the process */
147 ExReleaseRundownProtection(&Process->RundownProtect);
148 return STATUS_SUCCESS;
149 }
150
151 NTSTATUS
152 NTAPI
153 PsSuspendProcess(IN PEPROCESS Process)
154 {
155 PETHREAD Thread;
156 PAGED_CODE();
157
158 /* Lock the Process */
159 if (!ExAcquireRundownProtection(&Process->RundownProtect))
160 {
161 /* Process is terminating */
162 return STATUS_PROCESS_IS_TERMINATING;
163 }
164
165 /* Get the first thread */
166 Thread = PsGetNextProcessThread(Process, NULL);
167 while (Thread)
168 {
169 /* Resume it */
170 PsSuspendThread(Thread, NULL);
171
172 /* Move to the next thread */
173 Thread = PsGetNextProcessThread(Process, Thread);
174 }
175
176 /* Unlock the process */
177 ExReleaseRundownProtection(&Process->RundownProtect);
178 return STATUS_SUCCESS;
179 }
180
181 /* PUBLIC FUNCTIONS **********************************************************/
182
183 /*
184 * @implemented
185 */
186 NTSTATUS
187 NTAPI
188 NtAlertThread(IN HANDLE ThreadHandle)
189 {
190 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
191 PETHREAD Thread;
192 NTSTATUS Status;
193
194 /* Reference the Object */
195 Status = ObReferenceObjectByHandle(ThreadHandle,
196 THREAD_SUSPEND_RESUME,
197 PsThreadType,
198 PreviousMode,
199 (PVOID*)&Thread,
200 NULL);
201 if (NT_SUCCESS(Status))
202 {
203 /*
204 * Do an alert depending on the processor mode. If some kmode code wants to
205 * enforce a umode alert it should call KeAlertThread() directly. If kmode
206 * code wants to do a kmode alert it's sufficient to call it with Zw or just
207 * use KeAlertThread() directly
208 */
209 KeAlertThread(&Thread->Tcb, PreviousMode);
210
211 /* Dereference Object */
212 ObDereferenceObject(Thread);
213 }
214
215 /* Return status */
216 return Status;
217 }
218
219 NTSTATUS
220 NTAPI
221 NtAlertResumeThread(IN HANDLE ThreadHandle,
222 OUT PULONG SuspendCount)
223 {
224 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
225 PETHREAD Thread;
226 NTSTATUS Status;
227 ULONG PreviousState;
228
229 /* Check if we came from user mode with a suspend count */
230 if ((SuspendCount) && (PreviousMode != KernelMode))
231 {
232 /* Enter SEH for probing */
233 _SEH2_TRY
234 {
235 /* Probe the count */
236 ProbeForWriteUlong(SuspendCount);
237 }
238 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
239 {
240 /* Return the exception code */
241 _SEH2_YIELD(return _SEH2_GetExceptionCode());
242 }
243 _SEH2_END;
244 }
245
246 /* Reference the Object */
247 Status = ObReferenceObjectByHandle(ThreadHandle,
248 THREAD_SUSPEND_RESUME,
249 PsThreadType,
250 PreviousMode,
251 (PVOID*)&Thread,
252 NULL);
253 if (NT_SUCCESS(Status))
254 {
255 /* Call the Kernel Function */
256 PreviousState = KeAlertResumeThread(&Thread->Tcb);
257
258 /* Dereference Object */
259 ObDereferenceObject(Thread);
260
261 /* Check if the caller gave a suspend count */
262 if (SuspendCount)
263 {
264 /* Enter SEH for write */
265 _SEH2_TRY
266 {
267 /* Write state back */
268 *SuspendCount = PreviousState;
269 }
270 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
271 {
272 /* Get exception code */
273 Status = _SEH2_GetExceptionCode();
274 }
275 _SEH2_END;
276 }
277 }
278
279 /* Return status */
280 return Status;
281 }
282
283 NTSTATUS
284 NTAPI
285 NtResumeThread(IN HANDLE ThreadHandle,
286 OUT PULONG SuspendCount OPTIONAL)
287 {
288 PETHREAD Thread;
289 ULONG Prev;
290 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
291 NTSTATUS Status;
292 PAGED_CODE();
293
294 /* Check if caller gave a suspend count from user mode */
295 if ((SuspendCount) && (PreviousMode != KernelMode))
296 {
297 /* Enter SEH for probing */
298 _SEH2_TRY
299 {
300 /* Probe the count */
301 ProbeForWriteUlong(SuspendCount);
302 }
303 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
304 {
305 /* Return the exception code */
306 _SEH2_YIELD(return _SEH2_GetExceptionCode());
307 }
308 _SEH2_END;
309 }
310
311 /* Get the Thread Object */
312 Status = ObReferenceObjectByHandle(ThreadHandle,
313 THREAD_SUSPEND_RESUME,
314 PsThreadType,
315 PreviousMode,
316 (PVOID*)&Thread,
317 NULL);
318 if (!NT_SUCCESS(Status)) return Status;
319
320 /* Call the internal function */
321 Status = PsResumeThread(Thread, &Prev);
322
323 /* Check if the caller wanted the count back */
324 if (SuspendCount)
325 {
326 /* Enter SEH for write back */
327 _SEH2_TRY
328 {
329 /* Write the count */
330 *SuspendCount = Prev;
331 }
332 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
333 {
334 /* Get the exception code */
335 Status = _SEH2_GetExceptionCode();
336 }
337 _SEH2_END;
338 }
339
340 /* Dereference and return */
341 ObDereferenceObject(Thread);
342 return Status;
343 }
344
345 NTSTATUS
346 NTAPI
347 NtSuspendThread(IN HANDLE ThreadHandle,
348 OUT PULONG PreviousSuspendCount OPTIONAL)
349 {
350 PETHREAD Thread;
351 ULONG Prev;
352 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
353 NTSTATUS Status;
354 PAGED_CODE();
355
356 /* Check if caller gave a suspend count from user mode */
357 if ((PreviousSuspendCount) && (PreviousMode != KernelMode))
358 {
359 /* Enter SEH for probing */
360 _SEH2_TRY
361 {
362 /* Probe the count */
363 ProbeForWriteUlong(PreviousSuspendCount);
364 }
365 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
366 {
367 /* Return the exception code */
368 _SEH2_YIELD(return _SEH2_GetExceptionCode());
369 }
370 _SEH2_END;
371 }
372
373 /* Get the Thread Object */
374 Status = ObReferenceObjectByHandle(ThreadHandle,
375 THREAD_SUSPEND_RESUME,
376 PsThreadType,
377 PreviousMode,
378 (PVOID*)&Thread,
379 NULL);
380 if (!NT_SUCCESS(Status)) return Status;
381
382 /* Call the internal function */
383 Status = PsSuspendThread(Thread, &Prev);
384 ObDereferenceObject(Thread);
385 if (!NT_SUCCESS(Status)) return Status;
386
387 /* Protect write with SEH */
388 _SEH2_TRY
389 {
390 /* Return the Previous Count */
391 if (PreviousSuspendCount) *PreviousSuspendCount = Prev;
392 }
393 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
394 {
395 /* Get the exception code */
396 Status = _SEH2_GetExceptionCode();
397 }
398 _SEH2_END;
399
400 /* Return */
401 return Status;
402 }
403
404 NTSTATUS
405 NTAPI
406 NtSuspendProcess(IN HANDLE ProcessHandle)
407 {
408 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
409 PEPROCESS Process;
410 NTSTATUS Status;
411 PAGED_CODE();
412
413 /* Reference the process */
414 Status = ObReferenceObjectByHandle(ProcessHandle,
415 PROCESS_SUSPEND_RESUME,
416 PsProcessType,
417 PreviousMode,
418 (PVOID*)&Process,
419 NULL);
420 if (NT_SUCCESS(Status))
421 {
422 /* Call the internal function */
423 Status = PsSuspendProcess(Process);
424 ObDereferenceObject(Process);
425 }
426
427 /* Return status */
428 return Status;
429 }
430
431 NTSTATUS
432 NTAPI
433 NtResumeProcess(IN HANDLE ProcessHandle)
434 {
435 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
436 PEPROCESS Process;
437 NTSTATUS Status;
438 PAGED_CODE();
439
440 /* Reference the process */
441 Status = ObReferenceObjectByHandle(ProcessHandle,
442 PROCESS_SUSPEND_RESUME,
443 PsProcessType,
444 PreviousMode,
445 (PVOID*)&Process,
446 NULL);
447 if (NT_SUCCESS(Status))
448 {
449 /* Call the internal function */
450 Status = PsResumeProcess(Process);
451 ObDereferenceObject(Process);
452 }
453
454 /* Return status */
455 return Status;
456 }
457
458 NTSTATUS
459 NTAPI
460 NtTestAlert(VOID)
461 {
462 /* Check and Alert Thread if needed */
463 return KeTestAlertThread(ExGetPreviousMode()) ?
464 STATUS_ALERTED : STATUS_SUCCESS;
465 }
466
467 /*++
468 * @name NtQueueApcThread
469 * NT4
470 *
471 * This routine is used to queue an APC from user-mode for the specified
472 * thread.
473 *
474 * @param ThreadHandle
475 * Handle to the Thread.
476 * This handle must have THREAD_SET_CONTEXT privileges.
477 *
478 * @param ApcRoutine
479 * Pointer to the APC Routine to call when the APC executes.
480 *
481 * @param NormalContext
482 * Pointer to the context to send to the Normal Routine.
483 *
484 * @param SystemArgument[1-2]
485 * Pointer to a set of two parameters that contain untyped data.
486 *
487 * @return STATUS_SUCCESS or failure cute from associated calls.
488 *
489 * @remarks The thread must enter an alertable wait before the APC will be
490 * delivered.
491 *
492 *--*/
493 NTSTATUS
494 NTAPI
495 NtQueueApcThread(IN HANDLE ThreadHandle,
496 IN PKNORMAL_ROUTINE ApcRoutine,
497 IN PVOID NormalContext,
498 IN PVOID SystemArgument1,
499 IN PVOID SystemArgument2)
500 {
501 PKAPC Apc;
502 PETHREAD Thread;
503 NTSTATUS Status = STATUS_SUCCESS;
504 PAGED_CODE();
505
506 /* Get ETHREAD from Handle */
507 Status = ObReferenceObjectByHandle(ThreadHandle,
508 THREAD_SET_CONTEXT,
509 PsThreadType,
510 ExGetPreviousMode(),
511 (PVOID)&Thread,
512 NULL);
513 if (!NT_SUCCESS(Status)) return Status;
514
515 /* Check if this is a System Thread */
516 if (Thread->SystemThread)
517 {
518 /* Fail */
519 Status = STATUS_INVALID_HANDLE;
520 goto Quit;
521 }
522
523 /* Allocate an APC */
524 Apc = ExAllocatePoolWithTag(NonPagedPool |
525 POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
526 sizeof(KAPC),
527 TAG_PS_APC);
528 if (!Apc)
529 {
530 /* Fail */
531 Status = STATUS_NO_MEMORY;
532 goto Quit;
533 }
534
535 /* Initialize the APC */
536 KeInitializeApc(Apc,
537 &Thread->Tcb,
538 OriginalApcEnvironment,
539 PspQueueApcSpecialApc,
540 NULL,
541 ApcRoutine,
542 UserMode,
543 NormalContext);
544
545 /* Queue it */
546 if (!KeInsertQueueApc(Apc,
547 SystemArgument1,
548 SystemArgument2,
549 IO_NO_INCREMENT))
550 {
551 /* We failed, free it */
552 ExFreePool(Apc);
553 Status = STATUS_UNSUCCESSFUL;
554 }
555
556 /* Dereference Thread and Return */
557 Quit:
558 ObDereferenceObject(Thread);
559 return Status;
560 }
561
562 /* EOF */