#include "stdafx.h"

#include "LockManager.hpp"


#pragma once

#include <string>
#include <map>

#include <assert.h>



LockObject::LockObject(CRITICAL_SECTION *pCriticalSect)
	: owner_(true)
	, pCriticalSect_(pCriticalSect)
{
	assert(pCriticalSect && "クリティカルセクションにNULLは指定できません。");

	// ロックを開始する。
	EnterCriticalSection(pCriticalSect_);
}

LockObject::~LockObject()
{
	Release();
}

void LockObject::Release() throw()
{
	if (owner_ ) {
		owner_ = false;
		// ロックを終了する。
		// 一度ロック解除したら、
		// このオブジェクトでは二度とロックできないことが重要。
		LeaveCriticalSection(pCriticalSect_);
	}
}


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


LockManager::LockManager()
{
	ZeroMemory(&criticalSect_, sizeof(CRITICAL_SECTION));
	::InitializeCriticalSection(&criticalSect_);
}

LockManager::~LockManager()
{
	EnterCriticalSection(&criticalSect_);
	try {
		for (critical_section_map::iterator ite = criticalSections_.begin();
			ite != criticalSections_.end();
			++ite) {
			CRITICAL_SECTION* pCriticalSect = ite->second;
			::DeleteCriticalSection(pCriticalSect);
			delete pCriticalSect;
		}
	}
	catch (...) {
		LeaveCriticalSection(&criticalSect_);
		throw;
	}
	LeaveCriticalSection(&criticalSect_);

	// ロックマネージャ自身のクリティカルセクションを破棄する
	::DeleteCriticalSection(&criticalSect_);
}

LockObject *LockManager::GetLockObject(const tstring& name)
{
	LockObject *pLockObject = NULL;

	EnterCriticalSection(&criticalSect_);
	try {
		CRITICAL_SECTION* pCriticalSect = NULL;
		critical_section_map::iterator ite = criticalSections_.find(name);
		if (ite == criticalSections_.end()) {
			// 発見されず = 新規に作成して登録する。
			pCriticalSect = new CRITICAL_SECTION();
			::InitializeCriticalSection(pCriticalSect);
			criticalSections_.insert(std::pair<tstring, CRITICAL_SECTION*>(name, pCriticalSect));

		}
		else {
			// すでにある場合
			pCriticalSect = ite->second;
		}

		// ロックをかけた状態で返す。
		assert(pCriticalSect && "CRITICAL_SECTIONがNULLです。");
		pLockObject = new LockObject(pCriticalSect);
	}
	catch (...) {
		LeaveCriticalSection(&criticalSect_);
		throw;
	}
	LeaveCriticalSection(&criticalSect_);

	assert(pLockObject && "ロックオブジェクトがNULLです。");
	return pLockObject;
}

void LockManager::RemoveUnusedLock()
{
	EnterCriticalSection(&criticalSect_);
	try {
		for (critical_section_map::iterator ite = criticalSections_.begin();
			ite != criticalSections_.end();) {
			CRITICAL_SECTION* pCriticalSect = ite->second;
			if (TryEnterCriticalSection(pCriticalSect)) {
				// ロックを確保できたということは、だれも使っていないということとみなす。
				::LeaveCriticalSection(pCriticalSect);
				::DeleteCriticalSection(pCriticalSect);
				delete pCriticalSect;
				ite = criticalSections_.erase(ite);
				continue;
			}
			++ite;
		}
	}
	catch (...) {
		LeaveCriticalSection(&criticalSect_);
		throw;
	}
	LeaveCriticalSection(&criticalSect_);
}

