#pragma once

#include <library/cpp/coroutine/engine/impl.h>
#include <library/cpp/deprecated/atomic/atomic.h>

#include <pthread.h>

namespace NSrvKernel {
    class TUniversalMutex : TNonCopyable {
        friend class TUniversalGuard;

    public:
        explicit TUniversalMutex() {}

        ~TUniversalMutex() {
            Y_VERIFY(!AtomicGet(Lock_));
        }

    private:
        TAtomic Lock_ = 0;

#ifdef UNIVERSAL_MUTEX_DEBUG
        TCont* LockerCont_ = nullptr;
        pthread_t LockerThread_ = 0;
#endif
    };

    class TUniversalGuard : TNonCopyable {
    public:
        explicit TUniversalGuard(TUniversalMutex& mutex): Mutex_(mutex) {}

        [[nodiscard]] bool Lock() noexcept {
            TCont* runningCont = RunningCont();
            TDuration sleepTime = TDuration::MicroSeconds(100);
            while (!AtomicCas(&Mutex_.Lock_, 1, 0)) {
                if (runningCont) {
                    if (runningCont->Cancelled()) {
                        return false;
                    }
                    runningCont->SleepT(sleepTime);
                } else {
                    Sleep(sleepTime);
                }
            }
#ifdef UNIVERSAL_MUTEX_DEBUG
            Mutex_.LockerCont_ = runningCont;
            Mutex_.LockerThread_ = pthread_self();
#endif
            return Locked_ = true;
        }

        ~TUniversalGuard() {
            if (Locked_) {
#ifdef UNIVERSAL_MUTEX_DEBUG
                Mutex_.LockerCont_ = nullptr;
                Mutex_.LockerThread_ = 0;
#endif
                AtomicSet(Mutex_.Lock_, 0);
            }
        }

    private:
        TUniversalMutex& Mutex_;
        bool Locked_ = false;
    };
}
