- Added constants for all the ETHREAD flags so when we use Interlocked operations...
[reactos.git] / reactos / ntoskrnl / ps / thread.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Process Manager: Thread Management
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org)
8 */
9
10 /*
11 * Alex FIXMEs:
12 * - MAJOR: Use Process Rundown
13 * - MAJOR: Use Process Pushlock Locks
14 * - MAJOR: Use Safe Referencing in PsGetNextProcess/Thread.
15 * - MAJOR: Use Guarded Mutex instead of Fast Mutex for Active Process Locks.
16 * - Generate process cookie for user-more thread.
17 * - Add security calls where necessary.
18 * - KeInit/StartThread for better isolation of code
19 */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ntoskrnl.h>
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* GLOBALS ******************************************************************/
28
29 extern LIST_ENTRY PsActiveProcessHead;
30 extern PEPROCESS PsIdleProcess;
31 extern PVOID PspSystemDllEntryPoint;
32 extern PVOID PspSystemDllBase;
33 extern PHANDLE_TABLE PspCidTable;
34 extern BOOLEAN CcPfEnablePrefetcher;
35 extern ULONG MmReadClusterSize;
36 POBJECT_TYPE PsThreadType = NULL;
37
38 /* FUNCTIONS ***************************************************************/
39
40 VOID
41 NTAPI
42 PspUserThreadStartup(PKSTART_ROUTINE StartRoutine,
43 PVOID StartContext)
44 {
45 PETHREAD Thread;
46 PTEB Teb;
47 BOOLEAN DeadThread = FALSE;
48 PAGED_CODE();
49
50 /* Go to Passive Level */
51 KeLowerIrql(PASSIVE_LEVEL);
52 Thread = PsGetCurrentThread();
53
54 /* Check if the thread is dead */
55 if (Thread->DeadThread)
56 {
57 /* Remember that we're dead */
58 DeadThread = TRUE;
59 }
60 else
61 {
62 /* Get the Locale ID and save Preferred Proc */
63 Teb = NtCurrentTeb();
64 Teb->CurrentLocale = MmGetSessionLocaleId();
65 Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
66 }
67
68 /* Check if this is a system thread, or if we're hiding */
69 if (!(Thread->SystemThread) && !(Thread->HideFromDebugger))
70 {
71 /* We're not, so notify the debugger */
72 DbgkCreateThread(StartContext);
73 }
74
75 /* Make sure we're not already dead */
76 if (!DeadThread)
77 {
78 /* Check if the Prefetcher is enabled */
79 if (CcPfEnablePrefetcher)
80 {
81 /* FIXME: Prepare to prefetch this process */
82 }
83
84 /* Raise to APC */
85 KfRaiseIrql(APC_LEVEL);
86
87 /* Queue the User APC */
88 KiInitializeUserApc(NULL,
89 (PVOID)((ULONG_PTR)Thread->Tcb.InitialStack -
90 sizeof(KTRAP_FRAME) -
91 sizeof(FX_SAVE_AREA)),
92 PspSystemDllEntryPoint,
93 NULL,
94 PspSystemDllBase,
95 NULL);
96
97 /* Lower it back to passive */
98 KeLowerIrql(PASSIVE_LEVEL);
99 }
100 else
101 {
102 /* We're dead, kill us now */
103 PspTerminateThreadByPointer(Thread,
104 STATUS_THREAD_IS_TERMINATING,
105 TRUE);
106 }
107
108 /* Do we have a cookie set yet? */
109 if (!SharedUserData->Cookie)
110 {
111 /*
112 * FIXME: Generate cookie
113 * Formula (roughly): Per-CPU Page Fault ^ Per-CPU Interrupt Time ^
114 * Global System Time ^ Stack Address of where
115 * the LARGE_INTEGER containing the Global System
116 * Time is.
117 */
118 }
119 }
120
121 VOID
122 NTAPI
123 PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine,
124 PVOID StartContext)
125 {
126 PETHREAD Thread;
127
128 /* Unlock the dispatcher Database */
129 KeLowerIrql(PASSIVE_LEVEL);
130 Thread = PsGetCurrentThread();
131
132 /* Make sure the thread isn't gone */
133 if (!(Thread->Terminated) || !(Thread->DeadThread))
134 {
135 /* Call it the Start Routine */
136 StartRoutine(StartContext);
137 }
138
139 /* Exit the thread */
140 PspTerminateThreadByPointer(Thread, STATUS_SUCCESS, TRUE);
141 }
142
143 NTSTATUS
144 NTAPI
145 PspCreateThread(OUT PHANDLE ThreadHandle,
146 IN ACCESS_MASK DesiredAccess,
147 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
148 IN HANDLE ProcessHandle,
149 IN PEPROCESS TargetProcess,
150 OUT PCLIENT_ID ClientId,
151 IN PCONTEXT ThreadContext,
152 IN PINITIAL_TEB InitialTeb,
153 IN BOOLEAN CreateSuspended,
154 IN PKSTART_ROUTINE StartRoutine OPTIONAL,
155 IN PVOID StartContext OPTIONAL)
156 {
157 HANDLE hThread;
158 PEPROCESS Process;
159 PETHREAD Thread;
160 PTEB TebBase;
161 KIRQL OldIrql;
162 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
163 NTSTATUS Status;
164 HANDLE_TABLE_ENTRY CidEntry;
165 ULONG_PTR KernelStack;
166 PAGED_CODE();
167
168 /* If we were called from PsCreateSystemThread, then we're kernel mode */
169 if (StartRoutine) PreviousMode = KernelMode;
170
171 /* Reference the Process by handle or pointer, depending on what we got */
172 if (ProcessHandle)
173 {
174 /* Normal thread or System Thread */
175 Status = ObReferenceObjectByHandle(ProcessHandle,
176 PROCESS_CREATE_THREAD,
177 PsProcessType,
178 PreviousMode,
179 (PVOID*)&Process,
180 NULL);
181 }
182 else
183 {
184 /* System thread inside System Process, or Normal Thread with a bug */
185 if (StartRoutine)
186 {
187 /* Reference the Process by Pointer */
188 ObReferenceObject(TargetProcess);
189 Process = TargetProcess;
190 Status = STATUS_SUCCESS;
191 }
192 else
193 {
194 /* Fake ObReference returning this */
195 Status = STATUS_INVALID_HANDLE;
196 }
197 }
198
199 /* Check for success */
200 if (!NT_SUCCESS(Status)) return Status;
201
202 /* Also make sure that User-Mode isn't trying to create a system thread */
203 if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess))
204 {
205 ObDereferenceObject(Process);
206 return STATUS_INVALID_HANDLE;
207 }
208
209 /* Create Thread Object */
210 Status = ObCreateObject(PreviousMode,
211 PsThreadType,
212 ObjectAttributes,
213 PreviousMode,
214 NULL,
215 sizeof(ETHREAD),
216 0,
217 0,
218 (PVOID*)&Thread);
219 if (!NT_SUCCESS(Status))
220 {
221 /* We failed; dereference the process and exit */
222 ObDereferenceObject(Process);
223 return Status;
224 }
225
226 /* Zero the Object entirely */
227 RtlZeroMemory(Thread, sizeof(ETHREAD));
228
229 /* Initialize rundown protection */
230 ExInitializeRundownProtection(&Thread->RundownProtect);
231
232 /* Set the Process CID */
233 Thread->ThreadsProcess = Process;
234 Thread->Cid.UniqueProcess = Process->UniqueProcessId;
235
236 /* Create Cid Handle */
237 CidEntry.Object = Thread;
238 CidEntry.GrantedAccess = 0;
239 Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
240 if (!Thread->Cid.UniqueThread)
241 {
242 /* We couldn't create the CID, dereference the thread and fail */
243 ObDereferenceObject(Thread);
244 return STATUS_INSUFFICIENT_RESOURCES;
245 }
246
247 /* Save the read cluster size */
248 Thread->ReadClusterSize = MmReadClusterSize;
249
250 /* Initialize the LPC Reply Semaphore */
251 KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, 1);
252
253 /* Initialize the list heads and locks */
254 InitializeListHead(&Thread->LpcReplyChain);
255 InitializeListHead(&Thread->IrpList);
256 InitializeListHead(&Thread->PostBlockList);
257 InitializeListHead(&Thread->ActiveTimerListHead);
258 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
259
260 /* Acquire rundown protection */
261 ExAcquireRundownProtection(&Process->RundownProtect);
262
263 /* Allocate Stack for non-GUI Thread */
264 KernelStack = (ULONG_PTR)MmCreateKernelStack(FALSE) + KERNEL_STACK_SIZE;
265
266 /* Now let the kernel initialize the context */
267 if (ThreadContext)
268 {
269 /* User-mode Thread, create Teb */
270 TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb);
271 if (!TebBase)
272 {
273 /* Failed to create the TEB. Release rundown and dereference */
274 ExReleaseRundownProtection(&Process->RundownProtect);
275 ObDereferenceObject(Thread);
276 return STATUS_INSUFFICIENT_RESOURCES;
277 }
278
279 /* Set the Start Addresses */
280 Thread->StartAddress = (PVOID)ThreadContext->Eip;
281 Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
282
283 /* Let the kernel intialize the Thread */
284 KeInitializeThread(&Process->Pcb,
285 &Thread->Tcb,
286 PspUserThreadStartup,
287 NULL,
288 NULL,
289 ThreadContext,
290 TebBase,
291 (PVOID)KernelStack);
292 }
293 else
294 {
295 /* System Thread */
296 Thread->StartAddress = StartRoutine;
297 InterlockedOr((PLONG)&Thread->CrossThreadFlags, CT_SYSTEM_THREAD_BIT);
298
299 /* Let the kernel intialize the Thread */
300 KeInitializeThread(&Process->Pcb,
301 &Thread->Tcb,
302 PspSystemThreadStartup,
303 StartRoutine,
304 StartContext,
305 NULL,
306 NULL,
307 (PVOID)KernelStack);
308 }
309
310 /*
311 * Insert the Thread into the Process's Thread List
312 * Note, this is the ETHREAD Thread List. It is removed in
313 * ps/kill.c!PspExitThread.
314 */
315 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
316 Process->ActiveThreads++;
317
318 /* Release rundown */
319 ExReleaseRundownProtection(&Process->RundownProtect);
320
321 /* Notify WMI */
322 //WmiTraceProcess(Process, TRUE);
323 //WmiTraceThread(Thread, InitialTeb, TRUE);
324
325 /* Notify Thread Creation */
326 PspRunCreateThreadNotifyRoutines(Thread, TRUE);
327
328 /* Suspend the Thread if we have to */
329 if (CreateSuspended)
330 {
331 KeSuspendThread(&Thread->Tcb);
332 }
333
334 /* Check if we were already terminated */
335 if (Thread->Terminated)
336 {
337 /* Force us to wake up to terminate */
338 KeForceResumeThread(&Thread->Tcb);
339 }
340
341 /* Reference ourselves as a keep-alive */
342 ObReferenceObject(Thread);
343
344 /* Insert the Thread into the Object Manager */
345 Status = ObInsertObject((PVOID)Thread,
346 NULL,
347 DesiredAccess,
348 0,
349 NULL,
350 &hThread);
351 if(NT_SUCCESS(Status))
352 {
353 /* Wrap in SEH to protect against bad user-mode pointers */
354 _SEH_TRY
355 {
356 /* Return Cid and Handle */
357 if(ClientId) *ClientId = Thread->Cid;
358 *ThreadHandle = hThread;
359 }
360 _SEH_HANDLE
361 {
362 Status = _SEH_GetExceptionCode();
363 }
364 _SEH_END;
365 }
366
367 /* FIXME: SECURITY */
368
369 /* Dispatch thread */
370 OldIrql = KeAcquireDispatcherDatabaseLock ();
371 KiReadyThread(&Thread->Tcb);
372 KeReleaseDispatcherDatabaseLock(OldIrql);
373
374 /* Return */
375 return Status;
376 }
377
378 /*
379 * @implemented
380 */
381 NTSTATUS
382 NTAPI
383 PsCreateSystemThread(PHANDLE ThreadHandle,
384 ACCESS_MASK DesiredAccess,
385 POBJECT_ATTRIBUTES ObjectAttributes,
386 HANDLE ProcessHandle,
387 PCLIENT_ID ClientId,
388 PKSTART_ROUTINE StartRoutine,
389 PVOID StartContext)
390 {
391 PEPROCESS TargetProcess = NULL;
392 HANDLE Handle = ProcessHandle;
393 PAGED_CODE();
394
395 /* Check if we have a handle. If not, use the System Process */
396 if (!ProcessHandle)
397 {
398 Handle = NULL;
399 TargetProcess = PsInitialSystemProcess;
400 }
401
402 /* Call the shared function */
403 return PspCreateThread(ThreadHandle,
404 DesiredAccess,
405 ObjectAttributes,
406 Handle,
407 TargetProcess,
408 ClientId,
409 NULL,
410 NULL,
411 FALSE,
412 StartRoutine,
413 StartContext);
414 }
415
416 /*
417 * @implemented
418 */
419 NTSTATUS
420 NTAPI
421 PsLookupThreadByThreadId(IN HANDLE ThreadId,
422 OUT PETHREAD *Thread)
423 {
424 PHANDLE_TABLE_ENTRY CidEntry;
425 PETHREAD FoundThread;
426 NTSTATUS Status = STATUS_INVALID_PARAMETER;
427 PAGED_CODE();
428 KeEnterCriticalRegion();
429
430 /* Get the CID Handle Entry */
431 if ((CidEntry = ExMapHandleToPointer(PspCidTable,
432 ThreadId)))
433 {
434 /* Get the Process */
435 FoundThread = CidEntry->Object;
436
437 /* Make sure it's really a process */
438 if (FoundThread->Tcb.DispatcherHeader.Type == ThreadObject)
439 {
440 /* Reference and return it */
441 ObReferenceObject(FoundThread);
442 *Thread = FoundThread;
443 Status = STATUS_SUCCESS;
444 }
445
446 /* Unlock the Entry */
447 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
448 }
449
450 /* Return to caller */
451 KeLeaveCriticalRegion();
452 return Status;
453 }
454
455 /*
456 * @implemented
457 */
458 HANDLE
459 NTAPI
460 PsGetCurrentThreadId(VOID)
461 {
462 return(PsGetCurrentThread()->Cid.UniqueThread);
463 }
464
465 /*
466 * @implemented
467 */
468 ULONG
469 NTAPI
470 PsGetThreadFreezeCount(PETHREAD Thread)
471 {
472 return Thread->Tcb.FreezeCount;
473 }
474
475 /*
476 * @implemented
477 */
478 BOOLEAN
479 NTAPI
480 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
481 {
482 return Thread->HardErrorsAreDisabled;
483 }
484
485 /*
486 * @implemented
487 */
488 HANDLE
489 NTAPI
490 PsGetThreadId(PETHREAD Thread)
491 {
492 return Thread->Cid.UniqueThread;
493 }
494
495 /*
496 * @implemented
497 */
498 PEPROCESS
499 NTAPI
500 PsGetThreadProcess(PETHREAD Thread)
501 {
502 return Thread->ThreadsProcess;
503 }
504
505 /*
506 * @implemented
507 */
508 HANDLE
509 NTAPI
510 PsGetThreadProcessId(PETHREAD Thread)
511 {
512 return Thread->Cid.UniqueProcess;
513 }
514
515 /*
516 * @implemented
517 */
518 HANDLE
519 NTAPI
520 PsGetThreadSessionId(PETHREAD Thread)
521 {
522 return (HANDLE)Thread->ThreadsProcess->Session;
523 }
524
525 /*
526 * @implemented
527 */
528 PTEB
529 NTAPI
530 PsGetThreadTeb(PETHREAD Thread)
531 {
532 return Thread->Tcb.Teb;
533 }
534
535 /*
536 * @implemented
537 */
538 PVOID
539 NTAPI
540 PsGetThreadWin32Thread(PETHREAD Thread)
541 {
542 return Thread->Tcb.Win32Thread;
543 }
544
545 /*
546 * @implemented
547 */
548 KPROCESSOR_MODE
549 NTAPI
550 PsGetCurrentThreadPreviousMode(VOID)
551 {
552 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
553 }
554
555 /*
556 * @implemented
557 */
558 PVOID
559 NTAPI
560 PsGetCurrentThreadStackBase(VOID)
561 {
562 return PsGetCurrentThread()->Tcb.StackBase;
563 }
564
565 /*
566 * @implemented
567 */
568 PVOID
569 NTAPI
570 PsGetCurrentThreadStackLimit(VOID)
571 {
572 return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
573 }
574
575 /*
576 * @implemented
577 */
578 BOOLEAN
579 NTAPI
580 PsIsThreadTerminating(IN PETHREAD Thread)
581 {
582 return (Thread->Terminated ? TRUE : FALSE);
583 }
584
585 /*
586 * @implemented
587 */
588 BOOLEAN
589 NTAPI
590 PsIsSystemThread(PETHREAD Thread)
591 {
592 return (Thread->SystemThread ? TRUE: FALSE);
593 }
594
595 /*
596 * @implemented
597 */
598 BOOLEAN
599 NTAPI
600 PsIsThreadImpersonating(PETHREAD Thread)
601 {
602 return Thread->ActiveImpersonationInfo;
603 }
604
605 /*
606 * @implemented
607 */
608 VOID
609 NTAPI
610 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
611 BOOLEAN HardErrorsAreDisabled)
612 {
613 Thread->HardErrorsAreDisabled = HardErrorsAreDisabled;
614 }
615
616 /*
617 * @implemented
618 */
619 VOID
620 NTAPI
621 PsSetThreadWin32Thread(PETHREAD Thread,
622 PVOID Win32Thread)
623 {
624 Thread->Tcb.Win32Thread = Win32Thread;
625 }
626
627 NTSTATUS
628 NTAPI
629 NtCreateThread(OUT PHANDLE ThreadHandle,
630 IN ACCESS_MASK DesiredAccess,
631 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
632 IN HANDLE ProcessHandle,
633 OUT PCLIENT_ID ClientId,
634 IN PCONTEXT ThreadContext,
635 IN PINITIAL_TEB InitialTeb,
636 IN BOOLEAN CreateSuspended)
637 {
638 INITIAL_TEB SafeInitialTeb;
639 NTSTATUS Status = STATUS_SUCCESS;
640 PAGED_CODE();
641
642 /* Check if this was from user-mode */
643 if(KeGetPreviousMode() != KernelMode)
644 {
645 /* Make sure that we got a context */
646 if (!ThreadContext) return STATUS_INVALID_PARAMETER;
647
648 /* Protect checks */
649 _SEH_TRY
650 {
651 /* Make sure the handle pointer we got is valid */
652 ProbeForWriteHandle(ThreadHandle);
653
654 /* Check if the caller wants a client id */
655 if(ClientId)
656 {
657 /* Make sure we can write to it */
658 ProbeForWrite(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
659 }
660
661 /* Make sure that the entire context is readable */
662 ProbeForRead(ThreadContext, sizeof(CONTEXT), sizeof(ULONG));
663
664 /* Check the Initial TEB */
665 ProbeForRead(InitialTeb, sizeof(INITIAL_TEB), sizeof(ULONG));
666 SafeInitialTeb = *InitialTeb;
667 }
668 _SEH_HANDLE
669 {
670 Status = _SEH_GetExceptionCode();
671 }
672 _SEH_END;
673
674 /* Handle any failures in our SEH checks */
675 if (!NT_SUCCESS(Status)) return Status;
676 }
677 else
678 {
679 /* Use the Initial TEB as is */
680 SafeInitialTeb = *InitialTeb;
681 }
682
683 /* Call the shared function */
684 return PspCreateThread(ThreadHandle,
685 DesiredAccess,
686 ObjectAttributes,
687 ProcessHandle,
688 NULL,
689 ClientId,
690 ThreadContext,
691 &SafeInitialTeb,
692 CreateSuspended,
693 NULL,
694 NULL);
695 }
696
697 /*
698 * @implemented
699 */
700 NTSTATUS
701 NTAPI
702 NtOpenThread(OUT PHANDLE ThreadHandle,
703 IN ACCESS_MASK DesiredAccess,
704 IN POBJECT_ATTRIBUTES ObjectAttributes,
705 IN PCLIENT_ID ClientId OPTIONAL)
706 {
707 KPROCESSOR_MODE PreviousMode;
708 CLIENT_ID SafeClientId;
709 ULONG Attributes = 0;
710 HANDLE hThread = NULL;
711 NTSTATUS Status = STATUS_SUCCESS;
712 PETHREAD Thread;
713 BOOLEAN HasObjectName = FALSE;
714
715 PAGED_CODE();
716
717 PreviousMode = KeGetPreviousMode();
718
719 /* Probe the paraemeters */
720 if(PreviousMode != KernelMode)
721 {
722 _SEH_TRY
723 {
724 ProbeForWriteHandle(ThreadHandle);
725
726 if(ClientId != NULL)
727 {
728 ProbeForRead(ClientId,
729 sizeof(CLIENT_ID),
730 sizeof(ULONG));
731
732 SafeClientId = *ClientId;
733 ClientId = &SafeClientId;
734 }
735
736 /* just probe the object attributes structure, don't capture it
737 completely. This is done later if necessary */
738 ProbeForRead(ObjectAttributes,
739 sizeof(OBJECT_ATTRIBUTES),
740 sizeof(ULONG));
741 HasObjectName = (ObjectAttributes->ObjectName != NULL);
742 Attributes = ObjectAttributes->Attributes;
743 }
744 _SEH_HANDLE
745 {
746 Status = _SEH_GetExceptionCode();
747 }
748 _SEH_END;
749
750 if(!NT_SUCCESS(Status)) return Status;
751 }
752 else
753 {
754 HasObjectName = (ObjectAttributes->ObjectName != NULL);
755 Attributes = ObjectAttributes->Attributes;
756 }
757
758 if (HasObjectName && ClientId != NULL)
759 {
760 /* can't pass both, n object name and a client id */
761 return STATUS_INVALID_PARAMETER_MIX;
762 }
763
764 /* Open by name if one was given */
765 if (HasObjectName)
766 {
767 /* Open it */
768 Status = ObOpenObjectByName(ObjectAttributes,
769 PsThreadType,
770 PreviousMode,
771 NULL,
772 DesiredAccess,
773 NULL,
774 &hThread);
775 }
776 else if (ClientId)
777 {
778 /* Open by Thread ID */
779 if (ClientId->UniqueProcess)
780 {
781 /* Get the Process */
782 Status = PsLookupProcessThreadByCid(ClientId,
783 NULL,
784 &Thread);
785 }
786 else
787 {
788 /* Get the Process */
789 Status = PsLookupThreadByThreadId(ClientId->UniqueThread,
790 &Thread);
791 }
792
793 /* Fail if we didn't find anything */
794 if(!NT_SUCCESS(Status)) return Status;
795
796 /* Open the Thread Object */
797 Status = ObOpenObjectByPointer(Thread,
798 Attributes,
799 NULL,
800 DesiredAccess,
801 PsThreadType,
802 PreviousMode,
803 &hThread);
804
805 /* Dereference the thread */
806 ObDereferenceObject(Thread);
807 }
808 else
809 {
810 /* neither an object name nor a client id was passed */
811 return STATUS_INVALID_PARAMETER_MIX;
812 }
813
814 /* Check for success */
815 if (NT_SUCCESS(Status))
816 {
817 /* Protect against bad user-mode pointers */
818 _SEH_TRY
819 {
820 /* Write back the handle */
821 *ThreadHandle = hThread;
822 }
823 _SEH_HANDLE
824 {
825 Status = _SEH_GetExceptionCode();
826 }
827 _SEH_END;
828 }
829
830 /* Return status */
831 return Status;
832 }
833
834 NTSTATUS
835 NTAPI
836 NtYieldExecution(VOID)
837 {
838 KiDispatchThread(Ready);
839 return(STATUS_SUCCESS);
840 }
841
842 NTSTATUS
843 NTAPI
844 NtTestAlert(VOID)
845 {
846 /* Check and Alert Thread if needed */
847 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED : STATUS_SUCCESS;
848 }
849
850 /*
851 * @implemented
852 */
853 KPROCESSOR_MODE
854 NTAPI
855 ExGetPreviousMode (VOID)
856 {
857 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
858 }
859
860 /* EOF */