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

#ifndef _MUTEX_H
#define _MUTEX_H

#include "Exception.h"
#include <pthread.h>

namespace util
{
	/**
	 * MutexException is thrown if an error occurs when handling
	 * mutexes.
	 **/
 	class MutexException : public Exception
	{
	public:
		MutexException(std::string message) : Exception(message) {}
	};

	/**
	 * Mutual exclusion lock. Concurrent threads that must synchronize
	 * their access to shared variables need to use a mutex lock to
	 * prevent mixups.
	 **/
	class Mutex : virtual public Object
	{
	public:
		/**
		 * Create a new mutual exclusion lock. The type of the mutex can
		 * be one of the predefined constants in pthread.h, i.e.
		 * PTHREAD_MUTEX_{NORMAL,RECURSIVE,ERRORCHECK,DEFAULT}. See the
		 * unix man page "pthread_mutex_settype" for details.
		 **/
		Mutex(int type=PTHREAD_MUTEX_RECURSIVE) throw (MutexException&);
		/**
		 * Create a copy of a mutex.
		 **/
		Mutex(const Mutex& other) : _mutex(other._mutex), _bCopy(true) {}
		
		~Mutex();

		/**
		 * Lock a mutex. When a mutex is locked by a thread, a locking
		 * attempt by another thread cannot succeed until the lock is
		 * released. This method blocks until the mutex is available.
		 * @exception MutexException& if an error occurs
		 **/
		void lock(void) throw (MutexException&);
		/**
		 * Lock a mutex. This method is identical to lock except that the
		 * call terminates immediately if the mutex is alredy locked.
		 * @exception MutexException if the mutex is locked or an error occurs
		 **/
		void tryToLock(void) throw (MutexException&);
		/**
		 * Release the mutex.
		 * @exception MutexException& if an error occurs
		 **/
		void unlock(void) throw (MutexException&);

		/**
		 * Copy a mutex.
		 **/
		Mutex& operator= (const Mutex& other);

	private:
		pthread_mutex_t _mutex;
		bool _bCopy;
		
		void checkError(int, char*) throw (MutexException&);
	};

	/**
	 * AutoMutex is a class for automatically locking and unlocking a
	 * mutex. It comes in handy when there are many places where a mutex
	 * should be unlocked and especially with exceptions. AutoMutex
	 * locks a mutex when it is created and unlocks it upon deletion.
	 * <p>
	 * Example:
	 * <pre>
	 * ...
	 * int foobar(void)
	 * {
	 *   //mutex is defined elsewhere. Locally defined mutexes make no sense
	 *   AutoMutex auto(mutex); //lock, enter protected area
	 *   if (something)
	 *     return 0; //no need to unlock, AutoMutex takes care
	 *   if (something_else)
	 *     throw 0; //same here
	 *   return 1; //and here
	 * }
	 * </pre>
	 **/
	class AutoMutex : virtual public Object
	{
	public:
		/**
		 * Create a new AutoMutex, lock the mutex.
		 **/
		AutoMutex(Mutex& mutex);
		/**
		 * Destroy an AutoMutex, unlock the mutex.
		 **/
		~AutoMutex();

	private:
		Mutex& _mutex;
	};
}

#endif
