#include "FileZilla.h"
#include "ipcmutex.h"
#include "filezillaapp.h"

#ifndef __WXMSW__
int CInterProcessMutex::m_fd = -1;
int CInterProcessMutex::m_instanceCount = 0;
#endif

std::list<CReentrantInterProcessMutexLocker::t_data> CReentrantInterProcessMutexLocker::m_mutexes;

CInterProcessMutex::CInterProcessMutex(enum t_ipcMutexType mutexType, bool initialLock /*=true*/)
{
	m_locked = false;
#ifdef __WXMSW__
	// Create mutex_
	hMutex = ::CreateMutex(0, false, wxString::Format(_T("FileZilla 3 Mutex Type %d"), mutexType));
#else
	if (!m_instanceCount)
	{
		// Open file only if this is the first instance
		wxFileName fn(wxGetApp().GetSettingsDir(), _T("lockfile"));
		m_fd = open(fn.GetFullPath().mb_str(), O_CREAT | O_RDWR, 0644);
	}
	m_instanceCount++;
#endif
	m_type = mutexType;
	if (initialLock)
		Lock();
}

CInterProcessMutex::~CInterProcessMutex()
{
	if (m_locked)
		Unlock();
#ifdef __WXMSW__
	if (hMutex)
		::CloseHandle(hMutex);
#else
	m_instanceCount--;
	// Close file only if this is the last instance. At least under
	// Linux, closing the lock file has the affect of removing all locks.
	if (!m_instanceCount && m_fd >= 0)
		close(m_fd);
#endif
}

void CInterProcessMutex::Lock()
{
	wxASSERT(!m_locked);
#ifdef __WXMSW__
	if (hMutex)
		::WaitForSingleObject(hMutex, INFINITE);
#else
	if (m_fd >= 0)
	{
		// Lock 1 byte region in the lockfile. m_type specifies the byte to lock.
		struct flock f = {0};
		f.l_type = F_WRLCK;
		f.l_whence = SEEK_SET;
		f.l_start = m_type;
		f.l_len = 1;
		f.l_pid = getpid();
		fcntl(m_fd, F_SETLKW, &f);
	}
#endif

	m_locked = true;
}

bool CInterProcessMutex::TryLock()
{
	wxASSERT(!m_locked);

#ifdef __WXMSW__
	if (!hMutex)
	{
		m_locked = false;
		return false;
	}

	int res = ::WaitForSingleObject(hMutex, 1);
	if (res == WAIT_OBJECT_0)
	{
		m_locked = true;
		return true;
	}
#else
	if (m_fd >= 0)
	{
		// Try to lock 1 byte region in the lockfile. m_type specifies the byte to lock.
		struct flock f = {0};
		f.l_type = F_WRLCK;
		f.l_whence = SEEK_SET;
		f.l_start = m_type;
		f.l_len = 1;
		f.l_pid = getpid();
		if (!fcntl(m_fd, F_SETLK, &f))
		{
			m_locked = true;
			return true;
		}
	}
#endif

	return false;
}

void CInterProcessMutex::Unlock()
{
	wxASSERT(m_locked);
	m_locked = false;

#ifdef __WXMSW__
	if (hMutex)
		::ReleaseMutex(hMutex);
#else
	if (m_fd >= 0)
	{
		// Unlock region specified by m_type.
		struct flock f = {0};
		f.l_type = F_UNLCK;
		f.l_whence = SEEK_SET;
		f.l_start = m_type;
		f.l_len = 1;
		f.l_pid = getpid();
		fcntl(m_fd, F_SETLKW, &f);
	}
#endif
}

CReentrantInterProcessMutexLocker::CReentrantInterProcessMutexLocker(enum t_ipcMutexType mutexType)
{
	m_type = mutexType;

	std::list<t_data>::iterator iter;
	for (iter = m_mutexes.begin(); iter != m_mutexes.end(); iter++)
	{
		if (iter->pMutex->GetType() == mutexType)
			break;
	}
	
	if (iter != m_mutexes.end())
	{
		iter->lockCount++;
	}
	else
	{
		t_data data;
		data.lockCount = 1;
		data.pMutex = new CInterProcessMutex(mutexType);
		m_mutexes.push_back(data);
	}
}

CReentrantInterProcessMutexLocker::~CReentrantInterProcessMutexLocker()
{
	std::list<t_data>::iterator iter;
	for (iter = m_mutexes.begin(); iter != m_mutexes.end(); iter++)
	{
		if (iter->pMutex->GetType() == m_type)
			break;
	}
	
	wxASSERT(iter != m_mutexes.end());
	if (iter == m_mutexes.end())
		return;

	if (iter->lockCount == 1)
	{
		delete iter->pMutex;
		m_mutexes.erase(iter);
	}
	else
		iter->lockCount--;
}


syntax highlighted by Code2HTML, v. 0.9.1