/**
 * Mutex.cpp
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2004-2014 Null Team
 *
 * This software is distributed under multiple licenses;
 * see the COPYING file in the main directory for licensing
 * information for this specific distribution.
 *
 * This use of this software may be subject to additional restrictions.
 * See the LEGAL file in the main directory for details.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "yateclass.h"

#ifdef _WINDOWS

typedef HANDLE HMUTEX;
typedef HANDLE HSEMAPHORE;

#else

#include <pthread.h>
#include <semaphore.h>
#include <time.h>

#ifdef MUTEX_HACK
extern "C" {
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
extern int pthread_mutexattr_settype(pthread_mutexattr_t *__attr, int __kind);
#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
#else
extern int pthread_mutexattr_settype(pthread_mutexattr_t *__attr, int __kind) __THROW;
#endif
}
#endif

typedef pthread_mutex_t HMUTEX;
typedef sem_t HSEMAPHORE;

#endif /* ! _WINDOWS */

#ifdef MUTEX_STATIC_UNSAFE
#undef MUTEX_STATIC_UNSAFE
#define MUTEX_STATIC_UNSAFE true
#else
#define MUTEX_STATIC_UNSAFE false
#endif

namespace TelEngine {

class MutexPrivate {
public:
    MutexPrivate(bool recursive, const char* name);
    ~MutexPrivate();
    inline void ref()
	{ ++m_refcount; }
    inline void deref()
	{ if (!--m_refcount) delete this; }
    inline bool recursive() const
	{ return m_recursive; }
    inline const char* name() const
	{ return m_name; }
    inline const char* owner() const
	{ return m_owner; }
    bool locked() const
    	{ return (m_locked > 0); }
    bool lock(long maxwait);
    bool unlock();
    static volatile int s_count;
    static volatile int s_locks;
private:
    HMUTEX m_mutex;
    int m_refcount;
    volatile unsigned int m_locked;
    volatile unsigned int m_waiting;
    bool m_recursive;
    const char* m_name;
    const char* m_owner;
};

class SemaphorePrivate {
public:
    SemaphorePrivate(unsigned int maxcount, const char* name, unsigned int initialCount);
    ~SemaphorePrivate();
    inline void ref()
	{ ++m_refcount; }
    inline void deref()
	{ if (!--m_refcount) delete this; }
    inline const char* name() const
	{ return m_name; }
    bool locked() const
    	{ return (m_waiting > 0); }
    bool lock(long maxwait);
    bool unlock();
    static volatile int s_count;
    static volatile int s_locks;
private:
    HSEMAPHORE m_semaphore;
    int m_refcount;
    volatile unsigned int m_waiting;
    unsigned int m_maxcount;
    const char* m_name;
};

class GlobalMutex {
public:
    GlobalMutex();
    static void init();
    static void lock();
    static void unlock();
private:
    static bool s_init;
    static HMUTEX s_mutex;
};

};

using namespace TelEngine;

HMUTEX GlobalMutex::s_mutex;
static GlobalMutex s_global;
static unsigned long s_maxwait = 0;
static bool s_unsafe = MUTEX_STATIC_UNSAFE;
static bool s_safety = false;

volatile int MutexPrivate::s_count = 0;
volatile int MutexPrivate::s_locks = 0;
volatile int SemaphorePrivate::s_count = 0;
volatile int SemaphorePrivate::s_locks = 0;
bool GlobalMutex::s_init = true;

// WARNING!!!
// No debug messages are allowed in mutexes since the debug output itself
// is serialized using a mutex!

void GlobalMutex::init()
{
    if (s_init) {
	s_init = false;
#ifdef _WINDOWS
	s_mutex = ::CreateMutex(NULL,FALSE,NULL);
#else
	pthread_mutexattr_t attr;
	::pthread_mutexattr_init(&attr);
	::pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
	::pthread_mutex_init(&s_mutex,&attr);
	::pthread_mutexattr_destroy(&attr);
#endif
    }
}

GlobalMutex::GlobalMutex()
{
    init();
}

void GlobalMutex::lock()
{
    init();
    if (s_unsafe)
	return;
#ifdef _WINDOWS
    ::WaitForSingleObject(s_mutex,INFINITE);
#else
    ::pthread_mutex_lock(&s_mutex);
#endif
}

void GlobalMutex::unlock()
{
    if (s_unsafe)
	return;
#ifdef _WINDOWS
    ::ReleaseMutex(s_mutex);
#else
    ::pthread_mutex_unlock(&s_mutex);
#endif
}


MutexPrivate::MutexPrivate(bool recursive, const char* name)
    : m_refcount(1), m_locked(0), m_waiting(0), m_recursive(recursive),
      m_name(name), m_owner(0)
{
    GlobalMutex::lock();
    s_count++;
#ifdef _WINDOWS
    // All mutexes are recursive in Windows
    m_mutex = ::CreateMutex(NULL,FALSE,NULL);
#else
    if (recursive) {
	pthread_mutexattr_t attr;
	::pthread_mutexattr_init(&attr);
	::pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
	::pthread_mutex_init(&m_mutex,&attr);
	::pthread_mutexattr_destroy(&attr);
    }
    else
	::pthread_mutex_init(&m_mutex,0);
#endif
    GlobalMutex::unlock();
}

MutexPrivate::~MutexPrivate()
{
    bool warn = false;
    GlobalMutex::lock();
    if (m_locked) {
	warn = true;
	m_locked--;
	if (s_safety)
	    s_locks--;
#ifdef _WINDOWS
	::ReleaseMutex(m_mutex);
#else
	::pthread_mutex_unlock(&m_mutex);
#endif
    }
    s_count--;
#ifdef _WINDOWS
    ::CloseHandle(m_mutex);
    m_mutex = 0;
#else
    ::pthread_mutex_destroy(&m_mutex);
#endif
    GlobalMutex::unlock();
    if (m_locked || m_waiting)
	Debug(DebugFail,"MutexPrivate '%s' owned by '%s' destroyed with %u locks, %u waiting [%p]",
	    m_name,m_owner,m_locked,m_waiting,this);
    else if (warn)
	Debug(DebugCrit,"MutexPrivate '%s' owned by '%s' unlocked in destructor [%p]",
	    m_name,m_owner,this);
}

bool MutexPrivate::lock(long maxwait)
{
    bool rval = false;
    bool warn = false;
    if (s_maxwait && (maxwait < 0)) {
	maxwait = (long)s_maxwait;
	warn = true;
    }
    bool safety = s_safety;
    if (safety)
	GlobalMutex::lock();
    Thread* thr = Thread::current();
    if (thr)
	thr->m_locking = true;
    if (safety) {
	m_waiting++;
	GlobalMutex::unlock();
    }
#ifdef _WINDOWS
    DWORD ms = 0;
    if (maxwait < 0)
	ms = INFINITE;
    else if (maxwait > 0)
	ms = (DWORD)(maxwait / 1000);
    rval = s_unsafe || (::WaitForSingleObject(m_mutex,ms) == WAIT_OBJECT_0);
#else
    if (s_unsafe)
	rval = true;
    else if (maxwait < 0)
	rval = !::pthread_mutex_lock(&m_mutex);
    else if (!maxwait)
	rval = !::pthread_mutex_trylock(&m_mutex);
    else {
	u_int64_t t = Time::now() + maxwait;
#ifdef HAVE_TIMEDLOCK
	struct timeval tv;
	struct timespec ts;
	Time::toTimeval(&tv,t);
	ts.tv_sec = tv.tv_sec;
	ts.tv_nsec = 1000 * tv.tv_usec;
	rval = !::pthread_mutex_timedlock(&m_mutex,&ts);
#else
	bool dead = false;
	do {
	    if (!dead) {
		dead = Thread::check(false);
		// give up only if caller asked for a limited wait
		if (dead && !warn)
		    break;
	    }
	    rval = !::pthread_mutex_trylock(&m_mutex);
	    if (rval)
		break;
	    Thread::yield();
	} while (t > Time::now());
#endif // HAVE_TIMEDLOCK
    }
#endif // _WINDOWS
    if (safety) {
	GlobalMutex::lock();
	m_waiting--;
    }
    if (thr)
	thr->m_locking = false;
    if (rval) {
	if (safety)
	    s_locks++;
	m_locked++;
	if (thr) {
	    thr->m_locks++;
	    m_owner = thr->name();
	}
	else
	    m_owner = 0;
    }
    if (safety)
	GlobalMutex::unlock();
    if (warn && !rval)
	Debug(DebugFail,"Thread '%s' could not lock mutex '%s' owned by '%s' waited by %u others for %lu usec!",
	    Thread::currentName(),m_name,m_owner,m_waiting,maxwait);
    return rval;
}

bool MutexPrivate::unlock()
{
    bool ok = false;
    // Hope we don't hit a bug related to the debug mutex!
    bool safety = s_safety;
    if (safety)
	GlobalMutex::lock();
    if (m_locked) {
	Thread* thr = Thread::current();
	if (thr)
	    thr->m_locks--;
	if (!--m_locked) {
	    const char* tname = thr ? thr->name() : 0;
	    if (tname != m_owner)
		Debug(DebugFail,"MutexPrivate '%s' unlocked by '%s' but owned by '%s' [%p]",
		    m_name,tname,m_owner,this);
	    m_owner = 0;
	}
	if (safety) {
	    int locks = --s_locks;
	    if (locks < 0) {
		// this is very very bad - abort right now
		abortOnBug(true);
		s_locks = 0;
		Debug(DebugFail,"MutexPrivate::locks() is %d [%p]",locks,this);
	    }
	}
#ifdef _WINDOWS
	ok = s_unsafe || ::ReleaseMutex(m_mutex);
#else
	ok = s_unsafe || !::pthread_mutex_unlock(&m_mutex);
#endif
	if (!ok)
	    Debug(DebugFail,"Failed to unlock mutex '%s' [%p]",m_name,this);
    }
    else
	Debug(DebugFail,"MutexPrivate::unlock called on unlocked '%s' [%p]",m_name,this);
    if (safety)
	GlobalMutex::unlock();
    return ok;
}


SemaphorePrivate::SemaphorePrivate(unsigned int maxcount, const char* name,
    unsigned int initialCount)
    : m_refcount(1), m_waiting(0), m_maxcount(maxcount),
      m_name(name)
{
    if (initialCount > m_maxcount)
	initialCount = m_maxcount;
    GlobalMutex::lock();
    s_count++;
#ifdef _WINDOWS
    m_semaphore = ::CreateSemaphore(NULL,initialCount,maxcount,NULL);
#else
    ::sem_init(&m_semaphore,0,initialCount);
#endif
    GlobalMutex::unlock();
}

SemaphorePrivate::~SemaphorePrivate()
{
    GlobalMutex::lock();
    s_count--;
#ifdef _WINDOWS
    ::CloseHandle(m_semaphore);
    m_semaphore = 0;
#else
    ::sem_destroy(&m_semaphore);
#endif
    GlobalMutex::unlock();
    if (m_waiting)
	Debug(DebugFail,"SemaphorePrivate '%s' destroyed with %u locks [%p]",
	    m_name,m_waiting,this);
}

bool SemaphorePrivate::lock(long maxwait)
{
    bool rval = false;
    bool warn = false;
    if (s_maxwait && (maxwait < 0)) {
	maxwait = (long)s_maxwait;
	warn = true;
    }
    bool safety = s_safety;
    if (safety)
	GlobalMutex::lock();
    Thread* thr = Thread::current();
    if (thr)
	thr->m_locking = true;
    if (safety) {
	s_locks++;
	m_waiting++;
	GlobalMutex::unlock();
    }
#ifdef _WINDOWS
    DWORD ms = 0;
    if (maxwait < 0)
	ms = INFINITE;
    else if (maxwait > 0)
	ms = (DWORD)(maxwait / 1000);
    rval = s_unsafe || (::WaitForSingleObject(m_semaphore,ms) == WAIT_OBJECT_0);
#else
    if (s_unsafe)
	rval = true;
    else if (maxwait < 0)
	rval = !::sem_wait(&m_semaphore);
    else if (!maxwait)
	rval = !::sem_trywait(&m_semaphore);
    else {
	u_int64_t t = Time::now() + maxwait;
#ifdef HAVE_TIMEDWAIT
	struct timeval tv;
	struct timespec ts;
	Time::toTimeval(&tv,t);
	ts.tv_sec = tv.tv_sec;
	ts.tv_nsec = 1000 * tv.tv_usec;
	rval = !::sem_timedwait(&m_semaphore,&ts);
#else
	bool dead = false;
	do {
	    if (!dead) {
		dead = Thread::check(false);
		// give up only if caller asked for a limited wait
		if (dead && !warn)
		    break;
	    }
	    rval = !::sem_trywait(&m_semaphore);
	    if (rval)
		break;
	    Thread::yield();
	} while (t > Time::now());
#endif // HAVE_TIMEDWAIT
    }
#endif // _WINDOWS
    if (safety) {
	GlobalMutex::lock();
	int locks = --s_locks;
	if (locks < 0) {
	    // this is very very bad - abort right now
	    abortOnBug(true);
	    s_locks = 0;
	    Debug(DebugFail,"SemaphorePrivate::locks() is %d [%p]",locks,this);
	}
	m_waiting--;
    }
    if (thr)
	thr->m_locking = false;
    if (safety)
	GlobalMutex::unlock();
    if (warn && !rval)
	Debug(DebugFail,"Thread '%s' could not lock semaphore '%s' waited by %u others for %lu usec!",
	    Thread::currentName(),m_name,m_waiting,maxwait);
    return rval;
}

bool SemaphorePrivate::unlock()
{
    if (!s_unsafe) {
	bool safety = s_safety;
	if (safety)
	    GlobalMutex::lock();
#ifdef _WINDOWS
	::ReleaseSemaphore(m_semaphore,1,NULL);
#else
	int val = 0;
	if (!::sem_getvalue(&m_semaphore,&val) && (val < (int)m_maxcount))
	    ::sem_post(&m_semaphore);
#endif
	if (safety)
	    GlobalMutex::unlock();
    }
    return true;
}


Lockable::~Lockable()
{
}

bool Lockable::check(long maxwait)
{
    bool ret = lock(maxwait);
    if (ret)
	unlock();
    return ret;
}

bool Lockable::unlockAll()
{
    while (locked()) {
	if (!unlock())
	    return false;
	Thread::yield();
    }
    return true;
}

void Lockable::startUsingNow()
{
    s_unsafe = false;
}

void Lockable::enableSafety(bool safe)
{
    s_safety = safe;
}

bool Lockable::safety()
{
    return s_safety;
}

void Lockable::wait(unsigned long maxwait)
{
    s_maxwait = maxwait;
}

unsigned long Lockable::wait()
{
    return s_maxwait;
}


Mutex::Mutex(bool recursive, const char* name)
    : m_private(0)
{
    if (!name)
	name = "?";
    m_private = new MutexPrivate(recursive,name);
}

Mutex::Mutex(const Mutex &original)
    : Lockable(),
      m_private(original.privDataCopy())
{
}

Mutex::~Mutex()
{
    MutexPrivate* priv = m_private;
    m_private = 0;
    if (priv)
	priv->deref();
}

Mutex& Mutex::operator=(const Mutex& original)
{
    MutexPrivate* priv = m_private;
    m_private = original.privDataCopy();
    if (priv)
	priv->deref();
    return *this;
}

MutexPrivate* Mutex::privDataCopy() const
{
    if (m_private)
	m_private->ref();
    return m_private;
}

bool Mutex::lock(long maxwait)
{
    return m_private && m_private->lock(maxwait);
}

bool Mutex::unlock()
{
    return m_private && m_private->unlock();
}

bool Mutex::recursive() const
{
    return m_private && m_private->recursive();
}

bool Mutex::locked() const
{
    return m_private && m_private->locked();
}

const char* Mutex::owner() const
{
    return m_private ? m_private->owner() : static_cast<const char*>(0);
}

int Mutex::count()
{
    return MutexPrivate::s_count;
}

int Mutex::locks()
{
    return s_safety ? MutexPrivate::s_locks : -1;
}

bool Mutex::efficientTimedLock()
{
#if defined(_WINDOWS) || defined(HAVE_TIMEDLOCK)
    return true;
#else
    return false;
#endif
}


MutexPool::MutexPool(unsigned int len, bool recursive, const char* name)
    : m_name(0), m_data(0), m_length(len ? len : 1)
{
    if (TelEngine::null(name))
	name = "Pool";
    m_name = new String[m_length];
    for (unsigned int i = 0; i < m_length; i++)
	m_name[i] << name << "::" << (i + 1);
    m_data = new Mutex*[m_length];
    for (unsigned int i = 0; i < m_length; i++)
	m_data[i] = new Mutex(recursive,m_name[i]);
}

MutexPool::~MutexPool()
{
    if (m_data) {
	for (unsigned int i = 0; i < m_length; i++)
	    delete m_data[i];
	delete[] m_data;
    }
    if (m_name)
	delete[] m_name;
}


Semaphore::Semaphore(unsigned int maxcount, const char* name, unsigned int initialCount)
    : m_private(0)
{
    if (!name)
	name = "?";
    if (maxcount)
	m_private = new SemaphorePrivate(maxcount,name,initialCount);
}

Semaphore::Semaphore(const Semaphore &original)
    : Lockable(),
      m_private(original.privDataCopy())
{
}

Semaphore::~Semaphore()
{
    SemaphorePrivate* priv = m_private;
    m_private = 0;
    if (priv)
	priv->deref();
}

Semaphore& Semaphore::operator=(const Semaphore& original)
{
    SemaphorePrivate* priv = m_private;
    m_private = original.privDataCopy();
    if (priv)
	priv->deref();
    return *this;
}

SemaphorePrivate* Semaphore::privDataCopy() const
{
    if (m_private)
	m_private->ref();
    return m_private;
}

bool Semaphore::lock(long maxwait)
{
    return m_private && m_private->lock(maxwait);
}

bool Semaphore::unlock()
{
    return m_private && m_private->unlock();
}

bool Semaphore::locked() const
{
    return m_private && m_private->locked();
}

int Semaphore::count()
{
    return SemaphorePrivate::s_count;
}

int Semaphore::locks()
{
    return s_safety ? SemaphorePrivate::s_locks : -1;
}

bool Semaphore::efficientTimedLock()
{
#if defined(_WINDOWS) || defined(HAVE_TIMEDWAIT)
    return true;
#else
    return false;
#endif
}


bool Lock2::lock(Mutex* mx1, Mutex* mx2, long maxwait)
{
    // if we got only one mutex it must be mx1
    if (!mx1) {
	mx1 = mx2;
	mx2 = 0;
    }
    // enforce a fixed locking order - lowest address first
    else if (mx1 && mx2 && (mx1 > mx2)) {
	Mutex* tmp = mx1;
	mx1 = mx2;
	mx2 = tmp;
    }
    drop();
    if (!mx1)
	return false;
    if (!mx1->lock(maxwait))
	return false;
    if (mx2) {
	if (!mx2->lock(maxwait)) {
	    mx1->unlock();
	    return false;
	}
    }
    m_mx1 = mx1;
    m_mx2 = mx2;
    return true;
}

void Lock2::drop()
{
    Mutex* mx1 = m_mx1;
    Mutex* mx2 = m_mx2;
    m_mx1 = m_mx2 = 0;
    // unlock in reverse order for performance reason
    if (mx2)
	mx2->unlock();
    if (mx1)
	mx1->unlock();
}

/* vi: set ts=8 sw=4 sts=4 noet: */
