/*
    libmaus2
    Copyright (C) 2009-2013 German Tischler
    Copyright (C) 2011-2013 Genome Research Limited

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <libmaus2/parallel/StdSemaphore.hpp>
#include <libmaus2/parallel/SimpleThreadPoolBase.hpp>
#include <libmaus2/parallel/SimpleThreadPoolThreadIdTree.hpp>
#include <thread>

libmaus2::parallel::StdSemaphore::StdSemaphore() : condvar(), mutex(), count(0)
{
}
libmaus2::parallel::StdSemaphore::~StdSemaphore()
{
}

void libmaus2::parallel::StdSemaphore::post()
{
	{
		std::unique_lock<std::mutex> lck(mutex);
		count += 1;
	}
	condvar.notify_one();
}

void libmaus2::parallel::StdSemaphore::wait()
{
	std::unique_lock<std::mutex> lck(mutex);

	while ( true )
	{
		if ( count )
		{
			count -= 1;
			break;
		}
		else
		{
			condvar.wait_for(lck,std::chrono::seconds(1));

			if ( libmaus2::parallel::SimpleThreadPoolBase::globalPanicCount )
			{
				std::thread::id const tid = std::this_thread::get_id();
				libmaus2::parallel::SimpleThreadPoolThreadId const * p = libmaus2::parallel::SimpleThreadPoolThreadIdTree::findIdGlobal(tid);
				if ( p && p->pool->isInPanicMode() )
					throw std::runtime_error("[E] thread pool is in panic mode");
			}
		}
	}
}

bool libmaus2::parallel::StdSemaphore::trywait()
{
	std::unique_lock<std::mutex> lck(mutex);

	if ( count )
	{
		count -= 1;
		return true;
	}
	else
	{
		return false;
	}
}

bool libmaus2::parallel::StdSemaphore::timedWait()
{
	std::unique_lock<std::mutex> lck(mutex);

	if ( count )
	{
		count -= 1;
		return true;
	}

	condvar.wait_for(lck,std::chrono::seconds(1));

	if ( count )
	{
		count -= 1;
		return true;
	}
	else
	{
		return false;
	}
}
