/*
    libmaus2
    Copyright (C) 2009-2020 German Tischler-Höhle
    Copyright (C) 2011-2014 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/>.
*/
#if ! defined(LIBMAUS2_PARALLEL_SIMPLETHREADPOOLTHREAD_HPP)
#define LIBMAUS2_PARALLEL_SIMPLETHREADPOOLTHREAD_HPP

#include <libmaus2/parallel/SimpleThreadPoolInterface.hpp>
#include <libmaus2/parallel/SimpleThreadWorkPackage.hpp>
#include <libmaus2/parallel/SimpleThreadWorkPackageDispatcher.hpp>

#include <libmaus2/parallel/StdThread.hpp>

#if defined(__linux__)
#include <unistd.h>
#include <sys/syscall.h>
#endif

namespace libmaus2
{
	namespace parallel
	{
		struct SimpleThreadPoolThreadCallable : libmaus2::parallel::StdThreadCallable
		{
			typedef SimpleThreadPoolThreadCallable this_type;
			typedef std::unique_ptr<this_type> unique_ptr_type;
			typedef std::shared_ptr<this_type> shared_ptr_type;

			SimpleThreadPoolInterface & tpi;

			typedef libmaus2::parallel::StdMutex pack_lock_type;

			pack_lock_type curpacklock;
			libmaus2::parallel::SimpleThreadWorkPackage * curpack;

			uint64_t const threadid;

			SimpleThreadPoolThreadCallable(SimpleThreadPoolInterface & rtpi, uint64_t const rthreadid) : tpi(rtpi), curpack(0), threadid(rthreadid) {}
			virtual ~SimpleThreadPoolThreadCallable() {}

			libmaus2::parallel::SimpleThreadWorkPackage * getCurrentPackage()
			{
				pack_lock_type::scope_lock_type lcurpacklock(curpacklock);
				return curpack;
			}

			void setCurrentPackage(libmaus2::parallel::SimpleThreadWorkPackage * pack)
			{
				pack_lock_type::scope_lock_type lcurpacklock(curpacklock);
				curpack = pack;
			}

			void run()
			{
				try
				{
					// notify pool this thread is now running
					tpi.notifyThreadStart();

					while ( true )
					{
						libmaus2::parallel::SimpleThreadWorkPackage * P = nullptr;
						SimpleThreadWorkPackageDispatcher * D = nullptr;

						// throws exception if package queue is in terminated state
						P = tpi.getPackage();

						try
						{
							setCurrentPackage(P);
							D = tpi.getDispatcher(P);
							D->dispatch(P,tpi);
							setCurrentPackage(nullptr);
						}
						catch(libmaus2::exception::LibMausException const & ex)
						{
							tpi.panic(ex);
						}
						catch(std::exception const & ex)
						{
							tpi.panic(ex);
						}
						catch(...)
						{
							tpi.panic(std::runtime_error("[E] unknown exceptin caught in SimpleThreadPoolThread::run"));
						}
					}
				}
				// catch exception thrown for terminated queue
				catch(std::exception const & ex)
				{
					// std::cerr << ex.what() << std::endl;
				}
			}
		};
	}
}
#endif
