#include "pch.h"
#include "PeriodicProc.h"


CPeriodicProc::CPeriodicProc()
	: m_pFDD(nullptr)
#if (PER_THREADED_MODEL)
	, m_bThreadTerminate(false)
	, m_bNotify(false)
#endif
{};


CPeriodicProc::~CPeriodicProc() {};


void CPeriodicProc::init()
{
	fFDD_Mod = 0.0;
	fFDDTicks = 0.0;
#if (PER_THREADED_MODEL)
	StartThread();
#endif
}

void CPeriodicProc::clear()
{
	fFDDTicks = 0.0;
}

void CPeriodicProc::uninit()
{
#if (PER_THREADED_MODEL)
	StopThread();
#endif
}

void CPeriodicProc::clock()
{
#if (PER_THREADED_MODEL)
	{
		std::lock_guard<std::mutex> lk(m_mutex);
		m_bNotify = true;
	}
	m_cv.notify_one();
#else

	if (--fFDDTicks <= 0.0)
	{
		do
		{
			// проверка m_pFDD на nullptr делается снаружи
			m_pFDD->Periodic();    // Вращаем диск на одно слово на дорожке
			fFDDTicks += fFDD_Mod;
		}
		while (fFDDTicks < 1.0);
	}

#endif
}



#if (PER_THREADED_MODEL)

void CPeriodicProc::StopThread()
{
	{
		std::lock_guard<std::mutex> lk(m_mutex);
		m_bThreadTerminate = true;
		m_bNotify = true;
	}
	m_cv.notify_one();
	// вот такая странная ситуация: после подачи notify_one, поток не успевает
	// пробудиться и захватить мутекс, и деструктор успевает убить объект CHDD.
	// а поток остаётся в памяти. Потому что мутекс захватывается тут, а потом
	// сразу освобождается. Поэтому приходится добавлять ещё один мутекс,
	// который залочен всегда, пока поток существует, и разлочивается при его
	// уничтожении, а тут мы ждём, пока сможем залочить этот второй мутекс
	std::lock_guard<std::mutex> lk(m_mutThr);
}

bool CPeriodicProc::StartThread()
{
	m_ExecuteThread = std::thread(&CPeriodicProc::ExecuteThreadFunc, this);

	if (m_ExecuteThread.joinable())
	{
		m_ExecuteThread.detach();
		return true;
	}

	return false;
}

void CPeriodicProc::ExecuteThreadFunc()
{
	std::lock_guard<std::mutex> lk(m_mutThr);

	for (;;)
	{
		std::unique_lock<std::mutex> lk(m_mutex);
		m_cv.wait(lk, [&]()
		{
			return m_bNotify;
		});
		m_bNotify = false;
		lk.unlock();

		if (m_bThreadTerminate) // сигнал к остановке потока получен?
		{
			break;   // и выходим
		}

		if (--fFDDTicks <= 0.0)
		{
			do
			{
				// проверка m_pFDD на nullptr делается снаружи
				m_pFDD->Periodic();    // Вращаем диск на одно слово на дорожке
				fFDDTicks += fFDD_Mod;
			}
			while (fFDDTicks < 1.0);
		}
	}
}

#endif
