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