/*++

Copyright (c) 1990-2001  Microsoft Corporation

Module Name:

    thread.c

Abstract:

    This module implements Win32 Thread Object APIs

--*/

#include "basedll.h"
#pragma hdrstop

//
// Number of bytes that need to be allocated for thread local storage.
//
ULONG XapiTlsSize;

//
// General critical section used to guard XAPI data structures.
//
INITIALIZED_CRITICAL_SECTION(XapiProcessLock);

//
// List of routines to be notified at thread creation and deletion.  Access is
// guarded by the XapiProcessLock.
//
INITIALIZED_LIST_ENTRY(XapiThreadNotifyRoutineList);

//
// Top level Win32 exception filter implemented by the title.
//
LPTOP_LEVEL_EXCEPTION_FILTER XapiCurrentTopLevelFilter;

VOID
XapiCallThreadNotifyRoutines(
    BOOL Create
    )

/*++

Routine Description:

    Calls the registered thread notification routines when the active thread is
    created or deleted.

Arguments:

    Create - TRUE if the active thread has been created, else FALSE if the
        active thread is to be deleted.

Return Value:

    None.

--*/

{
    PLIST_ENTRY NextListEntry;
    PXTHREAD_NOTIFICATION ThreadNotification;

    XapiAcquireProcessLock();

    NextListEntry = XapiThreadNotifyRoutineList.Flink;

    while (NextListEntry != &XapiThreadNotifyRoutineList) {

        ThreadNotification = CONTAINING_RECORD(NextListEntry,
            XTHREAD_NOTIFICATION, ListEntry);
        NextListEntry = ThreadNotification->ListEntry.Flink;

        ThreadNotification->pfnNotifyRoutine(Create);
    }

    XapiReleaseProcessLock();
}

LONG
WINAPI
UnhandledExceptionFilter(
    IN PEXCEPTION_POINTERS ExceptionPointers
    )

/*++

Routine Description:

    Exception filter for exceptions that don't get handled before the
    top-level handler. This filter always spews debug info and passes
    on the exception to the exception handler.

Arguments:

    ExceptionPointers - Exception information

Return Value:

    EXCEPTION_EXECUTE_HANDLER - passes on control to the handler

--*/

{
#if DBG
    DbgPrint("Unhandled XAPI Exception. Exception Pointers = 0x%p\n", ExceptionPointers);
    DbgPrint("Code %x Addr %p\nInfo0 %p Info1 %p Info2 %p Info3 %p\n",
        ExceptionPointers->ExceptionRecord->ExceptionCode,
        (ULONG_PTR)ExceptionPointers->ExceptionRecord->ExceptionAddress,
        ExceptionPointers->ExceptionRecord->ExceptionInformation[0],
        ExceptionPointers->ExceptionRecord->ExceptionInformation[1],
        ExceptionPointers->ExceptionRecord->ExceptionInformation[2],
        ExceptionPointers->ExceptionRecord->ExceptionInformation[3]
        );
#endif // DBG

    if ( XapiCurrentTopLevelFilter )
    {
        LONG FilterReturn = (XapiCurrentTopLevelFilter)(ExceptionPointers);
        if ( FilterReturn == EXCEPTION_CONTINUE_EXECUTION )
        {
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

VOID
XapiThreadStartup(
    IN PKSTART_ROUTINE StartRoutine,
    IN PVOID StartContext
    )

/*++

Routine Description:

    Called by the kernel on thread startup. We set up the default exception handler
    and a data structure to keep track of Xapi data.

Arguments:

    StartRoutine - address the thread should be started at.

    StartContext - an LPVOID pointer passed in to CreateThread to pass on to the thread.

Return Value:

    None.

--*/

{
    DWORD dwExitCode;

    ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);

    try
    {
        /* Need to set up the TLS data */
        PULONG TlsData;
        DWORD RawDataSize;

        ASSERT(KeGetCurrentThread()->TlsData);

#if DBG
        /* First make sure the TLS data is where we think it should be */
        ASSERT((ULONG_PTR)KeGetCurrentThread()->TlsData -
            (ULONG_PTR)KeGetCurrentTlsDataTop() == *((PULONG)_tls_used.AddressOfIndex) * 4);
#endif

        /* Need to fill the first slot with a pointer to the remainder */
        TlsData = (PULONG)KeGetCurrentThread()->TlsData + 1;
        TlsData[-1] = (ULONG_PTR)TlsData;

        /* Ensure 16-byte alignment */
        ASSERT(((ULONG_PTR)TlsData & 15) == 0);

        RawDataSize = _tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData;

        RtlCopyMemory(TlsData, (PVOID)_tls_used.StartAddressOfRawData,
            RawDataSize);

        if (_tls_used.SizeOfZeroFill != 0) {
            RtlZeroMemory((PBYTE)TlsData + RawDataSize, _tls_used.SizeOfZeroFill);
        }

        XapiCallThreadNotifyRoutines(TRUE);

        dwExitCode = (*(LPTHREAD_START_ROUTINE)StartRoutine)(StartContext);

        XapiCallThreadNotifyRoutines(FALSE);
    }
    except (UnhandledExceptionFilter(GetExceptionInformation()))
    {
        //
        // UnhandledExceptionFilter will return either EXCEPTION_CONTINUE_SEARCH,
        // in which case, the exception search will stop since we're the top of
        // the exception stack, or it will return EXCEPTION_CONTINUE_EXECUTION.
        // We'll never execute this handler.
        //

        ASSERT(FALSE);
    }

    PsTerminateSystemThread(dwExitCode);
}

HANDLE
APIENTRY
CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    DWORD dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    )

/*++

Routine Description:

    A thread object can be created to execute within the address space of the
    calling process using CreateThread.

    Creating a thread causes a new thread of execution to begin in the address
    space of the current process. The thread has access to all objects opened
    by the process.

    The thread begins executing at the address specified by the StartAddress
    parameter. If the thread returns from this procedure, the results are
    un-specified.

    The thread remains in the system until it has terminated and
    all handles to the thread
    have been closed through a call to CloseHandle.

    When a thread terminates, it attains a state of signaled satisfying all
    waits on the object.

    In addition to the STANDARD_RIGHTS_REQUIRED access flags, the following
    object type specific access flags are valid for thread objects:

        - THREAD_QUERY_INFORMATION - This access is required to read
          certain information from the thread object.

        - SYNCHRONIZE - This access is required to wait on a thread
          object.

        - THREAD_GET_CONTEXT - This access is required to read the
          context of a thread using GetThreadContext.

        - THREAD_SET_CONTEXT - This access is required to write the
          context of a thread using SetThreadContext.

        - THREAD_SUSPEND_RESUME - This access is required to suspend or
          resume a thread using SuspendThread or ResumeThread.

        - THREAD_ALL_ACCESS - This set of access flags specifies all of
          the possible access flags for a thread object.

Arguments:

    hProcess - Supplies the handle to the process in which the thread is
        to be create in.

    lpThreadAttributes - An optional parameter that may be used to specify
        the attributes of the new thread.  If the parameter is not
        specified, then the thread is created without a security
        descriptor, and the resulting handle is not inherited on process
        creation.

    dwStackSize - Supplies the size in bytes of the stack for the new thread.
        A value of zero specifies that the thread's stack size should be
        the same size as the stack size of the first thread in the process.
        This size is specified in the application's executable file.

    lpStartAddress - Supplies the starting address of the new thread.  The
        address is logically a procedure that never returns and that
        accepts a single 32-bit pointer argument.

    lpParameter - Supplies a single parameter value passed to the thread.

    dwCreationFlags - Supplies additional flags that control the creation
        of the thread.

        dwCreationFlags Flags:

        CREATE_SUSPENDED - The thread is created in a suspended state.
            The creator can resume this thread using ResumeThread.
            Until this is done, the thread will not begin execution.

    lpThreadId - Returns the thread identifier of the thread.  The
        thread ID is valid until the thread terminates.

Return Value:

    NON-NULL - Returns a handle to the new thread.  The handle has full
        access to the new thread and may be used in any API that
        requires a handle to a thread object.

    NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    HANDLE Handle;

    if (dwStackSize == 0) {
        dwStackSize = XeImageHeader()->SizeOfStackCommit;
    }

    Status = PsCreateSystemThreadEx(
                &Handle,
                0,
                dwStackSize,
                XapiTlsSize,
                (PHANDLE)lpThreadId,
                (PKSTART_ROUTINE)lpStartAddress,
                lpParameter,
                (BOOLEAN)((dwCreationFlags & CREATE_SUSPENDED) ? TRUE : FALSE),
                FALSE,
                (PKSYSTEM_ROUTINE)XapiThreadStartup
                );

    if (!NT_SUCCESS(Status) && XapiIsXapiThread()) {
        XapiSetLastNTError(Status);
        return NULL;
    }

    return Handle;
}

BOOL
APIENTRY
SetThreadPriority(
    HANDLE hThread,
    int nPriority
    )

/*++

Routine Description:

    The specified thread's priority can be set using SetThreadPriority.

    A thread's priority may be set using SetThreadPriority.  This call
    allows the thread's relative execution importance to be communicated
    to the system.  The system normally schedules threads according to
    their priority.  The system is free to temporarily boost the
    priority of a thread when signifigant events occur (e.g.  keyboard
    or mouse input...).  Similarly, as a thread runs without blocking,
    the system will decay its priority.  The system will never decay the
    priority below the value set by this call.

    In the absence of system originated priority boosts, threads will be
    scheduled in a round-robin fashion at each priority level from
    THREAD_PRIORITY_TIME_CRITICAL to THREAD_PRIORITY_IDLE.  Only when there
    are no runnable threads at a higher level, will scheduling of
    threads at a lower level take place.

    All threads initially start at THREAD_PRIORITY_NORMAL.

    If for some reason the thread needs more priority, it can be
    switched to THREAD_PRIORITY_ABOVE_NORMAL or THREAD_PRIORITY_HIGHEST.
    Switching to THREAD_PRIORITY_TIME_CRITICAL should only be done in extreme
    situations.  Since these threads are given the highes priority, they
    should only run in short bursts.  Running for long durations will
    soak up the systems processing bandwidth starving threads at lower
    levels.

    If a thread needs to do low priority work, or should only run there
    is nothing else to do, its priority should be set to
    THREAD_PRIORITY_BELOW_NORMAL or THREAD_PRIORITY_LOWEST.  For extreme
    cases, THREAD_PRIORITY_IDLE can be used.

    Care must be taken when manipulating priorites.  If priorities are
    used carelessly (every thread is set to THREAD_PRIORITY_TIME_CRITICAL),
    the effects of priority modifications can produce undesireable
    effects (e.g.  starvation, no effect...).

Arguments:

    hThread - Supplies a handle to the thread whose priority is to be
        set.  The handle must have been created with
        THREAD_SET_INFORMATION access.

    nPriority - Supplies the priority value for the thread.  The
        following five priority values (ordered from lowest priority to
        highest priority) are allowed.

        nPriority Values:

        THREAD_PRIORITY_IDLE - The thread's priority should be set to
            the lowest possible settable priority.

        THREAD_PRIORITY_LOWEST - The thread's priority should be set to
            the next lowest possible settable priority.

        THREAD_PRIORITY_BELOW_NORMAL - The thread's priority should be
            set to just below normal.

        THREAD_PRIORITY_NORMAL - The thread's priority should be set to
            the normal priority value.  This is the value that all
            threads begin execution at.

        THREAD_PRIORITY_ABOVE_NORMAL - The thread's priority should be
            set to just above normal priority.

        THREAD_PRIORITY_HIGHEST - The thread's priority should be set to
            the next highest possible settable priority.

        THREAD_PRIORITY_TIME_CRITICAL - The thread's priority should be set
            to the highest possible settable priority.  This priority is
            very likely to interfere with normal operation of the
            system.

Return Value:

    TRUE - The operation was successful

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.
--*/

{
    NTSTATUS Status;
    LONG BasePriority;
    PETHREAD Thread;

    Status = ObReferenceObjectByHandle(hThread,
                                       PsThreadObjectType,
                                       (PVOID *)&Thread);

    if (NT_SUCCESS(Status)) {

        BasePriority = (LONG)nPriority;
        if (BasePriority == THREAD_PRIORITY_TIME_CRITICAL) {
            BasePriority = ((HIGH_PRIORITY + 1) / 2);
        } else if (BasePriority == THREAD_PRIORITY_IDLE) {
            BasePriority = -((HIGH_PRIORITY + 1) / 2);
        }

        KeSetBasePriorityThread(&Thread->Tcb, BasePriority);
        ObDereferenceObject(Thread);
        return TRUE;
    } else {
        XapiSetLastNTError(Status);
        return FALSE;
    }
}

int
APIENTRY
GetThreadPriority(
    HANDLE hThread
    )

/*++

Routine Description:

    The specified thread's priority can be read using GetThreadPriority.

Arguments:

    hThread - Supplies a handle to the thread whose priority is to be
        set.  The handle must have been created with
        THREAD_QUERY_INFORMATION access.

Return Value:

    The value of the thread's current priority is returned.  If an error
    occured, the value THREAD_PRIORITY_ERROR_RETURN is returned.
    Extended error status is available using GetLastError.

--*/

{
    NTSTATUS Status;
    PETHREAD Thread;
    int returnvalue;

    Status = ObReferenceObjectByHandle(hThread,
                                       PsThreadObjectType,
                                       (PVOID *)&Thread);

    if (NT_SUCCESS(Status)) {

        returnvalue = (int)KeQueryBasePriorityThread(&Thread->Tcb);
        if (returnvalue == ((HIGH_PRIORITY + 1) / 2)) {
            returnvalue = THREAD_PRIORITY_TIME_CRITICAL;
        } else if (returnvalue == -((HIGH_PRIORITY + 1) / 2)) {
            returnvalue = THREAD_PRIORITY_IDLE;
        }

        ObDereferenceObject(Thread);
        return returnvalue;
    } else {
        XapiSetLastNTError(Status);
        return (int)THREAD_PRIORITY_ERROR_RETURN;
    }
}

BOOL
WINAPI
SetThreadPriorityBoost(
    HANDLE hThread,
    BOOL bDisablePriorityBoost
    )
{
    NTSTATUS Status;
    PETHREAD Thread;

    Status = ObReferenceObjectByHandle(hThread,
                                       PsThreadObjectType,
                                       (PVOID *)&Thread);

    if (NT_SUCCESS(Status)) {
        KeSetDisableBoostThread(&Thread->Tcb, bDisablePriorityBoost ? TRUE : FALSE);
        ObDereferenceObject(Thread);
        return TRUE;
    } else {
        XapiSetLastNTError(Status);
        return FALSE;
    }
}

BOOL
WINAPI
GetThreadPriorityBoost(
    HANDLE hThread,
    PBOOL pDisablePriorityBoost
    )
{
    NTSTATUS Status;
    PETHREAD Thread;

    Status = ObReferenceObjectByHandle(hThread,
                                       PsThreadObjectType,
                                       (PVOID *)&Thread);

    if (NT_SUCCESS(Status)) {
        *pDisablePriorityBoost = Thread->Tcb.DisableBoost ? TRUE : FALSE;
        ObDereferenceObject(Thread);
        return TRUE;
    } else {
        XapiSetLastNTError(Status);
        return FALSE;
    }
}

DWORD
APIENTRY
GetCurrentThreadId(
    VOID
    )

/*++

Routine Description:

    The thread ID of the current thread may be retrieved using
    GetCurrentThreadId.

Arguments:

    None.

Return Value:

    Returns a unique value representing the thread ID of the currently
    executing thread.  The return value may be used to identify a thread
    in the system.

--*/

{
    return HandleToUlong(PsGetCurrentThreadId());
}

DWORD
APIENTRY
SuspendThread(
    HANDLE hThread
    )

/*++

Routine Description:

    A thread can be suspended using SuspendThread.

    Suspending a thread causes the thread to stop executing user-mode
    (or application) code.  Each thread has a suspend count (with a
    maximum value of MAXIMUM_SUSPEND_COUNT).  If the suspend count is
    greater than zero, the thread is suspended; otherwise, the thread is
    not suspended and is eligible for execution.

    Calling SuspendThread causes the target thread's suspend count to
    increment.  Attempting to increment past the maximum suspend count
    causes an error without incrementing the count.

Arguments:

    hThread - Supplies a handle to the thread that is to be suspended.
        The handle must have been created with THREAD_SUSPEND_RESUME
        access to the thread.

Return Value:

    -1 - The operation failed.  Extended error status is available using
         GetLastError.

    Other - The target thread was suspended. The return value is the thread's
        previous suspend count.

--*/

{
    NTSTATUS Status;
    DWORD PreviousSuspendCount;

    Status = NtSuspendThread(hThread,&PreviousSuspendCount);

    if ( !NT_SUCCESS(Status) ) {
        XapiSetLastNTError(Status);
        return (DWORD)-1;
        }
    else {
        return PreviousSuspendCount;
        }
}

DWORD
APIENTRY
ResumeThread(
    IN HANDLE hThread
    )

/*++

Routine Description:

    A thread can be resumed using ResumeThread.

    Resuming a thread object checks the suspend count of the subject
    thread.  If the suspend count is zero, then the thread is not
    currently suspended and no operation is performed.  Otherwise, the
    subject thread's suspend count is decremented.  If the resultant
    value is zero , then the execution of the subject thread is resumed.

    The previous suspend count is returned as the function value.  If
    the return value is zero, then the subject thread was not previously
    suspended.  If the return value is one, then the subject thread's
    the subject thread is still suspended and must be resumed the number
    of times specified by the return value minus one before it will
    actually resume execution.

    Note that while reporting debug events, all threads withing the
    reporting process are frozen.  This has nothing to do with
    SuspendThread or ResumeThread.  Debuggers are expected to use
    SuspendThread and ResumeThread to limit the set of threads that can
    execute within a process.  By suspending all threads in a process
    except for the one reporting a debug event, it is possible to
    "single step" a single thread.  The other threads will not be
    released by a continue if they are suspended.

Arguments:

    hThread - Supplies a handle to the thread that is to be resumed.
        The handle must have been created with THREAD_SUSPEND_RESUME
        access to the thread.

Return Value:

    -1 - The operation failed.  Extended error status is available using
        GetLastError.

    Other - The target thread was resumed (or was not previously
        suspended).  The return value is the thread's previous suspend
        count.

--*/

{
    NTSTATUS Status;
    DWORD PreviousSuspendCount;

    Status = NtResumeThread(hThread,&PreviousSuspendCount);

    if ( !NT_SUCCESS(Status) ) {
        XapiSetLastNTError(Status);
        return (DWORD)-1;
        }
    else {
        return PreviousSuspendCount;
        }
}

VOID
APIENTRY
RaiseException(
    DWORD dwExceptionCode,
    DWORD dwExceptionFlags,
    DWORD nNumberOfArguments,
    CONST ULONG_PTR *lpArguments
    )

/*++

Routine Description:

    Raising an exception causes the exception dispatcher to go through
    its search for an exception handler.  This includes debugger
    notification, frame based handler searching, and system default
    actions.

Arguments:

    dwExceptionCode - Supplies the exception code of the exception being
        raised.  This value may be obtained in exception filters and in
        exception handlers by calling GetExceptionCode.

    dwExceptionFlags - Supplies a set of flags associated with the exception.

    dwExceptionFlags Flags:

        EXCEPTION_NONCONTINUABLE - The exception is non-continuable.
            Returning EXCEPTION_CONTINUE_EXECUTION from an exception
            marked in this way causes the
            STATUS_NONCONTINUABLE_EXCEPTION exception.

    nNumberOfArguments - Supplies the number of arguments associated
        with the exception.  This value may not exceed
        EXCEPTION_MAXIMUM_PARAMETERS.  This parameter is ignored if
        lpArguments is NULL.

    lpArguments - An optional parameter, that if present supplies the
        arguments for the exception.

Return Value:

    None.

--*/

{
    EXCEPTION_RECORD ExceptionRecord;
    ULONG n;
    PULONG_PTR s,d;
    ExceptionRecord.ExceptionCode = (DWORD)dwExceptionCode;
    ExceptionRecord.ExceptionFlags = dwExceptionFlags & EXCEPTION_NONCONTINUABLE;
    ExceptionRecord.ExceptionRecord = NULL;
    ExceptionRecord.ExceptionAddress = (PVOID)RaiseException;
    if ( ARGUMENT_PRESENT(lpArguments) ) {
        n =  nNumberOfArguments;
        if ( n > EXCEPTION_MAXIMUM_PARAMETERS ) {
            n = EXCEPTION_MAXIMUM_PARAMETERS;
            }
        ExceptionRecord.NumberParameters = n;
        s = (PULONG_PTR)lpArguments;
        d = ExceptionRecord.ExceptionInformation;
        while(n--){
            *d++ = *s++;
            }
        }
    else {
        ExceptionRecord.NumberParameters = 0;
        }
    RtlRaiseException(&ExceptionRecord);
}

VOID
XapiDispatchAPC(
    LPVOID lpApcArgument1,
    LPVOID lpApcArgument2,
    LPVOID lpApcArgument3
    )
{
    PAPCFUNC pfnAPC;
    ULONG_PTR dwData;

    pfnAPC = (PAPCFUNC)lpApcArgument1;
    dwData = (ULONG_PTR)lpApcArgument2;
    (pfnAPC)(dwData);
}


WINBASEAPI
DWORD
WINAPI
QueueUserAPC(
    PAPCFUNC pfnAPC,
    HANDLE hThread,
    ULONG_PTR dwData
    )
/*++

Routine Description:

    This function is used to queue a user-mode APC to the specified thread. The APC
    will fire when the specified thread does an alertable wait.

Arguments:

    pfnAPC - Supplies the address of the APC routine to execute when the
        APC fires.

    hHandle - Supplies a handle to a thread object.  The caller
        must have THREAD_SET_CONTEXT access to the thread.

    dwData - Supplies a DWORD passed to the APC

Return Value:

    TRUE - The operations was successful

    FALSE - The operation failed. GetLastError() is not defined.

--*/

{
    NTSTATUS Status;

    Status = NtQueueApcThread(
                hThread,
                (PPS_APC_ROUTINE)XapiDispatchAPC,
                (PVOID)pfnAPC,
                (PVOID)dwData,
                NULL
                );

    if ( !NT_SUCCESS(Status) ) {
        return 0;
        }
    return 1;
}

BOOL
WINAPI
SwitchToThread(
    VOID
    )

/*++

Routine Description:

    This function causes a yield from the running thread to any other
    thread that is ready and can run on the current processor.  The
    yield will be effective for up to one quantum and then the yielding
    thread will be scheduled again according to its priority and
    whatever other threads may also be avaliable to run.  The thread
    that yields will not bounce to another processor even it another
    processor is idle or running a lower priority thread.

Arguments:

    None

Return Value:

    TRUE - Calling this function caused a switch to another thread to occur
    FALSE - There were no other ready threads, so no context switch occured

--*/

{

    if ( NtYieldExecution() == STATUS_NO_YIELD_PERFORMED ) {
        return FALSE;
        }
    else {
        return TRUE;
        }
}

VOID
APIENTRY
ExitThread(
    DWORD dwExitCode
    )

/*++

Routine Description:

    The current thread can exit using ExitThread.

    ExitThread is the prefered method of exiting a thread.  When this
    API is called (either explicitly or by returning from a thread
    procedure), The current thread's stack is deallocated and the thread
    terminates.  If the thread is the last thread in the process when
    this API is called, the behavior of this API does not change.  DLLs
    are not notified as a result of a call to ExitThread.

Arguments:

    dwExitCode - Supplies the termination status for the thread.

Return Value:

    None.

--*/

{
    XapiCallThreadNotifyRoutines(FALSE);

    PsTerminateSystemThread(dwExitCode);
}

BOOL
APIENTRY
GetExitCodeThread(
    HANDLE hThread,
    LPDWORD lpExitCode
    )

/*++

Routine Description:

    The termination status of a thread can be read using
    GetExitCodeThread.

    If a Thread is in the signaled state, calling this function returns
    the termination status of the thread.  If the thread is not yet
    signaled, the termination status returned is STILL_ACTIVE.

Arguments:

    hThread - Supplies a handle to the thread whose termination status is
        to be read.  The handle must have been created with
        THREAD_QUERY_INFORMATION access.

    lpExitCode - Returns the current termination status of the
        thread.

Return Value:

    TRUE - The operation was successful

    FALSE/NULL - The operation failed. Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    PETHREAD Thread;

    Status = ObReferenceObjectByHandle(hThread,
                                       PsThreadObjectType,
                                       (PVOID *)&Thread);

    if (NT_SUCCESS(Status)) {
        *lpExitCode = KeReadStateThread(&Thread->Tcb) ? Thread->ExitStatus : STATUS_PENDING;
        ObDereferenceObject(Thread);
        return TRUE;
    } else {
        XapiSetLastNTError(Status);
        return FALSE;
    }
}

LPTOP_LEVEL_EXCEPTION_FILTER
WINAPI
SetUnhandledExceptionFilter(
    LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
    )

/*++

Routine Description:

    This function allows an application to supersede the top level
    exception handler that Win32 places at the top of each thread and
    process.

    If an exception occurs, and it makes it to the Win32 unhandled
    exception filter, and the process is not being debugged, the Win32
    filter will call the unhandled exception filter specified by
    lpTopLevelExceptionFilter.

    This filter may return:

        EXCEPTION_EXECUTE_HANDLER - Return from the Win32
            UnhandledExceptionFilter and execute the associated
            exception handler.  This will usually result in process
            termination

        EXCEPTION_CONTINUE_EXECUTION - Return from the Win32
            UnhandledExceptionFilter and continue execution from the
            point of the exception.  The filter is of course free to
            modify the continuation state my modifying the passed
            exception information.

        EXCEPTION_CONTINUE_SEARCH - Proceed with normal execution of the
            Win32 UnhandledExceptionFilter.  e.g.  obey the SetErrorMode
            flags, or invoke the Application Error popup.

    This function is not a general vectored exception handling
    mechanism.  It is intended to be used to establish a per-process
    exception filter that can monitor unhandled exceptions at the
    process level and respond to these exceptions appropriately.

Arguments:

    lpTopLevelExceptionFilter - Supplies the address of a top level
        filter function that will be called whenever the Win32
        UnhandledExceptionFilter gets control, and the process is NOT
        being debugged.  A value of NULL specifies default handling
        within the Win32 UnhandledExceptionFilter.

Return Value:

    This function returns the address of the previous exception filter
    established with this API.  A value of NULL means that there is no
    current top level handler.

--*/

{
    LPTOP_LEVEL_EXCEPTION_FILTER PreviousTopLevelFilter;

    PreviousTopLevelFilter = XapiCurrentTopLevelFilter;
    XapiCurrentTopLevelFilter = lpTopLevelExceptionFilter;

    return PreviousTopLevelFilter;
}

BOOL
WINAPI
GetThreadTimes(
    HANDLE hThread,
    LPFILETIME lpCreationTime,
    LPFILETIME lpExitTime,
    LPFILETIME lpKernelTime,
    LPFILETIME lpUserTime
    )

/*++

Routine Description:

    This function is used to return various timing information about the
    thread specified by hThread.

    All times are in units of 100ns increments. For lpCreationTime and lpExitTime,
    the times are in terms of the SYSTEM time or GMT time.

Arguments:

    hThread - Supplies an open handle to the specified thread.  The
        handle must have been created with THREAD_QUERY_INFORMATION
        access.

    lpCreationTime - Returns a creation time of the thread.

    lpExitTime - Returns the exit time of a thread.  If the thread has
        not exited, this value is not defined.

    lpKernelTime - Returns the amount of time that this thread has
        executed in kernel-mode.

    lpUserTime - Returns the amount of time that this thread has
        executed in user-mode.


Return Value:

    TRUE - The API was successful

    FALSE - The operation failed.  Extended error status is available
        using GetLastError.

--*/

{
    NTSTATUS Status;
    PETHREAD Thread;

    Status = ObReferenceObjectByHandle(hThread,
                                       PsThreadObjectType,
                                       (PVOID *)&Thread);

    if (NT_SUCCESS(Status)) {

        *((PLARGE_INTEGER)lpCreationTime) = Thread->CreateTime;

        if (KeReadStateThread(&Thread->Tcb)) {
            *((PLARGE_INTEGER)lpExitTime) = Thread->ExitTime;
        } else {
            ((PLARGE_INTEGER)lpExitTime)->QuadPart = 0;
        }

        ((PLARGE_INTEGER)lpKernelTime)->QuadPart = UInt32x32To64(Thread->Tcb.KernelTime,
            *KeTimeIncrement);
        ((PLARGE_INTEGER)lpUserTime)->QuadPart = 0;

        ObDereferenceObject(Thread);
        return TRUE;
    } else {
        XapiSetLastNTError(Status);
        return FALSE;
    }

    return TRUE;
}

XBOXAPI
VOID
WINAPI
XRegisterThreadNotifyRoutine(
    PXTHREAD_NOTIFICATION pThreadNotification,
    BOOL fRegister
    )

/*++

Routine Description:

    Register or deregister a notification routine
    which will be called when a thread is created or deleted.

Arguments:

    pThreadNotification - Points to a XTHREAD_NOTIFICATION structure
        NOTE: The XTHREAD_NOTIFICATION structure must remain
        valid until the thread notification routine is deregistered.
        For example, you can use a global variable for this.
        But you should NOT use a local variable inside a function.

    fRegister - TRUE to register a new thread notification routine
        FALSE to deregister a previously registered notification routine

Return Value:

    None.

--*/

{
    XapiAcquireProcessLock();

    if (fRegister) {
        InsertTailList(&XapiThreadNotifyRoutineList,
            &pThreadNotification->ListEntry);
    } else {
        RemoveEntryList(&pThreadNotification->ListEntry);
    }

    XapiReleaseProcessLock();
}

XBOXAPI
VOID
XSetProcessQuantumLength(
    IN DWORD dwMilliseconds
    )

/*++

Routine Description:

    This routine changes the quantum length for the current process.  Changing
    the quantum length does not affect the quantum of the current thread.

    The quantum length determines the number of milliseconds that a thread is
    executed before the scheduler selects the next ready thread to execute.

Arguments:

    dwMilliseconds - Supplies the number of milliseconds for the process quantum
        length.

Return Value:

    None.

--*/

{
    LONG ThreadQuantum;

    if (dwMilliseconds == 0 || dwMilliseconds > 1000) {
        RIP("XSetProcessQuantumSize() invalid parameter (dwMilliseconds)");
    }

    ThreadQuantum = dwMilliseconds * CLOCK_QUANTUM_DECREMENT;
    KeGetCurrentThread()->ApcState.Process->ThreadQuantum = ThreadQuantum;
}

XBOXAPI
DWORD
XGetProcessQuantumLength(
    VOID
    )

/*++

Routine Description:

    This routine returns the quantum length for the current process.

    The quantum length determines the number of milliseconds that a thread is
    executed before the scheduler selects the next ready thread to execute.

Arguments:

    None.

Return Value:

    Returns the number of milliseconds for the process quantum length.

--*/

{
    LONG ThreadQuantum;

    ThreadQuantum = KeGetCurrentThread()->ApcState.Process->ThreadQuantum;

    return ThreadQuantum / CLOCK_QUANTUM_DECREMENT;
}
