/*++

Copyright (c) 2016 Minoca Corp. All Rights Reserved

Module Name:

    barrier.c

Abstract:

    This module implements barrier support functions for the POSIX thread
    library.

Author:

    Chris Stevens 1-Jul-2016

Environment:

    User Mode C Library

--*/

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

#include "pthreadp.h"

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

#define PTHREAD_BARRIER_SHARED        0x00000001
#define PTHREAD_BARRIER_FLAGS         0x00000001
#define PTHREAD_BARRIER_COUNTER_SHIFT 1
#define PTHREAD_BARRIER_COUNTER_MASK  (~PTHREAD_BARRIER_FLAGS)

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

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

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

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

PTHREAD_API
int
pthread_barrier_init (
    pthread_barrier_t *Barrier,
    const pthread_barrierattr_t *Attribute,
    unsigned Count
    )

/*++

Routine Description:

    This routine initializes the given POSIX thread barrier with the given
    attributes and thread count.

Arguments:

    Barrier - Supplies a pointer to the POSIX thread barrier to be initialized.

    Attribute - Supplies an optional pointer to the attribute to use for
        initializing the barrier.

    Count - Supplies the number of threads that must wait on the barrier for it
        to be satisfied.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER_ATTRIBUTE AttributeInternal;
    PPTHREAD_BARRIER BarrierInternal;
    pthread_mutexattr_t MutexAttribute;
    pthread_mutexattr_t *MutexAttributePointer;
    INT Result;

    ASSERT(sizeof(pthread_barrier_t) >= sizeof(PTHREAD_BARRIER));

    if (Count == 0) {
        return EINVAL;
    }

    BarrierInternal = (PPTHREAD_BARRIER)Barrier;
    MutexAttributePointer = NULL;
    if (Attribute == NULL) {
        BarrierInternal->State = 0;

    } else {
        AttributeInternal = (PPTHREAD_BARRIER_ATTRIBUTE)Attribute;
        BarrierInternal->State = AttributeInternal->Flags &
                                 PTHREAD_BARRIER_FLAGS;

        if ((BarrierInternal->State & PTHREAD_BARRIER_SHARED) != 0) {
            MutexAttributePointer = &MutexAttribute;
            pthread_mutexattr_init(MutexAttributePointer);
            pthread_mutexattr_setpshared(MutexAttributePointer,
                                         PTHREAD_PROCESS_SHARED);
        }
    }

    Result = pthread_mutex_init(&(BarrierInternal->Mutex),
                                MutexAttributePointer);

    if (Result != 0) {
        return Result;
    }

    BarrierInternal->WaitingThreadCount = 0;
    BarrierInternal->ThreadCount = Count;
    return 0;
}

PTHREAD_API
int
pthread_barrier_destroy (
    pthread_barrier_t *Barrier
    )

/*++

Routine Description:

    This routine destroys the given POSIX thread barrier.

Arguments:

    Barrier - Supplies a pointer to the POSIX thread barrier to destroy.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER BarrierInternal;

    BarrierInternal = (PPTHREAD_BARRIER)Barrier;
    BarrierInternal->State = MAX_ULONG;
    BarrierInternal->ThreadCount = 0;
    BarrierInternal->WaitingThreadCount = 0;
    pthread_mutex_destroy(&(BarrierInternal->Mutex));
    return 0;
}

PTHREAD_API
int
pthread_barrier_wait (
    pthread_barrier_t *Barrier
    )

/*++

Routine Description:

    This routine blocks untils the required number of threads have waited on
    the barrier. Upon success, an arbitrary thread will receive
    PTHREAD_BARRIER_SERIAL_THREAD as a return value; the rest will receive 0.
    This routine does not get interrupted by signals and will continue to block
    after a signal is handled.

Arguments:

    Barrier - Supplies a pointer to the POSIX thread barrier.

Return Value:

    0 or PTHREAD_BARRIER_SERIAL_THREAD on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER BarrierInternal;
    KSTATUS KernelStatus;
    ULONG OldState;
    ULONG Operation;
    INT Status;
    ULONG ThreadCount;

    BarrierInternal = (PPTHREAD_BARRIER)Barrier;

    //
    // Acquire the mutex and increment the waiting thread count. If this
    // thread's wait satisfies the barrier, then attempt to wake all of the
    // other waiting threads.
    //

    pthread_mutex_lock(&(BarrierInternal->Mutex));
    BarrierInternal->WaitingThreadCount += 1;
    if (BarrierInternal->WaitingThreadCount >= BarrierInternal->ThreadCount) {
        Operation = UserLockWake;
        if ((BarrierInternal->State & PTHREAD_BARRIER_SHARED) == 0) {
            Operation |= USER_LOCK_PRIVATE;
        }

        ThreadCount = MAX_ULONG;
        KernelStatus = OsUserLock(&(BarrierInternal->State),
                                  Operation,
                                  &ThreadCount,
                                  0);

        //
        // On success, this thread gets the unique serialization return value.
        // Also, reset the barrier to the initialization state and increment
        // the state, so that any threads about to wait on this now satisfied
        // barrier will fail in the kernel.
        //

        if (KSUCCESS(KernelStatus)) {
            Status = PTHREAD_BARRIER_SERIAL_THREAD;
            BarrierInternal->WaitingThreadCount = 0;
            RtlAtomicAdd32(&(BarrierInternal->State),
                           1 << PTHREAD_BARRIER_COUNTER_SHIFT);

        } else {
            Status = ClConvertKstatusToErrorNumber(KernelStatus);
        }

        pthread_mutex_unlock(&(BarrierInternal->Mutex));

    //
    // Otherwise, wait on the current state until the required number of
    // threads arrive.
    //

    } else {
        OldState = BarrierInternal->State;
        pthread_mutex_unlock(&(BarrierInternal->Mutex));
        Operation = UserLockWait;
        if ((OldState & PTHREAD_BARRIER_SHARED) == 0) {
            Operation |= USER_LOCK_PRIVATE;
        }

        //
        // If a signal interrupts the wait, the barrier should continue waiting
        // after the signal is handled.
        //

        do {
            KernelStatus = OsUserLock(&(BarrierInternal->State),
                                      Operation,
                                      &OldState,
                                      SYS_WAIT_TIME_INDEFINITE);

        } while (KernelStatus == STATUS_INTERRUPTED);

        //
        // The wait may have failed immediately if the barrier was satisfied
        // between this thread releasing the lock and executing the wait.
        // Convert this failure into success.
        //

        if (KSUCCESS(KernelStatus) ||
            (KernelStatus == STATUS_OPERATION_WOULD_BLOCK)) {

            Status = 0;

        } else {
            Status = ClConvertKstatusToErrorNumber(KernelStatus);
        }
    }

    return Status;
}

PTHREAD_API
int
pthread_barrierattr_init (
    pthread_barrierattr_t *Attribute
    )

/*++

Routine Description:

    This routine initializes a barrier attribute structure.

Arguments:

    Attribute - Supplies a pointer to the barrier attribute structure to
        initialize.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER_ATTRIBUTE AttributeInternal;

    AttributeInternal = (PPTHREAD_BARRIER_ATTRIBUTE)Attribute;
    AttributeInternal->Flags = 0;
    return 0;
}

PTHREAD_API
int
pthread_barrierattr_destroy (
    pthread_barrierattr_t *Attribute
    )

/*++

Routine Description:

    This routine destroys the given barrier attribute structure.

Arguments:

    Attribute - Supplies a pointer to the barrier attribute to destroy.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER_ATTRIBUTE AttributeInternal;

    AttributeInternal = (PPTHREAD_BARRIER_ATTRIBUTE)Attribute;
    AttributeInternal->Flags = MAX_ULONG;
    return 0;
}

PTHREAD_API
int
pthread_barrierattr_getpshared (
    const pthread_barrierattr_t *Attribute,
    int *Shared
    )

/*++

Routine Description:

    This routine determines the shared state in a barrier attribute.

Arguments:

    Attribute - Supplies a pointer to the barrier attribute structure.

    Shared - Supplies a pointer where the shared attribute will be returned,
        indicating whether the condition variable is visible across processes.
        See PTHREAD_PROCESS_* definitions.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER_ATTRIBUTE AttributeInternal;

    AttributeInternal = (PPTHREAD_BARRIER_ATTRIBUTE)Attribute;
    *Shared = PTHREAD_PROCESS_PRIVATE;
    if ((AttributeInternal->Flags & PTHREAD_BARRIER_SHARED) != 0) {
        *Shared = PTHREAD_PROCESS_SHARED;
    }

    return 0;
}

PTHREAD_API
int
pthread_barrierattr_setpshared (
    const pthread_barrierattr_t *Attribute,
    int Shared
    )

/*++

Routine Description:

    This routine sets the shared state in a barrier attribute.

Arguments:

    Attribute - Supplies a pointer to the barrier attribute structure.

    Shared - Supplies the value indicating whether this barrier should be
        visible across processes. See PTHREAD_PROCESS_* definitions.

Return Value:

    0 on success.

    Returns an error number on failure.

--*/

{

    PPTHREAD_BARRIER_ATTRIBUTE AttributeInternal;

    AttributeInternal = (PPTHREAD_BARRIER_ATTRIBUTE)Attribute;
    if (Shared == PTHREAD_PROCESS_PRIVATE) {
        AttributeInternal->Flags &= ~PTHREAD_BARRIER_SHARED;

    } else if (Shared == PTHREAD_PROCESS_SHARED) {
        AttributeInternal->Flags |= PTHREAD_BARRIER_SHARED;

    } else {
        return EINVAL;
    }

    return 0;
}

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

