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