#include "StdAfx.h"
#include "ConnectionManager.h"
#include "ServerStartErrorException.h"

#define	CLASS_NAME	"ConnectionManager"

CConnectionManager::CConnectionManager(void)
{
}

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



/////////////////////////////////////////////////////////////////////////////////////
//	操作
/////////////////////////////////////////////////////////////////////////////////////
/*!
	開始
*/
void CConnectionManager::Start(IServerToolPtr 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, "スレッドの起動に失敗しました");

	//	初期化
	m_init = TRUE;

	//	最終ステータス
	m_serverTool->WriteSystemLog(CLASS_NAME, SERVER_STATUS_NORMAL, "接続マネージャを起動しました");
}

/*!
	停止
*/
void CConnectionManager::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, SERVER_STATUS_NORMAL, "接続マネージャを停止しました");
}


/////////////////////////////////////////////////////////////////////////////////////
//	スレッド関数
/////////////////////////////////////////////////////////////////////////////////////
/*!
	スレッド
*/
void CConnectionManager::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 CConnectionManager::CreateNewThread(IClientConnectionPtr connection)
{
	SCOPE_LOCK();

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

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

		//	追加
		m_threads.AddTail(thread);
	}
	catch(CMemoryException *err)
	{
		//	報告
		m_serverTool->WriteSystemLog(CLASS_NAME, SERVER_STATUS_ERROR, "メモリ不足が発生しました", 1 * 60 * 60 * 1000);

		//	待機
		err->Delete();
		Sleep(1000);
	}
}

/*!
	接続を片付ける

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

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

	//	検索して削除
	POSITION pos = m_threads.GetHeadPosition();
	while(pos)
	{
		//	終了？
		if(!m_threads.GetAt(pos)->IsRunning())
		{
			//	削除
			POSITION	del = pos;
			m_threads.GetNext(pos);
			m_threads.RemoveAt(del);
		}
		else
			m_threads.GetNext(pos);
	}

	m_endThreadCount = 0;
}

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

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

/*!
	接続数取得
*/
int CConnectionManager::GetThreadCount()
{
	SCOPE_LOCK();

	return(m_threads.GetSize());
}

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

	m_endThreadCount++;
}

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

