[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ps / thread.c
index 42d3a10..d05fc85 100644 (file)
@@ -11,7 +11,7 @@
 
 #include <ntoskrnl.h>
 #define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
 
 /* GLOBALS ******************************************************************/
 
@@ -29,7 +29,10 @@ PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,
     PETHREAD Thread;
     PTEB Teb;
     BOOLEAN DeadThread = FALSE;
+    KIRQL OldIrql;
     PAGED_CODE();
+    PSTRACE(PS_THREAD_DEBUG,
+            "StartRoutine: %p StartContext: %p\n", StartRoutine, StartContext);
 
     /* Go to Passive Level */
     KeLowerIrql(PASSIVE_LEVEL);
@@ -49,11 +52,11 @@ PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,
         Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
     }
 
-    /* Check if this is a system thread, or if we're hiding */
-    if (!(Thread->SystemThread) && !(Thread->HideFromDebugger))
+    /* Check if this is a dead thread, or if we're hiding */
+    if (!(Thread->DeadThread) && !(Thread->HideFromDebugger))
     {
         /* We're not, so notify the debugger */
-        DbgkCreateThread(StartContext);
+        DbgkCreateThread(Thread, StartContext);
     }
 
     /* Make sure we're not already dead */
@@ -66,13 +69,11 @@ PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,
         }
 
         /* Raise to APC */
-        KfRaiseIrql(APC_LEVEL);
+        KeRaiseIrql(APC_LEVEL, &OldIrql);
 
         /* Queue the User APC */
-        KiInitializeUserApc(NULL,
-                            (PVOID)((ULONG_PTR)Thread->Tcb.InitialStack -
-                            sizeof(KTRAP_FRAME) -
-                            sizeof(FX_SAVE_AREA)),
+        KiInitializeUserApc(KeGetExceptionFrame(&Thread->Tcb),
+                            KeGetTrapFrame(&Thread->Tcb),
                             PspSystemDllEntryPoint,
                             NULL,
                             PspSystemDllBase,
@@ -92,33 +93,75 @@ PspUserThreadStartup(IN PKSTART_ROUTINE StartRoutine,
     /* Do we have a cookie set yet? */
     if (!SharedUserData->Cookie)
     {
-        /*
-         * FIXME: Generate cookie
-         * Formula (roughly): Per-CPU Page Fault ^ Per-CPU Interrupt Time ^
-         *                    Global System Time ^ Stack Address of where
-         *                    the LARGE_INTEGER containing the Global System
-         *                    Time is.
-         */
+        LARGE_INTEGER SystemTime;
+        ULONG NewCookie;
+        PKPRCB Prcb;
+
+        /* Generate a new cookie */
+        KeQuerySystemTime(&SystemTime);
+        Prcb = KeGetCurrentPrcb();
+        NewCookie = Prcb->MmPageFaultCount ^ Prcb->InterruptTime ^
+                    SystemTime.u.LowPart ^ SystemTime.u.HighPart ^
+                    (ULONG_PTR)&SystemTime;
+
+        /* Set the new cookie*/
+        InterlockedCompareExchange((LONG*)&SharedUserData->Cookie,
+                                   NewCookie,
+                                   0);
     }
 }
 
+LONG
+PspUnhandledExceptionInSystemThread(PEXCEPTION_POINTERS ExceptionPointers)
+{
+    /* Print debugging information */
+    DPRINT1("PS: Unhandled Kernel Mode Exception Pointers = 0x%p\n",
+            ExceptionPointers);
+    DPRINT1("Code %x Addr %p Info0 %p Info1 %p Info2 %p Info3 %p\n",
+            ExceptionPointers->ExceptionRecord->ExceptionCode,
+            ExceptionPointers->ExceptionRecord->ExceptionAddress,
+            ExceptionPointers->ExceptionRecord->ExceptionInformation[0],
+            ExceptionPointers->ExceptionRecord->ExceptionInformation[1],
+            ExceptionPointers->ExceptionRecord->ExceptionInformation[2],
+            ExceptionPointers->ExceptionRecord->ExceptionInformation[3]);
+
+    /* Bugcheck the system */
+    KeBugCheckEx(SYSTEM_THREAD_EXCEPTION_NOT_HANDLED,
+                 ExceptionPointers->ExceptionRecord->ExceptionCode,
+                 (ULONG_PTR)ExceptionPointers->ExceptionRecord->ExceptionAddress,
+                 (ULONG_PTR)ExceptionPointers->ExceptionRecord,
+                 (ULONG_PTR)ExceptionPointers->ContextRecord);
+    return 0;
+}
+
 VOID
 NTAPI
 PspSystemThreadStartup(IN PKSTART_ROUTINE StartRoutine,
                        IN PVOID StartContext)
 {
     PETHREAD Thread;
+    PSTRACE(PS_THREAD_DEBUG,
+            "StartRoutine: %p StartContext: %p\n", StartRoutine, StartContext);
 
     /* Unlock the dispatcher Database */
     KeLowerIrql(PASSIVE_LEVEL);
     Thread = PsGetCurrentThread();
 
     /* Make sure the thread isn't gone */
-    if (!(Thread->Terminated) && !(Thread->DeadThread))
+    _SEH2_TRY
     {
-        /* Call it the Start Routine */
-        StartRoutine(StartContext);
+        if (!(Thread->Terminated) && !(Thread->DeadThread))
+        {
+            /* Call the Start Routine */
+            StartRoutine(StartContext);
+        }
     }
+    _SEH2_EXCEPT(PspUnhandledExceptionInSystemThread(_SEH2_GetExceptionInformation()))
+    {
+        /* Bugcheck if we got here */
+        KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED);
+    }
+    _SEH2_END;
 
     /* Exit the thread */
     PspTerminateThreadByPointer(Thread, STATUS_SUCCESS, TRUE);
@@ -128,7 +171,7 @@ NTSTATUS
 NTAPI
 PspCreateThread(OUT PHANDLE ThreadHandle,
                 IN ACCESS_MASK DesiredAccess,
-                IN POBJECT_ATTRIBUTES ObjectAttributes  OPTIONAL,
+                IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
                 IN HANDLE ProcessHandle,
                 IN PEPROCESS TargetProcess,
                 OUT PCLIENT_ID ClientId,
@@ -142,11 +185,19 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     PEPROCESS Process;
     PETHREAD Thread;
     PTEB TebBase = NULL;
-    KIRQL OldIrql;
     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
-    NTSTATUS Status;
+    NTSTATUS Status, AccessStatus;
     HANDLE_TABLE_ENTRY CidEntry;
+    ACCESS_STATE LocalAccessState;
+    PACCESS_STATE AccessState = &LocalAccessState;
+    AUX_ACCESS_DATA AuxData;
+    BOOLEAN Result, SdAllocated;
+    PSECURITY_DESCRIPTOR SecurityDescriptor;
+    SECURITY_SUBJECT_CONTEXT SubjectContext;
     PAGED_CODE();
+    PSTRACE(PS_THREAD_DEBUG,
+            "ThreadContext: %p TargetProcess: %p ProcessHandle: %p\n",
+            ThreadContext, TargetProcess, ProcessHandle);
 
     /* If we were called from PsCreateSystemThread, then we're kernel mode */
     if (StartRoutine) PreviousMode = KernelMode;
@@ -161,6 +212,7 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
                                            PreviousMode,
                                            (PVOID*)&Process,
                                            NULL);
+        PSREFTRACE(Process);
     }
     else
     {
@@ -185,6 +237,7 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     /* Also make sure that User-Mode isn't trying to create a system thread */
     if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess))
     {
+        /* Fail */
         ObDereferenceObject(Process);
         return STATUS_INVALID_HANDLE;
     }
@@ -212,6 +265,9 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     /* Initialize rundown protection */
     ExInitializeRundownProtection(&Thread->RundownProtect);
 
+    /* Initialize exit code */
+    Thread->ExitStatus = STATUS_PENDING;
+
     /* Set the Process CID */
     Thread->ThreadsProcess = Process;
     Thread->Cid.UniqueProcess = Process->UniqueProcessId;
@@ -241,24 +297,29 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     KeInitializeSpinLock(&Thread->ActiveTimerListLock);
 
     /* Acquire rundown protection */
-    ExAcquireRundownProtection(&Process->RundownProtect);
+    if (!ExAcquireRundownProtection (&Process->RundownProtect))
+    {
+        /* Fail */
+        ObDereferenceObject(Thread);
+        return STATUS_PROCESS_IS_TERMINATING;
+    }
 
     /* Now let the kernel initialize the context */
     if (ThreadContext)
     {
         /* User-mode Thread, create Teb */
-        TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb);
-        if (!TebBase)
+        Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase);
+        if (!NT_SUCCESS(Status))
         {
             /* Failed to create the TEB. Release rundown and dereference */
             ExReleaseRundownProtection(&Process->RundownProtect);
             ObDereferenceObject(Thread);
-            return STATUS_INSUFFICIENT_RESOURCES;
+            return Status;
         }
 
         /* Set the Start Addresses */
-        Thread->StartAddress = (PVOID)ThreadContext->Eip;
-        Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
+        Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext);
+        Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext);
 
         /* Let the kernel intialize the Thread */
         Status = KeInitThread(&Thread->Tcb,
@@ -274,7 +335,7 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     {
         /* System Thread */
         Thread->StartAddress = StartRoutine;
-        InterlockedOr((PLONG)&Thread->CrossThreadFlags, CT_SYSTEM_THREAD_BIT);
+        PspSetCrossThreadFlag(Thread, CT_SYSTEM_THREAD_BIT);
 
         /* Let the kernel intialize the Thread */
         Status = KeInitThread(&Thread->Tcb,
@@ -299,7 +360,21 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
         return Status;
     }
 
-    /* FIXME: Acquire exclusive pushlock */
+    /* Lock the process */
+    KeEnterCriticalRegion();
+    ExAcquirePushLockExclusive(&Process->ProcessLock);
+
+    /* Make sure the proces didn't just die on us */
+    if (Process->ProcessDelete) goto Quickie;
+
+    /* Check if the thread was ours, terminated and it was user mode */
+    if ((Thread->Terminated) &&
+        (ThreadContext) &&
+        (Thread->ThreadsProcess == Process))
+    {
+        /* Cleanup, we don't want to start it up and context switch */
+        goto Quickie;
+    }
 
     /*
      * Insert the Thread into the Process's Thread List
@@ -312,7 +387,9 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     /* Start the thread */
     KeStartThread(&Thread->Tcb);
 
-    /* FIXME: Wake pushlock */
+    /* Release the process lock */
+    ExReleasePushLockExclusive(&Process->ProcessLock);
+    KeLeaveCriticalRegion();
 
     /* Release rundown */
     ExReleaseRundownProtection(&Process->RundownProtect);
@@ -333,43 +410,175 @@ PspCreateThread(OUT PHANDLE ThreadHandle,
     /* Check if we were already terminated */
     if (Thread->Terminated) KeForceResumeThread(&Thread->Tcb);
 
+    /* Create an access state */
+    Status = SeCreateAccessStateEx(NULL,
+                                   ThreadContext ?
+                                   PsGetCurrentProcess() : Process,
+                                   &LocalAccessState,
+                                   &AuxData,
+                                   DesiredAccess,
+                                   &PsThreadType->TypeInfo.GenericMapping);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Access state failed, thread is dead */
+        PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
+
+        /* If we were suspended, wake it up */
+        if (CreateSuspended) KeResumeThread(&Thread->Tcb);
+
+        /* Dispatch thread */
+        KeReadyThread(&Thread->Tcb);
+
+        /* Dereference completely to kill it */
+        ObDereferenceObjectEx(Thread, 2);
+        return Status;
+    }
+
     /* Insert the Thread into the Object Manager */
     Status = ObInsertObject(Thread,
-                            NULL,
+                            AccessState,
                             DesiredAccess,
                             0,
                             NULL,
                             &hThread);
+
+    /* Delete the access state if we had one */
+    if (AccessState) SeDeleteAccessState(AccessState);
+
+    /* Check for success */
     if (NT_SUCCESS(Status))
     {
         /* Wrap in SEH to protect against bad user-mode pointers */
-        _SEH_TRY
+        _SEH2_TRY
         {
             /* Return Cid and Handle */
             if (ClientId) *ClientId = Thread->Cid;
             *ThreadHandle = hThread;
         }
-        _SEH_HANDLE
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            /* Get the exception code */
-            Status = _SEH_GetExceptionCode();
+            /* Thread insertion failed, thread is dead */
+            PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
+
+            /* If we were suspended, wake it up */
+            if (CreateSuspended) KeResumeThread(&Thread->Tcb);
+
+            /* Dispatch thread */
+            KeReadyThread(&Thread->Tcb);
+
+            /* Dereference it, leaving only the keep-alive */
+            ObDereferenceObject(Thread);
+
+            /* Close its handle, killing it */
+            ObCloseHandle(ThreadHandle, PreviousMode);
+
+            /* Return the exception code */
+            _SEH2_YIELD(return _SEH2_GetExceptionCode());
         }
-        _SEH_END;
+        _SEH2_END;
+    }
+    else
+    {
+        /* Thread insertion failed, thread is dead */
+        PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
+
+        /* If we were suspended, wake it up */
+        if (CreateSuspended) KeResumeThread(&Thread->Tcb);
     }
 
-    /* Set the thread access mask */
-    Thread->GrantedAccess = THREAD_ALL_ACCESS;
+    /* Get the create time */
+    KeQuerySystemTime(&Thread->CreateTime);
+    ASSERT(!(Thread->CreateTime.HighPart & 0xF0000000));
+
+    /* Make sure the thread isn't dead */
+    if (!Thread->DeadThread)
+    {
+        /* Get the thread's SD */
+        Status = ObGetObjectSecurity(Thread,
+                                     &SecurityDescriptor,
+                                     &SdAllocated);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Thread insertion failed, thread is dead */
+            PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
+
+            /* If we were suspended, wake it up */
+            if (CreateSuspended) KeResumeThread(&Thread->Tcb);
+
+            /* Dispatch thread */
+            KeReadyThread(&Thread->Tcb);
+
+            /* Dereference it, leaving only the keep-alive */
+            ObDereferenceObject(Thread);
+
+            /* Close its handle, killing it */
+            ObCloseHandle(ThreadHandle, PreviousMode);
+            return Status;
+        }
+
+        /* Create the subject context */
+        SubjectContext.ProcessAuditId = Process;
+        SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
+        SubjectContext.ClientToken = NULL;
+
+        /* Do the access check */
+        Result = SeAccessCheck(SecurityDescriptor,
+                               &SubjectContext,
+                               FALSE,
+                               MAXIMUM_ALLOWED,
+                               0,
+                               NULL,
+                               &PsThreadType->TypeInfo.GenericMapping,
+                               PreviousMode,
+                               &Thread->GrantedAccess,
+                               &AccessStatus);
+
+        /* Dereference the token and let go the SD */
+        ObFastDereferenceObject(&Process->Token,
+                                SubjectContext.PrimaryToken);
+        ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
+
+        /* Remove access if it failed */
+        if (!Result) Process->GrantedAccess = 0;
+
+        /* Set least some minimum access */
+        Thread->GrantedAccess |= (THREAD_TERMINATE |
+                                  THREAD_SET_INFORMATION |
+                                  THREAD_QUERY_INFORMATION);
+    }
+    else
+    {
+        /* Set the thread access mask to maximum */
+        Thread->GrantedAccess = THREAD_ALL_ACCESS;
+    }
 
     /* Dispatch thread */
-    OldIrql = KeAcquireDispatcherDatabaseLock ();
-    KiReadyThread(&Thread->Tcb);
-    KeReleaseDispatcherDatabaseLock(OldIrql);
+    KeReadyThread(&Thread->Tcb);
 
     /* Dereference it, leaving only the keep-alive */
     ObDereferenceObject(Thread);
 
     /* Return */
     return Status;
+
+    /* Most annoying failure case ever, where we undo almost all manually */
+Quickie:
+    /* When we get here, the process is locked, unlock it */
+    ExReleasePushLockExclusive(&Process->ProcessLock);
+    KeLeaveCriticalRegion();
+
+    /* Uninitailize it */
+    KeUninitThread(&Thread->Tcb);
+
+    /* If we had a TEB, delete it */
+    if (TebBase) MmDeleteTeb(Process, TebBase);
+
+    /* Release rundown protection, which we also hold */
+    ExReleaseRundownProtection(&Process->RundownProtect);
+
+    /* Dereference the thread and return failure */
+    ObDereferenceObject(Thread);
+    return STATUS_PROCESS_IS_TERMINATING;
 }
 
 /* PUBLIC FUNCTIONS **********************************************************/
@@ -390,6 +599,9 @@ PsCreateSystemThread(OUT PHANDLE ThreadHandle,
     PEPROCESS TargetProcess = NULL;
     HANDLE Handle = ProcessHandle;
     PAGED_CODE();
+    PSTRACE(PS_THREAD_DEBUG,
+            "ProcessHandle: %p StartRoutine: %p StartContext: %p\n",
+            ProcessHandle, StartRoutine, StartContext);
 
     /* Check if we have a handle. If not, use the System Process */
     if (!ProcessHandle)
@@ -424,6 +636,7 @@ PsLookupThreadByThreadId(IN HANDLE ThreadId,
     PETHREAD FoundThread;
     NTSTATUS Status = STATUS_INVALID_PARAMETER;
     PAGED_CODE();
+    PSTRACE(PS_THREAD_DEBUG, "ThreadId: %p\n", ThreadId);
     KeEnterCriticalRegion();
 
     /* Get the CID Handle Entry */
@@ -480,7 +693,7 @@ BOOLEAN
 NTAPI
 PsGetThreadHardErrorsAreDisabled(IN PETHREAD Thread)
 {
-    return Thread->HardErrorsAreDisabled;
+    return Thread->HardErrorsAreDisabled ? TRUE : FALSE;
 }
 
 /*
@@ -600,7 +813,7 @@ BOOLEAN
 NTAPI
 PsIsThreadImpersonating(IN PETHREAD Thread)
 {
-    return Thread->ActiveImpersonationInfo;
+    return Thread->ActiveImpersonationInfo ? TRUE : FALSE;
 }
 
 /*
@@ -617,7 +830,7 @@ PsSetThreadHardErrorsAreDisabled(IN PETHREAD Thread,
 /*
  * @implemented
  */
-struct _W32THREAD*
+PVOID
 NTAPI
 PsGetCurrentThreadWin32Thread(VOID)
 {
@@ -647,23 +860,24 @@ NtCreateThread(OUT PHANDLE ThreadHandle,
                IN BOOLEAN CreateSuspended)
 {
     INITIAL_TEB SafeInitialTeb;
-    NTSTATUS Status = STATUS_SUCCESS;
     PAGED_CODE();
+    PSTRACE(PS_THREAD_DEBUG,
+            "ProcessHandle: %p Context: %p\n", ProcessHandle, ThreadContext);
 
     /* Check if this was from user-mode */
-    if(KeGetPreviousMode() != KernelMode)
+    if (KeGetPreviousMode() != KernelMode)
     {
         /* Make sure that we got a context */
         if (!ThreadContext) return STATUS_INVALID_PARAMETER;
 
         /* Protect checks */
-        _SEH_TRY
+        _SEH2_TRY
         {
             /* Make sure the handle pointer we got is valid */
             ProbeForWriteHandle(ThreadHandle);
 
             /* Check if the caller wants a client id */
-            if(ClientId)
+            if (ClientId)
             {
                 /* Make sure we can write to it */
                 ProbeForWrite(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
@@ -675,15 +889,13 @@ NtCreateThread(OUT PHANDLE ThreadHandle,
             /* Check the Initial TEB */
             ProbeForRead(InitialTeb, sizeof(INITIAL_TEB), sizeof(ULONG));
             SafeInitialTeb = *InitialTeb;
-            }
-        _SEH_HANDLE
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            Status = _SEH_GetExceptionCode();
+            /* Return the exception code */
+            _SEH2_YIELD(return _SEH2_GetExceptionCode());
         }
-        _SEH_END;
-
-        /* Handle any failures in our SEH checks */
-        if (!NT_SUCCESS(Status)) return Status;
+        _SEH2_END;
     }
     else
     {
@@ -719,18 +931,20 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
     CLIENT_ID SafeClientId;
     ULONG Attributes = 0;
     HANDLE hThread = NULL;
-    NTSTATUS Status = STATUS_SUCCESS;
+    NTSTATUS Status;
     PETHREAD Thread;
     BOOLEAN HasObjectName = FALSE;
     ACCESS_STATE AccessState;
-    AUX_DATA AuxData;
+    AUX_ACCESS_DATA AuxData;
     PAGED_CODE();
+    PSTRACE(PS_THREAD_DEBUG,
+            "ClientId: %p ObjectAttributes: %p\n", ClientId, ObjectAttributes);
 
     /* Check if we were called from user mode */
     if (PreviousMode != KernelMode)
     {
         /* Enter SEH for probing */
-        _SEH_TRY
+        _SEH2_TRY
         {
             /* Probe the thread handle */
             ProbeForWriteHandle(ThreadHandle);
@@ -754,13 +968,12 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
             HasObjectName = (ObjectAttributes->ObjectName != NULL);
             Attributes = ObjectAttributes->Attributes;
         }
-        _SEH_HANDLE
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
-            /* Get the exception code */
-            Status = _SEH_GetExceptionCode();
+            /* Return the exception code */
+            _SEH2_YIELD(return _SEH2_GetExceptionCode());
         }
-        _SEH_END;
-        if (!NT_SUCCESS(Status)) return Status;
+        _SEH2_END;
     }
     else
     {
@@ -859,32 +1072,21 @@ NtOpenThread(OUT PHANDLE ThreadHandle,
     if (NT_SUCCESS(Status))
     {
         /* Protect against bad user-mode pointers */
-        _SEH_TRY
+        _SEH2_TRY
         {
             /* Write back the handle */
             *ThreadHandle = hThread;
         }
-        _SEH_HANDLE
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
             /* Get the exception code */
-            Status = _SEH_GetExceptionCode();
+            Status = _SEH2_GetExceptionCode();
         }
-        _SEH_END;
+        _SEH2_END;
     }
 
     /* Return status */
     return Status;
 }
 
-/*
- * @implemented
- */
-NTSTATUS
-NTAPI
-NtYieldExecution(VOID)
-{
-    KiDispatchThread(Ready);
-    return STATUS_SUCCESS;
-}
-
 /* EOF */