#pragma once
#include <inttypes.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef int (*spin_worker)();

/* Begin C API */
struct fastlock;
void fastlock_init(struct fastlock *lock, const char *name);
#ifdef __cplusplus
void fastlock_lock(struct fastlock *lock, spin_worker worker = nullptr);
#else
void fastlock_lock(struct fastlock *lock, spin_worker worker);
#endif
int fastlock_trylock(struct fastlock *lock, int fWeak);
void fastlock_unlock(struct fastlock *lock);
void fastlock_free(struct fastlock *lock);
int fastlock_unlock_recursive(struct fastlock *lock);
void fastlock_lock_recursive(struct fastlock *lock, int nesting);
void fastlock_auto_adjust_waits();

uint64_t fastlock_getlongwaitcount();   // this is a global value

/* End C API */
#ifdef __cplusplus
}
#endif

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
struct ticket
{
    union
    {
        struct
        {
            uint16_t m_active;
            uint16_t m_avail;
        };
        unsigned u;
    };
};
#pragma GCC diagnostic pop

struct fastlock
{
    volatile int m_pidOwner;
    volatile int m_depth;
    char szName[56];
    /* Volatile data on seperate cache line */
    volatile struct ticket m_ticket;
    unsigned futex;
    char padding[56];   // ensure ticket and futex are on their own independent cache line

#ifdef __cplusplus
    fastlock(const char *name)
    {
        fastlock_init(this, name);
    }

    inline void lock(spin_worker worker = nullptr)
    {
        fastlock_lock(this, worker);
    }

    inline bool try_lock(bool fWeak = false)
    {
        return !!fastlock_trylock(this, fWeak);
    }

    inline void unlock()
    {
        fastlock_unlock(this);
    }

    int unlock_recursive()
    {
        return fastlock_unlock_recursive(this);
    }

    void lock_recursive(int nesting)
    {
        fastlock_lock_recursive(this, nesting);
    }

    bool fOwnLock();   // true if this thread owns the lock, NOTE: not 100% reliable, use for debugging only
#endif
};

#ifdef __cplusplus
static_assert(offsetof(struct fastlock, m_ticket) == 64, "ensure padding is correct");
#endif

