/*
 * SPDX-FileCopyrightText: (c) 2020 Forschungszentrum Jülich GmbH <fz-juelich.de>
 * SPDX-FileCopyrightText: (c) 2020 Technische Universität Dresden <tu-dresden.de>
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#ifndef STATIC_INLINE
#error STATIC_INLINE needs to be defined
#endif /* STATIC_INLINE */

#include <stddef.h>

#include <UTILS_Error.h>

STATIC_INLINE void
UTILS_MutexLock( UTILS_Mutex* mutex )
{
    UTILS_BUG_ON( mutex == NULL, "Invalid mutex handle given." );

    /* test-and-test-and-set lock */
    while ( true )
    {
        while ( UTILS_Atomic_LoadN_bool( mutex, UTILS_ATOMIC_RELAXED ) == true )
        {
            UTILS_CPU_RELAX;
        }
        if ( UTILS_Atomic_TestAndSet( mutex, UTILS_ATOMIC_ACQUIRE ) != true )
        {
            break;
        }
        UTILS_CPU_RELAX;
    }
}

STATIC_INLINE bool
UTILS_MutexTrylock( UTILS_Mutex* mutex )
{
    UTILS_BUG_ON( mutex == NULL, "Invalid mutex handle given." );

    if ( UTILS_Atomic_LoadN_bool( mutex, UTILS_ATOMIC_RELAXED ) == true )
    {
        UTILS_CPU_RELAX;
        return false;
    }

    if ( UTILS_Atomic_TestAndSet( mutex, UTILS_ATOMIC_ACQUIRE ) == true )
    {
        UTILS_CPU_RELAX;
        return false;
    }

    return true;
}

STATIC_INLINE void
UTILS_MutexUnlock( UTILS_Mutex* mutex )
{
    UTILS_BUG_ON( mutex == NULL, "Invalid mutex handle given." );

#if HAVE( CPU_INSTRUCTION_SET_NECVE )
    /**
     * The NEC SX Architecture does not have direct hardware support for atomic memory
     * operations. The C compiler instead implements the GCC atomic builtins as
     * non-atomic operations wrapped in a low-level locking primitive.
     * __atomic_clear does not acquire this low-level lock, thus violating the atomicity
     * guarantee, leading to frequent deadlocks.
     * __atomic_exchange acquires the low-level lock, so we use it instead.
     */
    UTILS_Atomic_ExchangeN_bool( mutex, 0, UTILS_ATOMIC_RELEASE );
#else
    UTILS_Atomic_clear( mutex, UTILS_ATOMIC_RELEASE );
#endif
}

STATIC_INLINE void
UTILS_MutexWait( UTILS_Mutex* mutex, UTILS_Atomic_Memorder memorder )
{
    UTILS_BUG_ON( mutex == NULL, "Invalid mutex handle given." );

    while ( UTILS_Atomic_LoadN_bool( mutex, ( memorder ) ) == true )
    {
        UTILS_CPU_RELAX;
    }
}
