/* =============================================================================
 *  scpp_thread.cpp
 *  Copyright (c)  2003 - 2011  Nomura Kei
 *  LICENSE :
 *  LGPL (GNU Lesser General General Public License - Version 3,29 June 2007)
 *  http://www.gnu.org/copyleft/lesser.html
 * =============================================================================
 *
 *  ライブラリの動的ロードを行うモジュール
 *
 */
#include <scpp_os.hpp>

#include <scpp_thread.hpp>

namespace
{

}	// namespace 無名名前空間



namespace scpp
{

////////////////////////////////////////////////////////////////////////////////
//
// ThreadException
//

/**
 * 最後に発生した errno に対するメッセージをもつ Exception を構築します.
 * エラーメッセージを取得できない場合, 空文字がメッセージに設定されます.
 */
ThreadException::ThreadException() throw() : Exception()
{
}

/**
 * コピーコンストラクタ.
 *
 * @param t コピー元
 */
ThreadException::ThreadException(const ThreadException& t) throw() : Exception(t)
{
}

/**
 * 指定されたメッセージをもつ ThreadException を構築します.
 *
 * @param msg メッセージ
 */
ThreadException::ThreadException(const std::string& msg) throw() : Exception(msg)
{
}

/**
 * デストラクタ.
 */
ThreadException::~ThreadException() throw()
{
}


////////////////////////////////////////////////////////////////////////////////
//
// Thread
//

/**
 * スレッドを構築します.
 */
Thread::Thread() : runnable(0), aliveState(false)
{
	// NOP
}


/**
 * スレッドを構築(コピー)します.
 *
 * @param t コピー元
 */
Thread::Thread(const Thread& t) : runnable(t.runnable), aliveState(t.aliveState)
{
	// NOP
}


/**
 * 指定された Runnable を実行するスレッドを構築します.
 *
 * @param r スレッドで実行する Runnable
 */
Thread::Thread(Runnable* r) : runnable(r), aliveState(false)
{
	// NOP
}


/**
 * スレッドを破棄します.
 */
Thread::~Thread()
{
	// NOP
}


/**
 * スレッドが生きているか否かを返します.
 *
 * @return ture/false (スレッドが生存している/していない)
 */
bool Thread::isAlive()
{
	return aliveState;
}


/**
 * スレッドで実行される関数.
 */
void Thread::run()
{
	if (runnable != 0)
	{
		runnable->run();
	}
}


/**
 * スレッドを開始します.
 */
void Thread::start()
{
	if (aliveState) {
		throw ThreadException("already running");
	}

	aliveState = true;
	bool ret = createThread();
	if (!ret)
	{
		aliveState = false;
		throw ThreadException();
	}

}


/**
 * スレッドが終了するのを待機します.
 */
void Thread::join()
{
#if (SCPP_IS_WINDOWS)
	WaitForSingleObject(tid, INFINITE);
	CloseHandle(tid);
#else
	pthread_join(tid, 0);
#endif
}


/**
 * 指定された ms 間スリープします。
 *
 * @param time スリープする時間(ms)
 */
void Thread::sleep(long time)
{
#if (SCPP_IS_WINDOWS)
	Sleep(time);
#else
	struct timespec req;
	long            sec  = time / 1000;
	long            msec = time - (sec * 1000);
	req.tv_sec  = sec;
	req.tv_nsec = msec * 1000000;
	nanosleep(&req, 0);
#endif
}


/**
 * スレッドを生成します.
 *
 * @return スレッドのハンドル
 */
bool Thread::createThread()
{
#if (SCPP_IS_WINDOWS)
	threadId = 0;
	tid = (thread_t) _beginthreadex(
			0,
			0,
			&Thread::executeThread,
			this,
			0,
			&threadId); 
	return (tid != 0);
#else
	int ret = pthread_create(&tid, 0, &Thread::executeThread, this);
	return (ret == 0);

#endif
}


/**
 * スレッド実行用関数
 * 本関数は, Thread::start メソッドからのみ実行されます.
 *
 * @param args Thread ポインタ
 */
#if (SCPP_IS_WINDOWS)
unsigned __stdcall Thread::executeThread(void* args)
{
	scpp::Thread* thread = reinterpret_cast<scpp::Thread*>(args);
	thread->run();
	thread->aliveState = false;
//	_endthreadex は自動で呼び出されるので省略.
//	_endthreadex(0);
	return 0;
}

#else
void* Thread::executeThread(void* args)
{
	scpp::Thread* thread = reinterpret_cast<scpp::Thread*>(args);
	thread->run();
	thread->aliveState = false;
//	pthread_exit(0);
	return 0;
}

#endif


////////////////////////////////////////////////////////////////////////////////
//
// Mutex
//

/**
 * Mutex を構築します.
 */
Mutex::Mutex()
{
#if (SCPP_IS_WINDOWS)
	mutex = CreateMutex(0, false, 0);
#else
	pthread_mutex_init(&mutex, 0);
#endif

}


/**
 * Mutex を破棄します.
 */
Mutex::~Mutex()
{
#if (SCPP_IS_WINDOWS)
	CloseHandle(mutex);
#else
	pthread_mutex_destroy(&mutex);
#endif
}


/**
 * ロックします.
 */
void Mutex::lock()
{
#if (SCPP_IS_WINDOWS)
	WaitForSingleObject(mutex, INFINITE);
#else
	pthread_mutex_lock(&mutex);
#endif

}

/**
 * ロックを解除します.
 */
void Mutex::unlock()
{
#if (SCPP_IS_WINDOWS)
	ReleaseMutex(mutex);
#else
	pthread_mutex_unlock(&mutex);
#endif
}


}	// namespace scpp

