/**
 * Copyright (C) 2023-2026 Linaro Limited (or its affiliates). All rights reserved.
 * Copyright (C) 2012-2023 Arm Limited (or its affiliates).
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */

#include "coroutinemutex.h"
#include "coroutine.h"

// #define DEBUG_COROUTINEMUTEX
#ifdef DEBUG_COROUTINEMUTEX
#define coDebug(x) qDebug x
#else
#define coDebug(x) do { } while(false)
#endif

CoroutineMutex::CoroutineMutex() :
    mHolder(0)
{
}

CoroutineMutex::~CoroutineMutex()
{
    coDebug(("%p: CoroutineMutex::~CoroutineMutex", Coroutine::currentCoroutine()));
    wakeAll();
}

void CoroutineMutex::commonUnlock()
{
    Q_ASSERT(mHolder != 0);
    mHolder = 0;
    coDebug(("%p: CoroutineMutex::commonUnlock: holder is now null", Coroutine::currentCoroutine()));
    coDebug(("%p: CoroutineMutex::commonUnlock: waking one", Coroutine::currentCoroutine()));
    wakeOne();
    coDebug(("%p: CoroutineMutex::commonUnlock: waking one done", Coroutine::currentCoroutine()));
}

void CoroutineMutex::lock()
{
    Q_ASSERT(mHolder != Coroutine::currentCoroutine());
    coDebug(("%p: CoroutineMutex::lock", Coroutine::currentCoroutine()));
    if (mHolder != 0) {
        coDebug(("%p: CoroutineMutex::lock: lock is already held by %p", Coroutine::currentCoroutine(), mHolder));
        coDebug(("%p: CoroutineMutex::lock: waiting", Coroutine::currentCoroutine()));
        wait();
        Q_ASSERT(mHolder == 0);
        coDebug(("%p: CoroutineMutex::lock: lock is now free", Coroutine::currentCoroutine()));
    }
    mHolder = Coroutine::currentCoroutine();
}

void CoroutineMutex::unlock()
{
    Q_ASSERT(mHolder == Coroutine::currentCoroutine());
    commonUnlock();
}

Coroutine *CoroutineMutex::remoteUnlock()
{
    Q_ASSERT(mHolder != 0);
    Coroutine *holder = mHolder;
    coDebug(("%p: CoroutineMutex::remoteUnlock", Coroutine::currentCoroutine()));
    commonUnlock();
    return holder;
}

Coroutine *CoroutineMutex::holder() const
{
    return mHolder;
}
