/*
    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/StdSpinLock.hpp>
#include <libmaus2/parallel/SimpleThreadPoolBase.hpp>
#include <libmaus2/parallel/SimpleThreadPoolThreadIdTree.hpp>
#include <thread>

libmaus2::parallel::StdSpinLock::StdSpinLock() : flag()
{
	flag.clear();
}
libmaus2::parallel::StdSpinLock::~StdSpinLock()
{
}

void libmaus2::parallel::StdSpinLock::lock()
{
	while ( flag.test_and_set() )
        {
		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");
		}
        }
}

void libmaus2::parallel::StdSpinLock::unlock()
{
	flag.clear();
}

/**
 * try to lock spin lock. returns true if locking was succesful, false if lock
 * was already locked
 **/
bool libmaus2::parallel::StdSpinLock::trylock()
{
	return (flag.test_and_set() == false);
}

/*
 * try to lock spin lock. if succesful, lock is unlocked and return value is true,
 * otherwise return value is false
 */
bool libmaus2::parallel::StdSpinLock::tryLockUnlock()
{
	bool const r = trylock();
        if ( r )
        	unlock();
	return r;
}

void libmaus2::parallel::ScopeStdSpinLock::lock()
{
	spinlock.lock();
        locked = true;
}

void libmaus2::parallel::ScopeStdSpinLock::unlock()
{
	spinlock.unlock();
        locked = false;
}

libmaus2::parallel::ScopeStdSpinLock::ScopeStdSpinLock(StdSpinLock & rspinlock, bool prelocked)
: spinlock(rspinlock), locked(prelocked)
{
	if ( ! prelocked )
        	lock();
}
libmaus2::parallel::ScopeStdSpinLock::~ScopeStdSpinLock()
{
	if ( locked )
		spinlock.unlock();
}


