#include "os/c-api/il2cpp-config-platforms.h"

#if IL2CPP_THREADS_PTHREAD && !IL2CPP_TINY_WITHOUT_DEBUGGER

#include "MutexImpl.h"
#include "PosixHelpers.h"
#include "os/Thread.h"

namespace il2cpp
{
namespace os
{
    MutexImpl::MutexImpl()
        : posix::PosixWaitObject(kMutex)
        , m_OwningThread(NULL)
        , m_RecursionCount(0)
    {
        // For a mutex, 1 means unlocked.
        m_Count = 1;
    }

    void MutexImpl::Lock(bool interruptible)
    {
        TryLock(posix::kNoTimeout, interruptible);
    }

    bool MutexImpl::TryLock(uint32_t milliseconds, bool interruptible)
    {
        Thread* currentThread = Thread::GetCurrentThread();
        if (m_OwningThread == currentThread)
        {
            IL2CPP_ASSERT(m_Count == 0);
            ++m_RecursionCount;
            return true;
        }

        if (Wait(milliseconds, interruptible) == kWaitStatusSuccess)
        {
            m_OwningThread = currentThread;
            m_RecursionCount = 1;
            return true;
        }

        return false;
    }

    void MutexImpl::Unlock()
    {
        IL2CPP_ASSERT(m_OwningThread == Thread::GetCurrentThread());

        // Undo one locking level.
        --m_RecursionCount;
        if (m_RecursionCount > 0)
        {
            // Still locked.
            return;
        }

        // Ok, we're releasing the mutex. Lock and signal. We don't absolutely
        // need the lock as we are already owning the mutex here but play it safe.
        posix::PosixAutoLock lock(&m_Mutex);

        IL2CPP_ASSERT(m_Count == 0);
        m_Count = 1; // Unintuitive but 1 means unlocked.
        m_OwningThread = NULL;

        // Signal condition so that either a thread that's already waiting or a thread that
        // comes around later and waits can claim the mutex.
        if (HaveWaitingThreads())
            pthread_cond_signal(&m_Condition);
    }
}
}

#endif
