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

#include "Mutex.h"
#include <errno.h>
#include <string.h>

namespace util
{
	Mutex::Mutex(int type) throw (MutexException&) : _bCopy(false) 
	{
		pthread_mutexattr_t attr;
		checkError(pthread_mutexattr_init(&attr),"Mutex::Mutex()");
		checkError(pthread_mutexattr_settype(&attr,type),"Mutex::Mutex()");
		checkError(pthread_mutex_init(&_mutex,&attr),"Mutex::Mutex()");
		checkError(pthread_mutexattr_destroy(&attr),"Mutex::Mutex()");
	}

	Mutex::~Mutex()
	{
		if (!_bCopy)
			pthread_mutex_destroy(&_mutex);
	}

	Mutex& Mutex::operator= (const Mutex& other)
	{
		if (&other == this || !memcmp(&other._mutex, &_mutex, sizeof(pthread_mutex_t)))
			return *this;

		if (!_bCopy)
			pthread_mutex_destroy(&_mutex);

		_mutex = other._mutex;
		_bCopy = true;
		return *this;
	}

	void Mutex::lock(void) throw (MutexException&)
	{
		checkError(pthread_mutex_lock(&_mutex), "Mutex::lock()");
	}

	void Mutex::tryToLock(void) throw (MutexException&)
	{
		checkError(pthread_mutex_trylock(&_mutex),"Mutex::tryToLock()");
	}
	
	void Mutex::unlock(void) throw (MutexException&)
	{
		checkError(pthread_mutex_unlock(&_mutex),"Mutex::unlock()");
	}

	void Mutex::checkError(int code, char* prefix) throw (MutexException&)
	{
		char bfr[256], *suffix;
		strcpy(bfr,prefix);
		strcat(bfr,": ");
		
		switch (code)
			{
			case 0: return;
			case EBUSY: suffix = "mutex is locked"; break;
			case EDEADLK: suffix = "the current thread has locked this mutex"; break;
#ifdef sun
			case EOWNERDEAD:
			case ENOTRECOVERABLE:	suffix = "the previous owner of the mutex is dead"; break;
#endif
			case EAGAIN: suffix = "no more mutexes allowed"; break;
			case ENOMEM: suffix = "not enough memory"; break;
			case EPERM: suffix = "initialization not allowed"; break;
			case EINVAL: suffix = "invalid mutex arguments"; break;
			default: suffix = "unrecognized mutex error"; break;
			}
		strcat(bfr,suffix);
		throw MutexException(bfr);
	}

	AutoMutex::AutoMutex(Mutex& mutex) : _mutex(mutex)
	{
		try {	_mutex.lock(); } catch (MutexException& me) {}
	}

	AutoMutex::~AutoMutex()
	{
		try {	_mutex.unlock(); } catch (MutexException& me) {}
	}
}
