/*
    libmaus2
    Copyright (C) 2019 German Tischler-Höhle

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

libmaus2::parallel::StdMutex::StdMutex() : mutex()
{
}
libmaus2::parallel::StdMutex::~StdMutex() throw()
{
}

// #define LIBMAUS2_PANIC_DEBUG

void libmaus2::parallel::StdMutex::lock()
{
	while ( ! mutex.try_lock_for(std::chrono::seconds(1)) )
	{
		if ( libmaus2::parallel::SimpleThreadPoolBase::globalPanicCount )
		{
			#if defined(LIBMAUS2_PANIC_DEBUG)
			{
				std::cerr << "[V] StdMutex::lock: global panic count is set" << std::endl;
			}
			#endif
			std::thread::id const tid = std::this_thread::get_id();
			libmaus2::parallel::SimpleThreadPoolThreadId const * p = libmaus2::parallel::SimpleThreadPoolThreadIdTree::findIdGlobal(tid);
			#if defined(LIBMAUS2_PANIC_DEBUG)
			{
				std::cerr << "[V] findIdGlobal returned " << p << std::endl;
				if ( p )
					std::cerr << "[V] data " << p->toString() << std::endl;
			}
			#endif
			if ( p && p->pool->isInPanicMode() )
				throw std::runtime_error("[E] thread pool is in panic mode");
		}
        }
}
void libmaus2::parallel::StdMutex::unlock()
{
        mutex.unlock();
}

bool libmaus2::parallel::StdMutex::trylock(uint64_t const seconds)
{
        return mutex.try_lock_for(std::chrono::seconds(seconds));
}

/**
 * try to lock mutex. returns true if locking was succesful, false if mutex
 * was already locked
 **/
bool libmaus2::parallel::StdMutex::trylock()
{
        return mutex.try_lock();
}

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

libmaus2::parallel::ScopeStdMutex::ScopeStdMutex(StdMutex & rmutex, bool const prelocked)
: mutex(rmutex)
{
        if ( ! prelocked )
                mutex.lock();
}
libmaus2::parallel::ScopeStdMutex::~ScopeStdMutex()
{
        mutex.trylock();
        mutex.unlock();
}
void libmaus2::parallel::ScopeStdMutex::lock()
{
        mutex.lock();
}

void libmaus2::parallel::ScopeStdMutex::unlock()
{
        mutex.unlock();
}
