#include "thread_loop.h"

#include <saas/library/daemon_base/threads/rty_pool.h>
#include <library/cpp/balloc/optional/operators.h>

#include <util/system/thread.h>

NFusion::IThreadLoop::IThreadLoop(const TString& threadName)
    : ThreadName(threadName)
    , StopFlag(false)
{
    ResumeEvent.Signal();
}

NFusion::IThreadLoop::~IThreadLoop() {
    Stop();
}

IThreadFactory* NFusion::IThreadLoop::GetThreadPool() {
    return Singleton<TRTYPools>()->Get(TRTYPools::TLoadtype::Update);
}

void NFusion::IThreadLoop::Start() {
    if (!Thread) {
        OnBeforeStart();

        StopFlag = false;
        Thread = GetThreadPool()->Run(this);

        OnAfterStart();
    }
}

void NFusion::IThreadLoop::Stop() {
    if (Thread) {
        OnBeforeStop();

        StopFlag = true;
        ResumeEvent.Signal();
        WakeupEvent.Signal();

        Thread->Join();
        Thread.Destroy();

        OnAfterStop();
    }
}

void NFusion::IThreadLoop::Pause() {
    OnPause();
    ResumeEvent.Reset();
}

void NFusion::IThreadLoop::Resume() {
    OnResume();
    ResumeEvent.Signal();
}

void NFusion::IThreadLoop::DoExecute() {
    ThreadDisableBalloc();
    TThread::SetCurrentThreadName(ThreadName.c_str());

    while (!StopFlag) {
        ResumeEvent.WaitI();
        if (StopFlag) {
            // An extra check is needed because Stop() may happen while thread is sleeping on ResumeEvent.
            // Otherwise we'll do one extra DoIteration on Pause()+Stop() command sequence.
            break;
        }
        TTimeToSleep toSleep;
        try {
            toSleep = DoIteration();
        } catch (...) {
            toSleep = OnException();
        }
        if (toSleep > TTimeToSleep::Zero()) {
            if (toSleep == TTimeToSleep::Max()) {
                WakeupEvent.WaitI();
            } else {
                WakeupEvent.WaitT(toSleep); // interruptable sleep
            }
        }
    }
}
