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

#include "SignalHandler.h"

#include <unistd.h>

#include <iostream>

using namespace std;

namespace util
{
	int SignalHandler::_iInstanceCount = 0;
	sigset_t SignalHandler::_signalSet;
	Hashtable<int,SignalHandler*> SignalHandler::_tblHandlers;
	SignalHandler::HandlerThread* SignalHandler::_handler = NULL;
	Mutex SignalHandler::_handlerMutex;
	
	void SignalHandler::HandlerThread::run()
	{
		SmartPtr<HandlerThread> autoDestroyer(this);
		int sig;
		while (_bRunning)
			{
				//Wait for the wanted signals
				int err = sigwait(&SignalHandler::_signalSet, &sig);
				if (!_bRunning)
					break;
				if (err)
					{
						std::cerr << "sigwait failed. Signal delivery aborted." << std::endl;
						return;
					}
				//Synhronize on the handler table
				AutoMutex mutex(SignalHandler::_handlerMutex);
				SignalHandler** handler = SignalHandler::_tblHandlers[sig];
				//A handler has been removed
				if (!handler)
					{
						//Send the signal to the parent process
						::kill(getpid(),sig);
						return;
					}
				(*handler)->handleSignal(sig);
			}
	}

	SignalHandler::SignalHandler()
	{
		_iInstanceCount++;
		if (_iInstanceCount == 1)
			unblockSignals();
	}

	SignalHandler::~SignalHandler()
	{
		_iInstanceCount--;
		if (!_iInstanceCount)
			{
				stop();
				unblockSignals();
			}
	}

	void SignalHandler::start()
	{
		if (!_handler)
			_handler = new HandlerThread;
	}

	void SignalHandler::stop()
	{
		if (_handler)
			{
				_handler->stop();
				_handler = NULL;
			}
	}

	void SignalHandler::blockSignals()
	{
		sigfillset(&_signalSet);
		pthread_sigmask(SIG_SETMASK,&_signalSet,NULL);
	}

	void SignalHandler::unblockSignals()
	{
		sigemptyset(&_signalSet);
		pthread_sigmask(SIG_SETMASK,&_signalSet,NULL);
	}

	void SignalHandler::blockSignal(int signal) throw (InvalidArgumentException&)
	{
		if (sigaddset(&_signalSet,signal))
			throw InvalidArgumentException("SignalHandler::blockSignal(int): Invalid signal number.");
		pthread_sigmask(SIG_SETMASK,&_signalSet,NULL);
	}

	void SignalHandler::unblockSignal(int signal) throw (InvalidArgumentException&)
	{
		if (sigdelset(&_signalSet,signal))
			throw InvalidArgumentException("SignalHandler::unblockSignal(int): Invalid signal number.");
		pthread_sigmask(SIG_SETMASK,&_signalSet,NULL);
	}

	void SignalHandler::setHandler(int signal, SignalHandler* handler) throw (InvalidArgumentException&)
	{
		blockSignal(signal);
		AutoMutex mutex(_handlerMutex);
		_tblHandlers.put(signal,handler);
	}

	void SignalHandler::removeHandler(int signal) throw (InvalidArgumentException&)
	{
		unblockSignal(signal);
		AutoMutex mutex(_handlerMutex);
		_tblHandlers.remove(signal);
	}
}
