
#include "stdafx.h"

#include "aplAggr.h"


#ifdef __linux__

#include <unistd.h>
#include <sys/syscall.h>
// описание функции: pid_t GetCurrentThreadId();
#define GetCurrentThreadId() syscall(SYS_gettid)

#else 
    #ifdef __GNUC__
	//#include <wtypes.h> - конфликтует с типами из aplWinDefineForGCC.h
	typedef void *HWND;

	#include <winbase.h>
	#include <handleapi.h>
	#include <winnt.h>
	#include <synchapi.h>

	#include <processthreadsapi.h>

	#endif // #ifndef _MFC_VER
#endif // #ifdef __linux__

#ifdef __linux__
	#define POINTER_FORMAT  _T("0x%llX")
	#define THREAD_FORMAT   _T("t%04i")
#else
	#if defined(LP64) || defined(_LP64)
		#define POINTER_FORMAT  _T("0x%llX")
	#else
		#define POINTER_FORMAT  _T("0x%08X")
	#endif

	#define THREAD_FORMAT   _T("t0x%04X")
#endif

// вывод в консоль отладочных сообщений критических секций
//#define LOG_CRITICAL_SECT

CAplCriticalSection::CAplCriticalSection()
{
	m_inited = false;
#ifdef __linux__
	//m_mutex =  PTHREAD_MUTEX_INITIALIZER; // быстрый мьютекс
	//m_mutex =  PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; // рекурсивный мьютекс
	m_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; // мьютекс проверки на ошибки
	// Третий вариант самый рабочий вариант. Рекурсивный мьютекс при схеме: 1я нить захватила - 2я нить
	// в ожидании - 1я еще раз захватила - 1я освободила - 1я освободила приводит к вечному ожиданию
	// во второй нити
	m_recurs_level = 0;
#else
	InitializeCriticalSection(&m_sect);
#endif
	m_inited = true;
}

void CAplCriticalSection::Init()
{
#ifdef __linux__
/* В линуксе сложная динамическая инициализация почему-то иногда выкидывает ошибку.
 * Вместо нее использована статическая инициализация в конструкторе макросом PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
	int error_id;
	pthread_mutexattr_t    attr;
	if((error_id = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) != 0)
	{
		CString report;
		report.Format(_T("Ошибка назначения атрибута мутекса для критической секции! Блокировки отключены!")
					  _T(" this ") POINTER_FORMAT  _T(" thread ") THREAD_FORMAT,
					  this, GetCurrentThreadId());
		aplMessagePrint(report, MB_ICONERROR);
		return;
	}

	//Инициализация мьютекса
	if((error_id = pthread_mutex_init(&m_mutex, &attr)) != 0)
	{
		CString report;
		report.Format(_T("Ошибка назначения мутекса для критической секции! Блокировки отключены!")
					  _T(" this ") POINTER_FORMAT  _T(" thread ") THREAD_FORMAT,
					  this, GetCurrentThreadId());
		CString str1;
		switch(error_id)
		{
		case EAGAIN: report += _T("Нехватка ресурсов (не памяти) для создания мьютекса"); break;
		case ENOMEM: report += _T("нехватка памяти для инициализации"); break;
		case EPERM: report += _T("нет разрешения на создание"); break;
		case EBUSY: report += _T("попытка повторной инициализации не уничтоженного мьютекса."); break;
		case EINVAL: report += _T("неверные атрибуты функции."); break;
		default: report += _T("Неизвестная ошибка. ");
			aplGetDescriptionSystemError(error_id, str1);
			report +=str1;
			break;
		}
		aplMessagePrint(report, MB_ICONERROR);
		_count_error++;
		return;
	}
*/
/*	Код оставлен как пример самостоятельного получения описания ошибки
		CString str;
		switch(ret)
		{
		case EAGAIN: str = _T("Нехватка ресурсов (не памяти) для создания мьютекса"); break;
		case ENOMEM: str = _T("нехватка памяти для инициализации"); break;
		case EPERM: str = _T("нет разрешения на создание"); break;
		case EBUSY: str = _T("попытка повторной инициализации не уничтоженного мьютекса."); break;
		case EINVAL: str = _T("неверные атрибуты функции."); break;
		default: str = _T("Неизвестная ошибка."); break;
		}
		APL_THROW_EX(ret, str);
*/
	m_recurs_level = 0;
#endif

#ifdef LOG_CRITICAL_SECT
	CString report;
	report.Format(_T("::CAplCriticalSection() ++ this ") POINTER_FORMAT  _T(" thread ") THREAD_FORMAT,
				  this, GetCurrentThreadId());
	aplMessagePrint(report);
#endif
	m_inited = true;
}

CAplCriticalSection::~CAplCriticalSection()
{
	//Уничтожение мьютекса
	if(!m_inited)
		return;
#ifdef __linux__
	pthread_mutex_destroy(&m_mutex);
#else
	DeleteCriticalSection(&m_sect);
#endif
	m_inited=false;

#ifdef LOG_CRITICAL_SECT
	CString report;
	report.Format(_T("::~CAplCriticalSection() this ") POINTER_FORMAT  _T(" thread ") THREAD_FORMAT,
				  this, GetCurrentThreadId());
	aplMessagePrint(report);
#endif

}

void CAplCriticalSection::EnterAplCriticalSection()
{
	if(!m_inited)
		Init();

	if(!m_inited)
		return;

#ifdef LOG_CRITICAL_SECT
	CString report;
	report.Format(_T("::EnterAplCriticalSection() this ") POINTER_FORMAT  _T(" thread ") THREAD_FORMAT,
				  this, GetCurrentThreadId());
	aplMessagePrint(report);
#endif


#ifdef __linux__
	pthread_mutex_lock(&m_mutex);
	m_recurs_level++;
#else
	EnterCriticalSection(&m_sect);
#endif
}

void CAplCriticalSection::LeaveAplCriticalSection()
{
	if(!m_inited)
		return;

#ifdef LOG_CRITICAL_SECT
	CString report;
	report.Format(_T("::LeaveAplCriticalSection() this ") POINTER_FORMAT  _T(" thread ") THREAD_FORMAT,
				  this, GetCurrentThreadId());
	aplMessagePrint(report);
#endif


#ifdef __linux__
	m_recurs_level--;
	if(m_recurs_level==0)
		pthread_mutex_unlock(&m_mutex);
#else
	LeaveCriticalSection(&m_sect);
#endif
}


CAplAtomicLong::CAplAtomicLong(long new_val)
{
	m_val = new_val;
}

CAplAtomicLong::~CAplAtomicLong()
{
}

long CAplAtomicLong::Set(long new_val)
{
#ifdef __linux__
	m_cr_sect.EnterAplCriticalSection();
	long prev_val = m_val;
	m_val = new_val;
	m_cr_sect.LeaveAplCriticalSection();
	return prev_val;
#else
	return InterlockedExchange(&m_val, new_val);
#endif
}

long CAplAtomicLong::Get() const
{
#ifdef __linux__
	m_cr_sect.EnterAplCriticalSection();
	long prev_val = m_val;
	m_cr_sect.LeaveAplCriticalSection();
	return prev_val;
#else
	return InterlockedExchangeAdd(&m_val, 0);
#endif

}

long CAplAtomicLong::Increment()
{
#ifdef __linux__
	m_cr_sect.EnterAplCriticalSection();
	long ret_val = m_val++;
	m_cr_sect.LeaveAplCriticalSection();
	return ret_val;
#else
	return InterlockedIncrement(&m_val);
#endif

}

long CAplAtomicLong::Decrement()
{
#ifdef __linux__
	long ret_val = 0;
	m_cr_sect.EnterAplCriticalSection();
	ret_val = m_val--;
	m_cr_sect.LeaveAplCriticalSection();
	return ret_val;
#else
	return InterlockedDecrement(&m_val);
#endif
}

long CAplAtomicLong::Add(long add_val)
{
#ifdef __linux__
	long prev_val = 0;
	m_cr_sect.EnterAplCriticalSection();
	prev_val = m_val;
	m_val+=add_val;
	m_cr_sect.LeaveAplCriticalSection();
	return prev_val;
#else
	return InterlockedExchangeAdd(&m_val, add_val);
#endif
}
