#include "ProcessStatus.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "Logger.h"
#include "ProcessRename.h"

// NEW 2014-06-19 huibong
// 설정 가능한 동시 Thread 수를 제한.
#define MAX_THREAD_COUNT_LIMIT 1024

// Process 정보 update 주기  (sec)
#define PROCESS_STATUS_UPDATE_TIME  2


////////////////////////////////////////

// 생성자
CProcessStatus::CProcessStatus( CDataQueue<CSourceData>* const pQueueSource
	, CDataQueue<CRetryData>* const pQueueRetry
	, CDataQueue<CCompleteData>* const pQueueCompleteReplication
	, CDataQueue<CCacheRequestData>* const pQueueCacheRequest
	, CDataQueue<CCacheData>* const pQueueCacheComplete
	, CCacheJobList* const pCacheJob )
	: m_pQueueSource( pQueueSource ) 
	, m_pQueueRetry( pQueueRetry ) 
	, m_pQueueCompleteReplication( pQueueCompleteReplication )
	, m_pQueueCacheRequest( pQueueCacheRequest )
	, m_pQueueCacheComplete( pQueueCacheComplete )
	, m_pCacheJob( pCacheJob )
{
	m_threadHandle = 0;

	m_nMaxReplicationThreadCount = 0;
	m_nMaxCacheThreadCount       = 0;

	m_nCurrentReplicationCount = 0;
    m_nCurrentCacheCount       = 0;
	
	// Lock 초기화
	pthread_mutex_init(&m_mxReplicateThreadCount, NULL);
	pthread_mutex_init(&m_mxCacheThreadCount, NULL);
}

// 소멸자
CProcessStatus::~CProcessStatus()
{
	// Thread 동작 정지 처리
	// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로 
	//   업무 Flow 수정시 주의할 것
	if( m_threadHandle != 0 )
		pthread_cancel( m_threadHandle );

	pthread_mutex_destroy(&m_mxReplicateThreadCount);
	pthread_mutex_destroy(&m_mxCacheThreadCount);	
}

// 객체 초기화
// @param [in] nMaxReplicationThreadCount Replication 처리 관련 동시 최대 생성 가능 Thread  수
// @param [in] nMaxCacheThreadCount Hot Content cache 처리 관련 동시 최대 생성 가능 Thread  수
// @return true 성공
//         false 생성자로 전달된 객체 포인터가 유효하지 않은 경우, 
//               인자로 전달된 최대 Thread 수가 너무 큰 경우.(MAX_THREAD_COUNT_LIMIT 초과시)
bool CProcessStatus::Init(const unsigned int nMaxReplicationThreadCount, const unsigned int nMaxCacheThreadCount)
{
	// 생성자로 전달된 변수 Check.
	if( m_pQueueSource == NULL || m_pQueueRetry == NULL || m_pQueueCompleteReplication == NULL
		|| m_pQueueCacheRequest == NULL || m_pQueueCacheComplete == NULL || m_pCacheJob == NULL )
	{
		LOG( LERR, "module constructor parameter include null. check parameter" );
		return false;
	}

	// 전달받은 동시 생성가능 Thread 가 내부 정의된 제약값을 초과하는지 확인
	if( nMaxReplicationThreadCount > MAX_THREAD_COUNT_LIMIT )
	{
		LOG( LERR, "replication max thread count[%u] is too high. set below limit[%d]"
			, nMaxReplicationThreadCount, MAX_THREAD_COUNT_LIMIT );
		return false;
	}
	if( nMaxCacheThreadCount > MAX_THREAD_COUNT_LIMIT )
	{
		LOG( LERR, "hot content cache max thread count[%u] is too high. set below limit[%d]"
			, nMaxCacheThreadCount, MAX_THREAD_COUNT_LIMIT );
		return false;
	}

	m_nMaxReplicationThreadCount = nMaxReplicationThreadCount;
	m_nMaxCacheThreadCount       = nMaxCacheThreadCount;

	// CHG 2014-06-18 huibong
	// - 본 함수는 Worker 상에서 각 객체 모듈 초기화 중에 호출되므로...
	//   프로세스 표시명을 아직 변경해서는 안된다.
    //UpdateTitle();
	
	return true;
}

// thread를 생성하여 관련 작업을 수행.
bool CProcessStatus::Start()
{
	int nRet = ::pthread_create( &m_threadHandle, NULL, CProcessStatus::threadFunc, this );
	if( nRet != 0 )
	{
		// Thread 생성 실패시
		int errorNum = errno;
		LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}

	return true;
}

void* CProcessStatus::threadFunc( void* arg )
{
	CProcessStatus* pObject = reinterpret_cast<CProcessStatus *>( arg );
	pthread_detach( pthread_self() );

	while( 1 )
	{
		pthread_testcancel();		
		pObject->UpdateTitle();		
		pthread_testcancel();
				
		sleep( PROCESS_STATUS_UPDATE_TIME );
			
	}

	// NEW 2014 - 06 - 23 huibong
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	pObject->m_threadHandle = 0;

	return NULL;
}


// 현재 프로세스 표시 정보 업데이트 
void CProcessStatus::UpdateTitle(void)
{
	// Rs: replication source queue size
	// Rt: current replication thread count
	// Rr: replication retry queue size
	// Rc: replication complete queue size

	// Cr: FHS 로 부터 수신된 Hot Content Cache 요청 정보 저장 queue size
	// Cj: hot content cache job list size
	// Ct: current hot content cache thread count
	// Cc: hot content cache complete queue size
		
#if defined(__FreeBSD__)
	setproctitle("[Rs%u Rt%d Rr%u Rc%u  Cr%u Ct%d Cj%u Cc%u]",
			m_pQueueSource->GetSize(), 
			m_nCurrentReplicationCount,
			m_pQueueRetry->GetSize(),
			m_pQueueCompleteReplication->GetSize(),
			m_pQueueContentInfo->GetSize(),
			m_nCurrentCacheCount,
			m_pListJob->GetSize(),
			m_pQueueCompleteCache->GetSize() );

#else
	char szTemp[100] = {0};	
	sprintf(szTemp,	 "%s: [Rs%u Rt%d Rr%u Rc%u  Cr%u Cj%d Ct%d Cc%u]",
			PROG_NAME,
			m_pQueueSource->GetSize(),
		    m_nCurrentReplicationCount,
			m_pQueueRetry->GetSize(),
			m_pQueueCompleteReplication->GetSize(),
			m_pQueueCacheRequest->GetSize(),
			m_pCacheJob->GetSize(),
			m_nCurrentCacheCount,			
			m_pQueueCacheComplete->GetSize() );
	
	set_ps_display(szTemp, false);

#endif

	return ;
}

// Replication 관련 동시 생성 가능한 Thread 수를 반환
unsigned int CProcessStatus::GetValidReplicationCount(void)
{	
	// 생성 불가인 경우.
	if( m_nMaxReplicationThreadCount == 0 )
		return 0;
	else
	{
		int nAvailCount;

		// lock 설정 후 사용 가능 Thread 수 계산.
		pthread_mutex_lock( &m_mxReplicateThreadCount );
		nAvailCount = m_nMaxReplicationThreadCount - m_nCurrentReplicationCount;
		pthread_mutex_unlock( &m_mxReplicateThreadCount );

		// 현재 생성된 Thread 수가 Max 값보다 큰 경우
		// 사용 가능 Thread 수를 0 으로 전달.
		if( nAvailCount < 0 )
			return 0;		
		else
			return nAvailCount;
	}
}

// Hot Content Cache 관련 동시 생성 가능한 Thread 수를 반환
unsigned int CProcessStatus::GetValidCacheCount(void)
{
	// 생성 불가인 경우.
	if( m_nMaxCacheThreadCount <= 0 )
		return 0;
	else
	{
		int nAvailCount;

		// lock 설정 후 사용 가능 Thread 수 계산.
		pthread_mutex_lock( &m_mxCacheThreadCount );
		nAvailCount = m_nMaxCacheThreadCount - m_nCurrentCacheCount;
		pthread_mutex_unlock( &m_mxCacheThreadCount );

		// 현재 생성된 Thread 수가 Max 값보다 큰 경우
		// 사용 가능 Thread 수를 0 으로 전달.
		if( nAvailCount < 0 )
			return 0;
		else
			return nAvailCount;
	}
}

// Replication 관련 작업 Thread 수를 1 증가 처리
void CProcessStatus::AddReplicationCount(void)
{
	AddCount(Replication);
}

// Replication 관련 작업 Thread 수를 1 감소 처리
void CProcessStatus::SubtractReplicationCount(void)
{
	SubtractCount(Replication);
}

// Hot content cache 관련 작업 Thread 수를 1 증가 처리
void CProcessStatus::AddCacheCount(void)
{
	AddCount(Cache);
}

// Hot content cache 관련 작업 Thread 수를 1 감소 처리
void CProcessStatus::SubtractCacheCount(void)
{
	SubtractCount(Cache);
}

// 전달받은 작업의 Thread 수를 1 증가 시킨다.
void CProcessStatus::AddCount(Job job)
{
	if( job == Cache )
	{
		pthread_mutex_lock(&m_mxCacheThreadCount);	
		++m_nCurrentCacheCount;
		pthread_mutex_unlock(&m_mxCacheThreadCount);		
	}
	else if( job == Replication )
	{
		pthread_mutex_lock(&m_mxReplicateThreadCount);		
		++m_nCurrentReplicationCount;
		pthread_mutex_unlock(&m_mxReplicateThreadCount);		
	}
	
	// 프로세스 출력 표시명 갱신
	// - Worker 프로세스에서 주기적으로 하므로.. 제거 처리.
	//UpdateTitle();	
}

// 전달받은 작업의 Thread 수를 1 감소 시킨다.
void CProcessStatus::SubtractCount(Job job)
{
	if( job == Cache )
	{
		pthread_mutex_lock(&m_mxCacheThreadCount);		
		--m_nCurrentCacheCount;
		pthread_mutex_unlock(&m_mxCacheThreadCount);		
	}
	else if( job == Replication )
	{
		pthread_mutex_lock(&m_mxReplicateThreadCount);
		--m_nCurrentReplicationCount;
		pthread_mutex_unlock(&m_mxReplicateThreadCount);
	}

	// 프로세스 출력 표시명 갱신
	// - Worker 프로세스에서 주기적으로 하므로.. 제거 처리.
	//UpdateTitle();	
}
