/*------------------------------------------------------------------------------
* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team
* 
* Distributable under the terms of either the Apache License (Version 2.0) or 
* the GNU Lesser General Public License, as specified in the COPYING file.
------------------------------------------------------------------------------*/
#include "CLucene/StdHeader.h"

#ifndef _CL_DISABLE_MULTITHREADING
CL_NS_DEF(util)


mutexGuard::mutexGuard(const mutexGuard& clone){
	//no autoclone
	mrMutex = NULL;
}
mutexGuard::mutexGuard( _LUCENE_THREADMUTEX& rMutex ) :
	mrMutex(&rMutex)
{
	mrMutex->lock();
}
mutexGuard::~mutexGuard()
{
	mrMutex->unlock();
}

#if defined(_LUCENE_DONTIMPLEMENT_THREADMUTEX)
	//do nothing
	#if defined(_LUCENE_PRAGMA_WARNINGS)
	 #pragma message ("==================Not implementing any thread mutex==================")
	#else
	 #warning "==================Not implementing any thread mutex=================="
	#endif



#elif defined(_CL_HAVE_WIN32_THREADS)
	#include "CLucene/config/threadCSection.h"

   #if !defined(LUCENE_USE_WINDOWS_H) && !defined(_WINDOWS_)
      //we have not explicity included windows.h and windows.h has
      //not been included (check _WINDOWS_), then we must define
      //our own definitions to the thread locking functions:
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
      extern "C" __declspec(dllimport) void __stdcall InitializeCriticalSectionEx(CRITICAL_SECTION *, DWORD, DWORD);
#else
      extern "C" __declspec(dllimport) void __stdcall InitializeCriticalSection(CRITICAL_SECTION *);
#endif
      extern "C" __declspec(dllimport) void __stdcall EnterCriticalSection(CRITICAL_SECTION *);
      extern "C" __declspec(dllimport) void __stdcall LeaveCriticalSection(CRITICAL_SECTION *);
      extern "C" __declspec(dllimport) void __stdcall DeleteCriticalSection(CRITICAL_SECTION *);
	  extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId();
   #endif
	mutex_win32::mutex_win32(const mutex_win32& clone){
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
        InitializeCriticalSectionEx(&mtx, 0, 0);
#else
        InitializeCriticalSection(&mtx);
#endif
	}
	mutex_win32::mutex_win32()
	{ 
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
        InitializeCriticalSectionEx(&mtx, 0, 0);
#else
        InitializeCriticalSection(&mtx);
#endif
    }
	
	mutex_win32::~mutex_win32()
	{ 
		DeleteCriticalSection(&mtx); 
	}
	
	void mutex_win32::lock()
	{ 
		EnterCriticalSection(&mtx); 
	}
	
	void mutex_win32::unlock()
	{ 
		LeaveCriticalSection(&mtx); 
	}



#elif defined(_CL_HAVE_PTHREAD)
	#include "CLucene/config/threadPthread.h"
		
	#ifdef _CL_HAVE_PTHREAD_MUTEX_RECURSIVE
		bool mutex_pthread_attr_initd=false;
		pthread_mutexattr_t mutex_pthread_attr;
	#endif
	
	#ifdef _CL__CND_DEBUG
		#define _CLPTHREAD_CHECK(c,m) CND_PRECONDITION(c==0,m)
	#else
		#define _CLPTHREAD_CHECK(c,m) c;
	#endif

	mutex_pthread::mutex_pthread(const mutex_pthread& clone){
		#ifdef _CL_HAVE_PTHREAD_MUTEX_RECURSIVE
		_CLPTHREAD_CHECK(pthread_mutex_init(&mtx, &mutex_pthread_attr), "mutex_pthread(clone) constructor failed")
		#else
	  	#if defined(__hpux) && defined(_DECTHREADS_)
			_CLPTHREAD_CHECK(pthread_mutex_init(&mtx, pthread_mutexattr_default), "mutex_pthread(clone) constructor failed")
		#else
			_CLPTHREAD_CHECK(pthread_mutex_init(&mtx, 0), "mutex_pthread(clone) constructor failed")
		#endif
		lockCount=0;
		lockOwner=0;
		#endif
	}
	mutex_pthread::mutex_pthread()
	{ 
		#ifdef _CL_HAVE_PTHREAD_MUTEX_RECURSIVE
	  	if ( mutex_pthread_attr_initd == false ){
	  		pthread_mutexattr_init(&mutex_pthread_attr);
		  	pthread_mutexattr_settype(&mutex_pthread_attr, PTHREAD_MUTEX_RECURSIVE);
		  	mutex_pthread_attr_initd = true;
		}
		_CLPTHREAD_CHECK(pthread_mutex_init(&mtx, &mutex_pthread_attr), "mutex_pthread(clone) constructor failed")
		#else
	  	#if defined(__hpux) && defined(_DECTHREADS_)
			_CLPTHREAD_CHECK(pthread_mutex_init(&mtx, pthread_mutexattr_default), "mutex_pthread(clone) constructor failed")
		#else
			_CLPTHREAD_CHECK(pthread_mutex_init(&mtx, 0), "mutex_pthread(clone) constructor failed")
		#endif
		lockCount=0;
		lockOwner=0;
		#endif
	}
	
	mutex_pthread::~mutex_pthread()
	{ 
		_CLPTHREAD_CHECK(pthread_mutex_destroy(&mtx), "~mutex_pthread destructor failed") 
	}
	
	void mutex_pthread::lock()
	{ 
		#ifndef _CL_HAVE_PTHREAD_MUTEX_RECURSIVE
		pthread_t currentThread = pthread_self();
		if( pthread_equal( lockOwner, currentThread ) ) {
			++lockCount;
		} else {
			_CLPTHREAD_CHECK(pthread_mutex_lock(&mtx), "mutex_pthread::lock")
			lockOwner = currentThread;
			lockCount = 1;
		}
		#else
		_CLPTHREAD_CHECK(pthread_mutex_lock(&mtx), "mutex_pthread::lock")
		#endif
	}
	
	void mutex_pthread::unlock()
	{ 
		#ifndef _CL_HAVE_PTHREAD_MUTEX_RECURSIVE
		--lockCount;
		if( lockCount == 0 )
		{
			lockOwner = 0;
			_CLPTHREAD_CHECK(pthread_mutex_unlock(&mtx), "mutex_pthread::unlock")
		}
		#else
		_CLPTHREAD_CHECK(pthread_mutex_unlock(&mtx), "mutex_pthread::unlock")
		#endif
	}

#endif //thread impl choice


CL_NS_END
#endif //!_CL_DISABLE_MULTITHREADING
