/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.9 $
 *********************************************************************/

#ifndef _RUNNABLE_H
#define _RUNNABLE_H

#ifndef _REENTRANT
#define _REENTRANT
#endif

#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

#include "Exception.h"
#include "Mutex.h"
#include "List.h"

namespace util
{
	/**
	 * RunException is thrown when a Runnable code cannot be started for
	 * some reason.
	 **/
 	class RunException : public Exception
	{
	public:
		RunException(std::string message) : Exception(message) {}
	};

	/**
	 * The abstract base class for all parallel processable things like
	 * processes, daemon processes and threads.
	 **/
	class Runnable : virtual public Object
	{
	public:
		virtual ~Runnable() {}
		/**
		 * Start the parallel processing. A new thread, process or daemon
		 * is created and its run method is called.
		 * @exception RunException& if the parallel processing code cannot
		 *            be executed
		 **/
		virtual void start(void) throw (RunException&) = 0;
		//bool isRunning(void) { return running; }
		
	protected:
		/**
		 * Subclasses must redefine this method to include the code that
		 * constitutes the actual parallel processing. When this method
		 * exists, the Runnable (thread, process or daemon) will be killed.
		 **/
		virtual void run(void) = 0;
		//bool running;
	};

	/**
	 * Process is an object the can be used to easily create a new
	 * process. The new process is created using the fork system call.
	 * Note that the new process works in its own environment.
	 * Therefore, communication between processes cannot be made using
	 * variables, but shared memory or pipes or the like must be used.
	 **/
	class Process : public Runnable
	{
	public:
		Process();

		void start(void) throw (RunException&);
	};

	/**
	 * Daemon is a process that works with no controlling terminal. That
	 * is, the standard input and output streams cannot be used (or they
	 * must be reassigned). Daemon also changes its process group
	 * thereby placing itself into "background". Killing a parent
	 * process does not affect the daemon processes created by it.
	 **/
	class Daemon : public Runnable
	{
	public:
		Daemon();
		
		void start(void) throw (RunException&);
	};

	class Thread;

	/**
	 * ThreadListener is an interface for classes that must know when a
	 * thread has finished its job.
	 **/
	class ThreadListener : virtual public Object
	{
	public:
		/**
		 * When a thread is finished, this method is called with a pointer
		 * to the thread as its only argument. Actually, the call is still
		 * invoked from a separate thread, but just after the run() method
		 * of a thread returns.
		 **/
		virtual void threadDied(Thread* t) = 0;
	};

	/**
	 * The Thread class is a convenient way of creating concurrently
	 * executing code that can use the same variables. A thread works in
	 * the same environment as its parent. Mutexes must be used to
	 * protect access to shared variables.
	 **/
	class Thread : public Runnable
	{
	public:
		/**
		 * Create a new thread.
		 * @param detachState can be either PTHREAD_CREATE_JOINABLE or
		 *        PTHREAD_CREATE_DETACHED. In the latter case, a started
		 *        thread cannot be joined or detached.
		 **/
		Thread(int detachState=PTHREAD_CREATE_JOINABLE);
		~Thread();
		
		/**
		 * Start the thread.
		 * @exception RunException& if the thread is already running or
		 *            cannot be started for some other reason
		 **/
		void start(void) throw (RunException&);
		
		/**
		 * Wait this thread to die. The calling thread will be blocked
		 * until this thread exists.
		 **/
		void join(void) throw (RunException&);

		/**
		 * Detach this thread from its parent so that no one needs to
		 * "join" it to release the reserved resources.
		 **/
		void detach(void) throw (RunException&);

		/**
		 * Send a signal to a thread. The default signal to be sent is
		 * SIGKILL which - by default - causes the thread to exit. One
		 * must ensure that the resources reserved by the killed thread
		 * are released. Especially mutexes must be handled with extreme
		 * care. Typically, however, it is not safe to kill any thread.
		 * Instead, one should consider setting an internal "cancellation"
		 * flag that is checked every now and then.
		 *
		 * @param signal the signal to send ('man signal' for more info)
		 **/
		void kill(int signal = SIGKILL) throw (RunException&);

		/**
		 * Add a listener that gets notified when the thread has finished
		 * its execution.
		 * @param l the listener to add to the list of interested listeners
		 **/
		void addThreadListener(ThreadListener* l) { _lstListeners += l; }
		/**
		 * Remove a listener.
		 **/
		void removeThreadListener(ThreadListener* l) { _lstListeners -= l; }

		/**
		 * Check if the thread is currently running. Note that this method
		 * may return false when the thread is just being started. One
		 * should not rely on the return value exclusively when starting a
		 * thread, but catch the RunException& thrown by start() instead.
		 * @return true, if the thread is currently executing, false otherwise
		 **/
	 	bool isRunning(void) const { return _bRunning; }

	protected:
		/**
		 * Suspend the execution of this thread for the specified number
		 * of seconds.
		 *
		 * @exception RunException if the sleep is interrupted
		 **/
		void sleep(double secs) throw (RunException&) { sleep((unsigned int)secs, (unsigned int)((secs-int(secs))*1000000000.0)); }
		/**
		 * Suspend the execution of this thread for the specified number
		 * of seconds and nanoseconds.
		 *
		 * @exception RunException if the sleep is interrupted or
		 * <i>nanosecs</i> is greater than or equal to one billion.
		 **/
		void sleep(unsigned int secs, unsigned int nanosecs) throw (RunException&);
		
		/**
		 * A flag that indicates whether the thread is currently executing
		 * or not.
		 **/
		bool _bRunning;
		
	private:
		List<ThreadListener*> _lstListeners;

		pthread_attr_t _attr;
		pthread_t _tid;

		mutable Mutex _syncMutex;

		static void *starter(void*);
	};
}

#endif
