////////////////////////////////////////////////////////////////////////////
// CLock クラスのインプリメンテーション
//
////////////////////////////////////////////////////////////////////////////

#include "Lock.h"
#include <assert.h>

using namespace std;

////////////////////////////////////////////////////////////////////////////
// 構築/破棄
////////////////////////////////////////////////////////////////////////////

CLock::CLock() :
	sema_Occupation( 1 ), sema_Share( 1 ), sema_id( 1 ), counter( 1 ), ShareLockCount( 0 )
{

}

CLock::~CLock()
{

}

////////////////////////////////////////////////////////////////////////////
// メソッド
////////////////////////////////////////////////////////////////////////////

// 占有ロックを試みる
LOCK_ID CLock::TryOccupationLock()
{
	// まず共有ロックの確認を行う
	// （誰か一人でも共有ロックがかかっていたら、占有ロックは出来ない）
	if ( !sema_Share.Try() ) return 0;

	// 占有ロックの確認を行う
	if ( !sema_Occupation.Try() ) return 0;

	// 占有ロックのIDを発番する
	return CreateID( LT_OCCUPATION );
}

// 共有ロックを試みる
LOCK_ID CLock::TryShareLock()
{
	// 占有ロックの確認を行う
	// 誰かが占有ロックしていたら、共有ロックは出来ない
	if ( !sema_Occupation.Try() ) return 0;
	sema_Occupation.V();

	// 共有ロックを行う（ロック出来なくてもかまわない）
	sema_Share.Try();

	// 共有ロックのIDを発番する
	return CreateID( LT_SHARE );
}

// 占有ロックするまで待ち合わせる
LOCK_ID CLock::WaitOccupationLock()
{
	// まず共有ロックを行う
	sema_Share.Try();

	// 占有ロックを行う
	sema_Occupation.P();

	// 占有ロックのIDを発番する
	return CreateID( LT_OCCUPATION );
}

// 共有ロックするまで待ち合わせる
LOCK_ID CLock::WaitShareLock()
{
	// 占有ロックの待ち合わせを行う
	sema_Occupation.P();
	sema_Occupation.V();

	// 共有ロックを行う
	sema_Share.Try();

	// 共有ロックのIDを発番する
	return CreateID( LT_SHARE );
}

// ロックを解放する
void CLock::Free( LOCK_ID id )
{
	sema_id.P();

	assert( NULL != this );
	map< LOCK_ID, LOCKTYPE >::iterator itr = mapLockNumber.find( id );
	assert( mapLockNumber.end() != itr );

	// 共有ロックの数を減算する
	ShareLockCount--;

	if ( itr->second == LT_OCCUPATION ) {
		// 占有ロックの解除
		sema_Occupation.V();
		sema_Share.V();
	}
	else {
		// 共有ロックの解除
		if ( 0 == ShareLockCount )
			sema_Share.V();
	}

	// mapから削除する
	mapLockNumber.erase( itr );

	sema_id.V();
}

// 指定されたIDで占有ロックされているか否か
bool CLock::CheckOccupationLock( LOCK_ID id ) const
{
	bool r = true;
	sema_id.P();

	assert( NULL != this );
	map< LOCK_ID, LOCKTYPE >::const_iterator itr = mapLockNumber.find( id );
	if ( itr == mapLockNumber.end() )
		r = false;
	else
		r = ( LT_OCCUPATION == itr->second );

	sema_id.V();

	return r;
}

// 指定されたIDで共有ロックされているか否か
bool CLock::CheckShareLock( LOCK_ID id ) const
{
	bool r = true;
	sema_id.P();

	assert( NULL != this );
	map< LOCK_ID, LOCKTYPE >::const_iterator itr = mapLockNumber.find( id );
	if ( itr == mapLockNumber.end() )
		r = false;
	else
		r = ( LT_SHARE == itr->second );

	sema_id.V();

	return r;
}

// IDを発番する
LOCK_ID CLock::CreateID( LOCKTYPE type )
{
	// ID管理用セマフォで、mapLockNumberやcounterをロックする
	sema_id.P();

	// 共有ロックのかかっている数をカウントする
	ShareLockCount++;

	// カウンタ値を取得、mapに追加、カウンタ値の加算を行う
	LOCK_ID r = counter;
	++counter;
	mapLockNumber.insert( pair< LOCK_ID, LOCKTYPE >( r, type ) );

	// ロックを解放
	sema_id.V();

	return r;
}

