#include "StdAfx.h"
#include "threadskeleton.h"
#include <typeinfo.h>

CThreadSkeleton::CThreadSkeleton()
{
	m_thread = NULL;
	m_hThread = NULL;

	//	中断用イベント
	m_breakEvent.Create(TRUE,FALSE);
}

CThreadSkeleton::~CThreadSkeleton(void)
{
	//	スレッド停止
	EndThread();

	//	スレッドオブジェクト削除
	SAFE_DELETE(m_thread);	//	スレッドハンドルも閉じられる模様
}

////////////////////////////////////////////////////////////////////////////////////////////
//	開始・終了・中断
////////////////////////////////////////////////////////////////////////////////////////////
/*!
	スレッドの開始
*/
int CThreadSkeleton::StartThread(int stackSize)
{
	//	中断イベント解除
	::ResetEvent(m_breakEvent);

	//	スレッドを作成する
	m_thread = AfxBeginThread(WorkThread, this, THREAD_PRIORITY_NORMAL, stackSize*1024, CREATE_SUSPENDED);
	if(m_thread == NULL)
	{
		TRACE("GetLastError :%d\n",GetLastError());
		return(-1);
	}

	//	自動削除解除
	m_thread->m_bAutoDelete = FALSE;

	//	ハンドル取得
	m_hThread = m_thread->m_hThread;

	//	スレッド実行開始
	m_thread->ResumeThread();

	return(0);
}


/*!
	スレッドの終了・強制終了
*/
void CThreadSkeleton::EndThread()
{
	Break();

	//	スレッドが終了するまで待つ
	if(IsRunning())
		::WaitForSingleObject(m_hThread,INFINITE);
}


/*!
	中断
*/
void CThreadSkeleton::Break()
{
	::SetEvent(m_breakEvent);
}

/*!
	強制終了（特別な理由が無い限り、使用禁止）
*/
void CThreadSkeleton::ForceKillThread()
{
	::TerminateThread(m_hThread, 0);
	::SetEvent(m_breakEvent);
}

/*!
	実行中か取得する
*/
int CThreadSkeleton::IsRunning()
{
	if(m_hThread == NULL)
		return(0);

	return(::WaitForSingleObject(m_hThread, 0) != WAIT_OBJECT_0);
};


////////////////////////////////////////////////////////////////////////////////////////////
//	ツール関数
////////////////////////////////////////////////////////////////////////////////////////////
/*!
	いくつかのオブジェクトを待つ

	\return	-1:エラー、0:タイムアウト、1以降:反応したハンドル	
*/
int CThreadSkeleton::WaitForSomeObjects(int timeoutMs, HANDLE obj1, HANDLE obj2, HANDLE obj3, HANDLE obj4)
{
	HANDLE	wait[4];
	DWORD 	count;

	wait[0] = obj1;
	wait[1] = obj2;
	wait[2] = obj3;
	wait[3] = obj4;

	//	ハンドル数カウント
	for(count=0;count<4;count++)
	{
		if(wait[count] == NULL)
			break;
	}

	DWORD ret = ::WaitForMultipleObjects(count,wait,FALSE,timeoutMs);
	if(ret == WAIT_TIMEOUT)
		return(0);

	if(ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + count)
		return(ret - WAIT_OBJECT_0 + 1);

	return(-1);
}


////////////////////////////////////////////////////////////////////////////////////////////
//	スレッド関数
////////////////////////////////////////////////////////////////////////////////////////////
/*!
	継承クラスのThreadMainを呼ぶ
*/
void CThreadSkeleton::_ThreadMain()
{
#ifndef _DEBUG
	try
	{
#endif

		ThreadMain();

#ifndef _DEBUG
	}
	catch(CMemoryException *err)
	{
		FatalError(typeid(this).name(), "メモリ不足により、処理スレッドが停止しました。");
		err->Delete();
	}
	catch(CException *err)
	{
		FatalError(typeid(this).name(), "サーバ内部の未処理MFC例外により、処理スレッドが停止しました。");
		err->Delete();
	}
#endif

}


/*!
	実行関数
*/
UINT CThreadSkeleton::WorkThread(LPVOID pParam)
{
	CoInitialize(NULL);
	CThreadSkeleton	*thread = (CThreadSkeleton*)pParam;

#ifndef _DEBUG
	__try
	{
#endif

		thread->_ThreadMain();

#ifndef _DEBUG
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		FatalError(typeid(thread).name(), "サーバ内部の未処理例外により、処理スレッドが停止しました。");
	}
#endif
	CoUninitialize();

	return(0);
}


/*!
	致命的エラー
*/
void CThreadSkeleton::FatalError(const char *clsName, const char *errInfo)
{
	//	まず、保存先を生成
	char modulePath[MAX_PATH+1];
	DWORD len = GetModuleFileName(NULL, modulePath, MAX_PATH);
	while ( modulePath[len--]!='\\' && len!=0 )
		;
	modulePath[len+2] = '\0';        // ￥マークで終わらせる
	strcat_s(modulePath, MAX_PATH, "FatalError.log");

	//	ログを出力
	FILE *errlog;
	if(fopen_s(&errlog, modulePath, "wt") == 0)
	{
		fprintf(errlog, "%s : %s\n", clsName, errInfo);
		fclose(errlog);
	}
}

