/*********************************************************************
 * 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.3 $
 *********************************************************************/

#ifndef _SIGNALHANDLER_H
#define _SIGNALHANDLER_H

#include "Runnable.h"
#include "Mutex.h"

#include <signal.h>

namespace util
{
	/**
	 * SignalHandler is a class for easily assigning handlers for
	 * different types of signals. SignalHandler works by creating a
	 * thread that waits for the signals for which a handler has been
	 * set. The signals that SignalHandler is waiting for must be
	 * blocked by all other threads. This is done automatically when
	 * adding new handlers. When a signal is caught, the corresponding
	 * handler is invoked.<p>
	 *
	 * Example
	 * <pre>
	 * class MyHandler : public SignalHandler
	 * {
	 * public:
	 *   void handleSignal(int signal)
	 *   {
	 *     switch (signal)
	 *       {
	 *       case SIGINT: break;
	 *       case SIGUSR1: break;
	 *       }
	 *   }
	 * };
	 *
	 * ...
	 *
	 * MyHandler myHandler;
	 * SignalHandler::addHandler(SIGINT, &myHandler);  //myHandler handles SIGINT
	 * SignalHandler::addHandler(SIGUSR1, &myHandler); //... and SIGUSR1
	 * SignalHandler::start();
	 * </pre>
	 *
	 * It is advisable that you create the needed signal handlers before
	 * any threads are created in you process to ensure the signal masks
	 * are set correctly.
	 **/
	class SignalHandler : virtual public Object
	{
	public:
		SignalHandler();
		virtual ~SignalHandler();
		/**
		 * Set a handler for <i>signal</i>. The signal numbers are
		 * specified in signal.h (see the Unix manual). Typical examples
		 * include SIGINT (Ctrl-C), SIGUSR1 (user signal 1) and the like.
		 * If a handler is added after the start() method has been called,
		 * the newly added signal will be caught only after some other,
		 * previously added signal has been caught. The handler of a
		 * signal can, however, be changed with no problems.
		 *
		 * @exception InvalidArgumentException& if <i>signal</i> is not a
		 * valid signal number
		 **/
		static void setHandler(int signal, SignalHandler* handler) throw (InvalidArgumentException&);
		/**
		 * Remove a signal handler. If a handler is removed after the
		 * start() method has been called, the signal is still delivered
		 * to this SignalHandler. In this case, SignalHandler resends the
		 * signal to the owner process.
		 *
		 * @exception InvalidArgumentException& if <i>signal</i> is not a
		 * valid signal number
		 **/
		static void removeHandler(int signal) throw (InvalidArgumentException&);

		/**
		 * Block all signals from the calling process. The calling process
		 * and all threads created by it after this call will not see any
		 * signals.
		 **/
		static void blockSignals();
		
		/**
		 * Unblock all signals from the calling process. The calling
		 * process and all threads created by it after this call will see
		 * all signals.
		 **/
		static void unblockSignals();

		/**
		 * Block a signal from the calling process. The calling process
		 * and all threads created by it after this call will not see the
		 * specified signal.
		 *
		 * @exception InvalidArgumentException& if <i>sig</i> is not a
		 * valid signal number
		 **/
		static void blockSignal(int sig) throw (InvalidArgumentException&);

		/**
		 * Unblock a signal from the calling process. The calling process
		 * and all threads created by it after this call will see the
		 * specified signal in addition to other unblocked signals.
		 *
		 * @exception InvalidArgumentException& if <i>sig</i> is not a
		 * valid signal number
		 **/
		static void unblockSignal(int sig) throw (InvalidArgumentException&);

		/**
		 * Start the signal handling. This method starts a thread that
		 * waits for the signals specified by setHandler() calls. Only one
		 * such thread can be active, and the signals must be blocked by
		 * other threads.
		 **/
		static void start();

		/**
		 * Halt the signal handler. Actually, the signal handler exits
		 * only after one of the signals it has been waiting for is
		 * issued. Thus, calling stop() and start() again is not a good
		 * idea, unless you explicitly send a signal that causes the
		 * handler to exit.
		 **/
		static void stop();
		
		/**
		 * This function is invoked by the SignalHandler when a signal is
		 * caught on which a handler is registered.
		 *
		 * @param signal the number of the signal that was caught
		 **/
		virtual void handleSignal(int signal) = 0;
	private:
		class HandlerThread;
		friend class HandlerThread;

		static int _iInstanceCount;
		static sigset_t _signalSet;
		static Hashtable<int,SignalHandler*> _tblHandlers;
		static HandlerThread* _handler;
		static Mutex _handlerMutex;
	};

	class SignalHandler::HandlerThread : public Thread
	{
	public:
		void run(void);
		void stop(void) { _bRunning = false; }
	};
}

#endif
