#include "StdAfx.h"
#include "ConnectionThreadManager.h"
#include "ServerException.h"

#define	CLASS_NAME	_U8("ConnectionThreadManager")

CConnectionThreadManager::CConnectionThreadManager(void)
{
}

CConnectionThreadManager::~CConnectionThreadManager(void)
{
	Stop();
}



/////////////////////////////////////////////////////////////////////////////////////
//	操作
/////////////////////////////////////////////////////////////////////////////////////
/*!
	開始
*/
void CConnectionThreadManager::Start(CIServerToolWrap serverTool)
{
	//	初期化済み？
	if(m_init)
		return;

	//	コピー、初期化
	m_serverTool = serverTool;
	m_endThreadCount = 0;
	m_connectionID = 0;


	//	設定取得
	m_setting = m_serverTool.GetManagerSetting(m_setting.GetSettingName());

	//	設定初期化
	int maxConnect = m_setting.GetMaxConnect();
	m_maxThreads = maxConnect;

	//	キュー初期化
	m_queue.SetMaxQueue(maxConnect); //	最大接続数を設定する
	m_queue.Start();

	//	オーバーロード初期化
	m_overload.Start(m_serverTool.GetIServerTool(), maxConnect, m_setting.GetOverloadModeKeepTime());

	//	コンテキスト初期化
	m_context.Start(m_serverTool.GetIServerTool());

	//	スレッド起動
	if(StartThread())
		throw CServerStartErrorException(CLASS_NAME, _U16("スレッドの起動に失敗しました"));

	//	初期化
	m_init = TRUE;

	//	最終ステータス
	m_serverTool.WriteSystemLog(CLASS_NAME, SYSTEM_LOG_INFO, _U16("接続スレッドマネージャを起動しました"));
}

/*!
	停止
*/
void CConnectionThreadManager::Stop()
{
	//	初期化チェック
	if(!m_init)
		return;
	m_init = FALSE;

	//	スレッド停止
	EndThread();

	//	全スレッド削除
	m_threads.RemoveAll();

	//	コンテキスト停止
	m_context.Stop();

	//	オーバーロードマネージャ停止
	m_overload.Stop();

	//	待ち受けキュー停止
	m_queue.Stop();

	//	最終ステータス
	m_serverTool.WriteSystemLog(CLASS_NAME, SYSTEM_LOG_INFO, _U16("接続スレッドマネージャを停止しました"));
}


/////////////////////////////////////////////////////////////////////////////////////
//	スレッド関数
/////////////////////////////////////////////////////////////////////////////////////
/*!
	スレッド
*/
void CConnectionThreadManager::ThreadMain()
{
	//	接続ループ
	while(!GetBreak())
	{
		//	動作状態
		if(InMaxThreads())
		{
			//	ちょっと待つ
			WaitForSomeObjects(100, GetBreakEvent());
		}
		else
		{
			//	接続を待つ
			if(WaitForSomeObjects(500, m_queue.GetSemaphore(), GetBreakEvent()) == 1)
				CreateNewThread(m_queue.GetNewConnection());
		}

		//	GC
		DeleteEndThreads();

		//	サーバ負荷設定
		m_overload.UpdateServerload(GetThreadCount(), m_queue.GetCurrentQueueCount());
	}
}


/*!
	新しい接続を追加
*/
void CConnectionThreadManager::CreateNewThread(IClientConnectionAutoPtr connection)
{
	CRITICAL_SECTION_SCOPE();

	//	無い？
	if(connection.IsEmpty())
		return;

	try
	{
		//	生成
		CConnectionThread *thread = new CConnectionThread();
		if(thread->Start(	m_serverTool.GetIServerTool(),
							this,
							m_context.GetIConetextGene(),
							connection,
							m_connectionID++))
			m_serverTool.WriteSystemLog(CLASS_NAME, SYSTEM_LOG_ERROR, _U16("処理スレッドの起動に失敗しました"));

		//	追加
		m_threads.AddTail(CConnectionThreadAutoPtr(thread));
	}
	catch(CBadAllocException &err)
	{
		//	使用しない
		UNREFERENCED_PARAMETER(err);

		//	報告
		m_serverTool.WriteSystemLog(CLASS_NAME, SYSTEM_LOG_ERROR, _U16("メモリ不足が発生しました"));

		//	待機
		Sleep(1000);
	}
}

/*!
	接続を片付ける

	EndConnectionが呼ばれた時点では、まだスレッドが終了していないため、ポーリングする必要がある
*/
void CConnectionThreadManager::DeleteEndThreads()
{
	CRITICAL_SECTION_SCOPE();

	//	不要
	if(m_endThreadCount == 0)
		return;

	//	検索して削除
	for(int i=m_threads.GetCount()-1;i>=0;i--)
	{
		//	終了？
		if(!m_threads[i]->IsRunning())
			m_threads.RemoveAt(i);
	}

	//	数を0に
	m_endThreadCount = 0;
}

/*!
	最大接続数に到達？
*/
int CConnectionThreadManager::InMaxThreads()
{
	CRITICAL_SECTION_SCOPE();

	//	最大スレッド数に到達？
	if(m_threads.GetCount() >= m_maxThreads)
		return(TRUE);
	return(FALSE);
}

/*!
	接続数取得
*/
int CConnectionThreadManager::GetThreadCount()
{
	CRITICAL_SECTION_SCOPE();

	return(m_threads.GetCount());
}

/////////////////////////////////////////////////////////////////////////////////////
//	IConnectionThreadParent
/////////////////////////////////////////////////////////////////////////////////////
/*!
	スレッド終了
*/
void CConnectionThreadManager::EndConnection(CConnectionThread *connectionPtr)
{
	CRITICAL_SECTION_SCOPE();

	m_endThreadCount++;
}

/*!
	オーバーロード
*/
int CConnectionThreadManager::InOverloadMode()
{
	return m_overload.InOverloadMode();
}

