/*++

Copyright (c) 2015 Minoca Corp. All Rights Reserved

Module Name:

    pthread.c

Abstract:

    This module implements support for POSIX threads.

Author:

    Evan Green 29-Apr-2015

Environment:

    User Mode C Library

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "pthreadp.h"
#include <assert.h>
#include <limits.h>
#include <sys/mman.h>
#include <unistd.h>

//
// ---------------------------------------------------------------- Definitions
//

//
// ------------------------------------------------------ Data Type Definitions
//

/*++

Structure Description:

    This structure stores a thread-local destructor entry.

Members:

    ListEntry - Stores pointers to the next and previous elements in the
        destructor entry.

    DestructorRoutine - Stores a pointer to the routine to call.

    Argument - Stores a pointer to the argument to pass to the routine.

    SharedObjectHandle - Stores the shared object handle this destructor is
        associated with.

--*/

typedef struct _THREAD_DESTRUCTOR {
    LIST_ENTRY ListEntry;
    PTHREAD_ENTRY_ROUTINE DestructorRoutine;
    PVOID Argument;
    PVOID SharedObjectHandle;
} THREAD_DESTRUCTOR, *PTHREAD_DESTRUCTOR;

//
// ----------------------------------------------- Internal Function Prototypes
//

VOID
ClpThreadStart (
    PVOID Parameter
    );

VOID
ClpInitializeThreading (
    VOID
    );

void
ClpThreadSignalHandler (
    int Signal
    );

int
ClpAllocateThread (
    const pthread_attr_t *Attribute,
    void *(*StartRoutine)(void *),
    void *Argument,
    PPTHREAD *Thread
    );

void
ClpDestroyThread (
    PPTHREAD Thread
    );

VOID
ClpCallThreadDestructors (
    VOID
    );

PPTHREAD
ClpGetThreadFromId (
    pthread_t ThreadId
    );

//
// -------------------------------------------------------------------- Globals
//

//
// Store the global thread list.
//

LIST_ENTRY ClThreadList;
pthread_mutex_t ClThreadListMutex = PTHREAD_MUTEX_INITIALIZER;

//
// Store a thread-local pointer to the current thread.
//

__THREAD PPTHREAD ClCurrentThread;

//
// Store a thread-local list of destructor functions registered by the
// compiler.
//

__THREAD LIST_ENTRY ClThreadDestructors;

//
// ------------------------------------------------------------------ Functions
//

PTHREAD_API
pthread_t
pthread_self (
    void
    )

/*++

Routine Description:

    This routine returns the thread ID for the current thread.

Arguments:

    None.

Return Value:

    None.

--*/

{

    PPTHREAD Thread;

    Thread = ClCurrentThread;

    //
    // If there is no current thread, then this must be the main thread.
    // Lazily allocate a new thread structure for it.
    //

    if (Thread == NULL) {
        ClpInitializeThreading();
        Thread = ClCurrentThread;
    }

    return (pthread_t)Thread;
}

PTHREAD_API
int
pthread_create (
    pthread_t *CreatedThread,
    const pthread_attr_t *Attribute,
    void *(*StartRoutine)(void *),
    void *Argument
    )

/*++

Routine Description:

    This routine creates and starts a new thread. The signal mask of the new
    thread is inherited from the current thread. The set of pending signals in
    the new thread will be initially empty.

Arguments:

    CreatedThread - Supplies a pointer where the identifier of the new thread
        will be returned on success.

    Attribute - Supplies an optional pointer to the attributes of the thread.

    StartRoutine - Supplies a pointer to the routine to call on the new thread.

    Argument - Supplies the argument to pass to the start routine.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    sigset_t InternalSignals;
    KSTATUS KernelStatus;
    pthread_attr_t LocalAttributes;
    PPTHREAD NewThread;
    sigset_t OriginalMask;
    int Status;

    //
    // Initialize the default attributes if none were supplied.
    //

    if (Attribute == NULL) {
        pthread_attr_init(&LocalAttributes);
        Attribute = &LocalAttributes;
    }

    Status = ClpAllocateThread(Attribute, StartRoutine, Argument, &NewThread);
    if (Status != 0) {
        return Status;
    }

    //
    // Force the main thread to get with the program in case this is the first
    // thread created.
    //

    pthread_self();
    pthread_mutex_lock(&(NewThread->StartMutex));

    //
    // Block certain signals in the new thread while it sets itself up.
    //

    sigemptyset(&InternalSignals);
    sigaddset(&InternalSignals, SIGNAL_PTHREAD);
    pthread_sigmask(SIG_BLOCK, &InternalSignals, &OriginalMask);
    KernelStatus = OsCreateThread(NULL,
                                  0,
                                  ClpThreadStart,
                                  NewThread,
                                  NewThread->Attribute.StackBase,
                                  NewThread->Attribute.StackSize,
                                  NewThread->OsData,
                                  &(NewThread->ThreadId));

    pthread_sigmask(SIG_SETMASK, &OriginalMask, NULL);
    if (!KSUCCESS(KernelStatus)) {
        Status = ClConvertKstatusToErrorNumber(KernelStatus);
        ClpDestroyThread(NewThread);
        return Status;
    }

    if ((NewThread->Attribute.Flags & PTHREAD_FLAG_DETACHED) != 0) {
        NewThread->State = PthreadStateDetached;

    } else {
        NewThread->State = PthreadStateNotJoined;
    }

    //
    // Add the thread to the global list.
    //

    pthread_mutex_lock(&ClThreadListMutex);
    INSERT_BEFORE(&(NewThread->ListEntry), &ClThreadList);
    pthread_mutex_unlock(&ClThreadListMutex);

    //
    // Let the thread run.
    //

    pthread_mutex_unlock(&(NewThread->StartMutex));
    *CreatedThread = (pthread_t)NewThread;
    return 0;
}

PTHREAD_API
int
pthread_detach (
    pthread_t ThreadId
    )

/*++

Routine Description:

    This routine marks the given thread as detached, which means that when it
    exits, its resources are automatically released without needing another
    thread to call join on it. It is illegal to call join on a detached thread,
    as the thread ID may be already released and reused.

Arguments:

    ThreadId - Supplies the thread ID to detach.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    ULONG OldState;
    PPTHREAD Thread;

    Thread = ClpGetThreadFromId(ThreadId);
    if (Thread == NULL) {
        return ESRCH;
    }

    //
    // Try to detach the thread.
    //

    OldState = RtlAtomicCompareExchange32(&(Thread->State),
                                          PthreadStateDetached,
                                          PthreadStateNotJoined);

    //
    // If the compare exchange was won, then the thread was successfully
    // detached.
    //

    if (OldState == PthreadStateNotJoined) {
        return 0;
    }

    //
    // If the thread has already exited, call join to clean up the remaining
    // thread resources.
    //

    if (OldState == PthreadStateExited) {
        return pthread_join(ThreadId, NULL);
    }

    //
    // The thread is either all funky or has already been joined in which case
    // the user is on drugs.
    //

    assert((OldState == PthreadStateJoined) ||
           (OldState == PthreadStateDetached));

    return EINVAL;
}

PTHREAD_API
int
pthread_join (
    pthread_t ThreadId,
    void **ReturnValue
    )

/*++

Routine Description:

    This routine waits for the given thread to exit and collects its return
    value. Detached threads cannot be joined.

Arguments:

    ThreadId - Supplies the identifier of the thread to join with.

    ReturnValue - Supplies a pointer where the thread return value will be
        returned on success.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    ULONG NewState;
    ULONG OldState;
    THREAD_ID OsThreadId;
    PPTHREAD Thread;

    pthread_testcancel();

    //
    // Don't be ridiculous.
    //

    if (ThreadId == pthread_self()) {
        return EDEADLK;
    }

    Thread = ClpGetThreadFromId(ThreadId);
    if (Thread == NULL) {
        return ESRCH;
    }

    //
    // Try to change the state from not joined or exited to joined. This may
    // race with other calls to join (weird), detach, and the thread exiting.
    //

    OldState = PthreadStateNotJoined;
    while ((OldState == PthreadStateNotJoined) ||
           (OldState == PthreadStateExited)) {

        NewState = RtlAtomicCompareExchange32(&(Thread->State),
                                              PthreadStateJoined,
                                              OldState);

        if (OldState == NewState) {
            break;
        }

        OldState = NewState;
    }

    if ((OldState == PthreadStateDetached) ||
        (OldState == PthreadStateJoined)) {

        return EINVAL;
    }

    //
    // Wait for the thread to exit.
    //

    OsThreadId = Thread->ThreadId;
    while (Thread->ThreadId != 0) {
        OsUserLock(&(Thread->ThreadId),
                   UserLockWait,
                   (PULONG)&OsThreadId,
                   SYS_WAIT_TIME_INDEFINITE);
    }

    //
    // Get the return value if requested.
    //

    if (ReturnValue != NULL) {
        *ReturnValue = Thread->ReturnValue;
    }

    //
    // Remove and clean up the thread structure.
    //

    pthread_mutex_lock(&ClThreadListMutex);
    LIST_REMOVE(&(Thread->ListEntry));
    Thread->ListEntry.Next = NULL;
    pthread_mutex_unlock(&ClThreadListMutex);
    if (Thread->KeyData != NULL) {
        ClpDestroyThreadKeyData(Thread);
    }

    ClpDestroyThread(Thread);
    return 0;
}

PTHREAD_API
__NO_RETURN
void
pthread_exit (
    void *ReturnValue
    )

/*++

Routine Description:

    This routine exits the current thread. If this is a detached thread, then
    all thread resources are destroyed immediately. If this is a joinable
    thread, then some state is kept around until another thread calls join to
    collect the return value.

Arguments:

    ReturnValue - Supplies an optional return value to give to anyone that
        calls join.

Return Value:

    None. This routine does not return.

--*/

{

    __pthread_cleanup_t *CleanupItem;
    PVOID DestroyRegion;
    UINTN DestroyRegionSize;
    ULONG OldState;
    sigset_t SignalMask;
    PPTHREAD Thread;

    //
    // Call C++ thread_local destructors.
    //

    Thread = (PPTHREAD)pthread_self();
    ClpCallThreadDestructors();
    Thread->ReturnValue = ReturnValue;

    //
    // Call the cleanup handlers as well.
    //

    while (Thread->CleanupStack != NULL) {
        CleanupItem = Thread->CleanupStack;
        Thread->CleanupStack = CleanupItem->__cleanup_prev;
        CleanupItem->__cleanup_routine(CleanupItem->__cleanup_arg);
    }

    //
    // Clean up all thread local keys.
    //

    ClpDestroyThreadKeyData(Thread);
    DestroyRegion = NULL;
    DestroyRegionSize = 0;

    //
    // Mask out all signals, as this thread will not be handling anything else,
    // and then exit. This may touch errno so it must be done before the thread
    // is torn down.
    //

    sigfillset(&SignalMask);
    pthread_sigmask(SIG_SETMASK, &SignalMask, NULL);

    //
    // Indicate that the thread has exited.
    //

    OldState = RtlAtomicCompareExchange32(&(Thread->State),
                                          PthreadStateExited,
                                          PthreadStateNotJoined);

    if (OldState == PthreadStateDetached) {

        //
        // No one will be joining this thread, it must clean itself up. The
        // kernel will help with the last deallocation since it is the stack.
        //

        pthread_mutex_lock(&ClThreadListMutex);
        LIST_REMOVE(&(Thread->ListEntry));
        pthread_mutex_unlock(&ClThreadListMutex);
        DestroyRegion = Thread->ThreadAllocation;
        DestroyRegionSize = Thread->ThreadAllocationSize;
        Thread->ThreadAllocationSize = 0;
        ClpDestroyThread(Thread);
    }

    OsExitThread(DestroyRegion, DestroyRegionSize);
}

PTHREAD_API
int
pthread_equal (
    pthread_t FirstThread,
    pthread_t SecondThread
    )

/*++

Routine Description:

    This routine compares two thread identifiers.

Arguments:

    FirstThread - Supplies the first thread identifier to compare.

    SecondThread - Supplies the second thread identifier to compare.

Return Value:

    Returns non-zero if the two thread IDs are equal.

    0 if the thread IDs are not equal.

--*/

{

    return FirstThread == SecondThread;
}

PTHREAD_API
int
pthread_kill (
    pthread_t ThreadId,
    int Signal
    )

/*++

Routine Description:

    This routine sends a signal to the given thread.

Arguments:

    ThreadId - Supplies the identifier of the thread to send the signal to.

    Signal - Supplies the signal number to send. Supply 0 to test if a signal
        can be sent, but not actually send any signal.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    THREAD_ID KernelThreadId;
    KSTATUS Status;
    PPTHREAD Thread;

    Thread = ClpGetThreadFromId(ThreadId);
    if (Thread == NULL) {
        return ESRCH;
    }

    KernelThreadId = Thread->ThreadId;
    if (KernelThreadId == 0) {
        return ESRCH;
    }

    Status = OsSendSignal(SignalTargetThread,
                          KernelThreadId,
                          Signal,
                          SIGNAL_CODE_USER,
                          0);

    if (!KSUCCESS(Status)) {
        return ClConvertKstatusToErrorNumber(Status);
    }

    return 0;
}

PTHREAD_API
int
pthread_sigqueue (
    pthread_t ThreadId,
    int Signal,
    const union sigval Value
    )

/*++

Routine Description:

    This routine queues a signal with data the given thread.

Arguments:

    ThreadId - Supplies the identifier of the thread to send the signal to.

    Signal - Supplies the signal number to send. Supply 0 to test if a signal
        can be sent, but not actually send any signal.

    Value - Supplies the signal value to send.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    KSTATUS Status;
    PPTHREAD Thread;

    ASSERT(sizeof(void *) >= sizeof(int));

    Thread = ClpGetThreadFromId(ThreadId);
    if (Thread == NULL) {
        return ESRCH;
    }

    Status = OsSendSignal(SignalTargetThread,
                          Thread->ThreadId,
                          Signal,
                          SIGNAL_CODE_QUEUE,
                          (UINTN)(Value.sival_ptr));

    if (!KSUCCESS(Status)) {
        return ClConvertKstatusToErrorNumber(Status);
    }

    return 0;
}

PTHREAD_API
int
pthread_cancel (
    pthread_t ThreadId
    )

/*++

Routine Description:

    This routine attempts to cancel (terminate) the thread with the given
    thread ID. This may not terminate the thread immediately if it has disabled
    or deferred cancellation.

Arguments:

    ThreadId - Supplies the identifier of the thread to cancel.

Return Value:

    0 on success.

    ESRCH if a thread with the given ID could not be found.

--*/

{

    PPTHREAD Thread;

    Thread = ClpGetThreadFromId(ThreadId);
    if (Thread == NULL) {
        return ESRCH;
    }

    Thread->CancelRequested = TRUE;
    pthread_kill(ThreadId, SIGNAL_PTHREAD);
    return 0;
}

PTHREAD_API
int
pthread_setcancelstate (
    int State,
    int *OldState
    )

/*++

Routine Description:

    This routine atomically sets the thread cancellation state for the current
    thread and returns the old state. By default, new threads are created with
    cancellation enabled.

Arguments:

    State - Supplies the new state to set. Valid values are
        PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DISABLE.

    OldState - Supplies the previously set cancellation state.

Return Value:

    0 on success.

    EINVAL if an invalid new state was supplied.

--*/

{

    ULONG PreviousState;
    PPTHREAD Thread;

    if ((State != PTHREAD_CANCEL_ENABLE) && (State != PTHREAD_CANCEL_DISABLE)) {
        return EINVAL;
    }

    Thread = (PPTHREAD)pthread_self();
    PreviousState = RtlAtomicExchange32(&(Thread->CancelState), State);
    if (OldState != NULL) {
        *OldState = PreviousState;
    }

    if (State == PTHREAD_CANCEL_ENABLE) {
        pthread_testcancel();
    }

    return 0;
}

PTHREAD_API
int
pthread_setcanceltype (
    int Type,
    int *OldType
    )

/*++

Routine Description:

    This routine atomically sets the thread cancellation type for the current
    thread and returns the old type. By default, new threads are created with
    deferred cancellation.

Arguments:

    Type - Supplies the new type to set. Valid values are
        PTHREAD_CANCEL_DEFERRED and PTHREAD_CANCEL_ASYNCHRONOUS.

    OldType - Supplies the previously set cancellation type.

Return Value:

    0 on success.

    EINVAL if an invalid new type was supplied.

--*/

{

    ULONG PreviousType;
    PPTHREAD Thread;

    if ((Type != PTHREAD_CANCEL_DEFERRED) &&
        (Type != PTHREAD_CANCEL_ASYNCHRONOUS)) {

        return EINVAL;
    }

    Thread = (PPTHREAD)pthread_self();
    PreviousType = RtlAtomicExchange32(&(Thread->CancelType), Type);
    if (OldType != NULL) {
        *OldType = PreviousType;
    }

    if (Type == PTHREAD_CANCEL_ASYNCHRONOUS) {
        pthread_testcancel();
    }

    return 0;
}

PTHREAD_API
void
pthread_testcancel (
    void
    )

/*++

Routine Description:

    This routine creates a cancellation point in the calling thread. If
    cancellation is currently disabled, this does nothing.

Arguments:

    None.

Return Value:

    None.

--*/

{

    PPTHREAD Thread;

    Thread = (PPTHREAD)pthread_self();
    if (Thread->CancelState != PTHREAD_CANCEL_ENABLE) {
        return;
    }

    if (Thread->CancelRequested != FALSE) {
        pthread_exit(PTHREAD_CANCELED);
    }

    return;
}

PTHREAD_API
pid_t
pthread_gettid_np (
    pthread_t ThreadId
    )

/*++

Routine Description:

    This routine returns the kernel thread ID for the given POSIX thread ID.

Arguments:

    ThreadId - Supplies the POSIX thread ID.

Return Value:

    Returns the kernel thread ID.

--*/

{

    PPTHREAD Thread;

    Thread = (PPTHREAD)ThreadId;
    return Thread->ThreadId;
}

PTHREAD_API
pid_t
pthread_getthreadid_np (
    void
    )

/*++

Routine Description:

    This routine returns the kernel thread ID for the current thread.

Arguments:

    None.

Return Value:

    Returns the kernel thread ID.

--*/

{

    return pthread_gettid_np(pthread_self());
}

PTHREAD_API
void
__pthread_cleanup_push (
    __pthread_cleanup_t *CleanupItem,
    __pthread_cleanup_func_t CleanupRoutine,
    void *Argument
    )

/*++

Routine Description:

    This routine pushes a new element onto the cleanup stack for the current
    thread.

Arguments:

    CleanupItem - Supplies a pointer to the cleanup item context. This routine
        uses this buffer, so it cannot be freed until the cleanup item is
        popped.

    CleanupRoutine - Supplies a pointer to the routine to call if the thread
        exits.

    Argument - Supplies a pointer to pass to the cleanup routine.

Return Value:

    None.

--*/

{

    PPTHREAD Thread;

    Thread = (PPTHREAD)pthread_self();
    CleanupItem->__cleanup_routine = CleanupRoutine;
    CleanupItem->__cleanup_arg = Argument;
    CleanupItem->__cleanup_prev = Thread->CleanupStack;
    Thread->CleanupStack = CleanupItem;
    return;
}

PTHREAD_API
void
__pthread_cleanup_pop (
    __pthread_cleanup_t *CleanupItem,
    int Execute
    )

/*++

Routine Description:

    This routine potentially pops an element from the cleanup stack.

Arguments:

    CleanupItem - Supplies a pointer to the cleanup item to pop.

    Execute - Supplies an integer that is non-zero if the cleanup routine
        should be run, or zero if it should just be popped.

Return Value:

    None.

--*/

{

    PPTHREAD Thread;

    Thread = (PPTHREAD)pthread_self();
    Thread->CleanupStack = CleanupItem->__cleanup_prev;
    if (Execute != 0) {
        CleanupItem->__cleanup_routine(CleanupItem->__cleanup_arg);
    }

    return;
}

LIBC_API
int
__cxa_thread_atexit_impl (
    PTHREAD_ENTRY_ROUTINE DestructorRoutine,
    void *Argument,
    void *DynamicObjectHandle
    )

/*++

Routine Description:

    This routine registers a new thread-local destructor, that is called when
    the thread is destroyed.

Arguments:

    DestructorRoutine - Supplies a pointer to the routine to call when the
        thread is destroyed.

    Argument - Supplies an argument to pass to the destructor routine.

    DynamicObjectHandle - Supplies a pointer to the dynamic object this
        destructor is associated with.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PTHREAD_DESTRUCTOR Destructor;

    Destructor = malloc(sizeof(THREAD_DESTRUCTOR));
    if (Destructor == NULL) {
        return ENOMEM;
    }

    Destructor->DestructorRoutine = DestructorRoutine;
    Destructor->Argument = Argument;
    Destructor->SharedObjectHandle = DynamicObjectHandle;
    if (ClThreadDestructors.Next == NULL) {
        INITIALIZE_LIST_HEAD(&ClThreadDestructors);
    }

    INSERT_AFTER(&(Destructor->ListEntry), &ClThreadDestructors);
    return 0;
}

//
// --------------------------------------------------------- Internal Functions
//

VOID
ClpThreadStart (
    PVOID Parameter
    )

/*++

Routine Description:

    This routine implements the initial routine for a POSIX thread.

Arguments:

    Parameter - Supplies a pointer to the parameter, which in this case is a
        pointer to the PTHREAD structure for this thread.

Return Value:

    None. Actually, this routine never returns.

--*/

{

    sigset_t InternalSignals;
    PVOID Result;
    PPTHREAD Thread;

    Thread = Parameter;

    //
    // Acquire the start mutex to synchronize with the thread that created this
    // thread.
    //

    ClCurrentThread = Thread;
    pthread_mutex_lock(&(Thread->StartMutex));
    sigemptyset(&InternalSignals);
    sigaddset(&InternalSignals, SIGNAL_PTHREAD);
    pthread_sigmask(SIG_UNBLOCK, &InternalSignals, NULL);
    pthread_testcancel();
    Result = Thread->ThreadRoutine(Thread->ThreadParameter);
    pthread_exit(Result);
    return;
}

VOID
ClpInitializeThreading (
    VOID
    )

/*++

Routine Description:

    This routine is called to initialize threading support, mostly performing
    some initialization tasks on the main thread that were deferred for
    better performance on non-threaded applications. This code must be called
    on the main thread.

Arguments:

    None.

Return Value:

    None.

--*/

{

    struct sigaction Action;
    UINTN AllocationSize;
    PPTHREAD Thread;

    AllocationSize = sizeof(PTHREAD) +
                     (PTHREAD_KEYS_MAX * sizeof(PTHREAD_KEY_DATA));

    Thread = malloc(AllocationSize);
    if (Thread == NULL) {
        return;
    }

    memset(Thread, 0, AllocationSize);
    pthread_mutex_init(&(Thread->StartMutex), NULL);
    Thread->KeyData = (PPTHREAD_KEY_DATA)(Thread + 1);

    //
    // Set the thread ID pointer of the main thread in the kernel so that
    // other threads can join the main thread if desired.
    //

    OsSetThreadIdPointer(&(Thread->ThreadId));
    Thread->State = PthreadStateNotJoined;
    ClCurrentThread = Thread;

    //
    // Add the thread to the global list.
    //

    pthread_mutex_lock(&ClThreadListMutex);

    ASSERT(ClThreadList.Next == NULL);

    INITIALIZE_LIST_HEAD(&ClThreadList);
    INSERT_AFTER(&(Thread->ListEntry), &ClThreadList);
    pthread_mutex_unlock(&ClThreadListMutex);

    //
    // Also register the thread signal handler and the set ID signal handler.
    //

    sigemptyset(&(Action.sa_mask));
    Action.sa_flags = 0;
    Action.sa_handler = ClpThreadSignalHandler;
    sigaction(SIGNAL_PTHREAD, &Action, NULL);
    Action.sa_handler = ClpSetIdSignalHandler;
    sigaction(SIGNAL_SETID, &Action, NULL);
    return;
}

void
ClpThreadSignalHandler (
    int Signal
    )

/*++

Routine Description:

    This routine implements the thread signal handler, which manages
    cancellation requests.

Arguments:

    Signal - Supplies the signal number that occurred.

Return Value:

    None.

--*/

{

    PPTHREAD Thread;

    Thread = (PPTHREAD)pthread_self();

    ASSERT(Thread->CancelRequested != FALSE);

    if (Thread->CancelType == PTHREAD_CANCEL_ASYNCHRONOUS) {
        pthread_testcancel();
    }

    return;
}

int
ClpAllocateThread (
    const pthread_attr_t *Attribute,
    void *(*StartRoutine)(void *),
    void *Argument,
    PPTHREAD *Thread
    )

/*++

Routine Description:

    This routine allocates and initializes a new thread structure.

Arguments:

    Attribute - Supplies a required pointer to the attributes of the thread.

    StartRoutine - Supplies a pointer to the routine to call on the new thread.

    Argument - Supplies the argument to pass to the start routine.

    Thread - Supplies a pointer where a pointer to the thread will be returned
        on success.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PVOID Allocation;
    PPTHREAD_ATTRIBUTE AttributeInternal;
    UINTN GuardSize;
    KSTATUS KernelStatus;
    ULONG MapFlags;
    UINTN MapSize;
    PPTHREAD NewThread;
    UINTN PageSize;
    PVOID Stack;
    UINTN StackSize;
    int Status;

    Allocation = NULL;

    //
    // If the caller wants something non-standard for a stack, then allocate
    // the stack.
    //

    GuardSize = 0;
    MapSize = 0;
    NewThread = NULL;
    PageSize = sysconf(_SC_PAGE_SIZE);
    AttributeInternal = (PPTHREAD_ATTRIBUTE)Attribute;
    Stack = AttributeInternal->StackBase;
    StackSize = 0;
    MapSize = sizeof(PTHREAD) + (PTHREAD_KEYS_MAX * sizeof(PTHREAD_KEY_DATA));

    //
    // Also allocate a stack if none was supplied but a weird guard size was
    // requested.
    //

    if ((Stack == NULL) && (AttributeInternal->GuardSize != PageSize)) {
        StackSize = ALIGN_RANGE_UP(AttributeInternal->StackSize, 16);
        MapSize += StackSize;
        MapSize = ALIGN_RANGE_UP(MapSize, PageSize);
        GuardSize = ALIGN_RANGE_UP(AttributeInternal->GuardSize, PageSize);
        MapSize += GuardSize;
    }

    MapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
    Allocation = mmap(Stack, MapSize, PROT_READ | PROT_WRITE, MapFlags, -1, 0);
    if (Allocation == MAP_FAILED) {
        Status = errno;
        goto AllocateThreadEnd;
    }

    if ((Stack == NULL) && (AttributeInternal->GuardSize != PageSize)) {
        if (GuardSize != 0) {
            if (mprotect(Stack, GuardSize, PROT_NONE) < 0) {
                Status = errno;
                goto AllocateThreadEnd;
            }
        }
    }

    NewThread = Allocation + GuardSize + StackSize;
    memcpy(&(NewThread->Attribute),
           AttributeInternal,
           sizeof(PTHREAD_ATTRIBUTE));

    NewThread->Attribute.StackBase = Stack;
    if (StackSize != 0) {
        NewThread->Attribute.StackSize = StackSize;
    }

    NewThread->ThreadRoutine = StartRoutine;
    NewThread->ThreadParameter = Argument;
    NewThread->ThreadAllocation = Allocation;
    NewThread->ThreadAllocationSize = MapSize;
    NewThread->KeyData = (PPTHREAD_KEY_DATA)(NewThread + 1);
    pthread_mutex_init(&(NewThread->StartMutex), NULL);
    KernelStatus = OsCreateThreadData(&(NewThread->OsData));
    if (!KSUCCESS(KernelStatus)) {
        Status = ClConvertKstatusToErrorNumber(KernelStatus);
        goto AllocateThreadEnd;
    }

    Status = 0;

AllocateThreadEnd:
    if (Status != 0) {
        if (Allocation != NULL) {
            munmap(Allocation, MapSize);
        }

        NewThread = NULL;
    }

    *Thread = NewThread;
    return Status;
}

void
ClpDestroyThread (
    PPTHREAD Thread
    )

/*++

Routine Description:

    This routine frees a thread structure. The thread better not be alive there.

Arguments:

    Thread - Supplies a pointer to the thread structure to destroy.

Return Value:

    None.

--*/

{

    assert(Thread->KeyData == NULL);

    pthread_mutex_destroy(&(Thread->StartMutex));
    if (Thread->OsData != NULL) {
        OsDestroyThreadData(Thread->OsData);
        Thread->OsData = NULL;
    }

    if (Thread->ThreadAllocationSize != 0) {
        munmap(Thread->ThreadAllocation, Thread->ThreadAllocationSize);
    }

    return;
}

VOID
ClpCallThreadDestructors (
    VOID
    )

/*++

Routine Description:

    This routine calls all the thread destructor routines in the reverse order
    as they were registered.

Arguments:

    None.

Return Value:

    None.

--*/

{

    PLIST_ENTRY CurrentEntry;
    PTHREAD_DESTRUCTOR Destructor;

    if (ClThreadDestructors.Next == NULL) {
        return;
    }

    CurrentEntry = ClThreadDestructors.Next;
    while (CurrentEntry != &ClThreadDestructors) {
        Destructor = LIST_VALUE(CurrentEntry, THREAD_DESTRUCTOR, ListEntry);
        CurrentEntry = CurrentEntry->Next;
        Destructor->DestructorRoutine(Destructor->Argument);
        free(Destructor);
    }

    return;
}

PPTHREAD
ClpGetThreadFromId (
    pthread_t ThreadId
    )

/*++

Routine Description:

    This routine looks up the given thread based on its thread ID.

Arguments:

    ThreadId - Supplies the thread ID to look up.

Return Value:

    Returns a pointer to the internal thread structure on success.

    NULL on failure.

--*/

{

    PLIST_ENTRY CurrentEntry;
    PPTHREAD CurrentThread;
    PPTHREAD FoundThread;

    if (ClThreadList.Next == NULL) {
        pthread_self();
    }

    FoundThread = NULL;
    pthread_mutex_lock(&ClThreadListMutex);
    CurrentEntry = ClThreadList.Next;
    while (CurrentEntry != &ClThreadList) {
        CurrentThread = LIST_VALUE(CurrentEntry, PTHREAD, ListEntry);
        if (CurrentThread == (PPTHREAD)ThreadId) {
            FoundThread = CurrentThread;
            break;
        }

        CurrentEntry = CurrentEntry->Next;
    }

    pthread_mutex_unlock(&ClThreadListMutex);
    return FoundThread;
}

