/** * 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-2006 Null Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "yateclass.h" #ifdef _WINDOWS typedef HANDLE HMUTEX; #else #include #ifdef MUTEX_HACK extern "C" { #if defined(__FreeBSD__) 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; #endif /* ! _WINDOWS */ namespace TelEngine { class MutexPrivate { public: MutexPrivate(bool recursive); ~MutexPrivate(); inline void ref() { ++m_refcount; } inline void deref() { if (!--m_refcount) delete this; } inline bool recursive() const { return m_recursive; } bool locked() const { return (m_locked > 0); } bool lock(long maxwait); void unlock(); static volatile int s_count; static volatile int s_locks; private: HMUTEX m_mutex; int m_refcount; volatile unsigned int m_locked; bool m_recursive; const char* m_owner; }; 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; static GlobalMutex s_global; static unsigned long s_maxwait = 0; volatile int MutexPrivate::s_count = 0; volatile int MutexPrivate::s_locks = 0; bool GlobalMutex::s_init = true; HMUTEX GlobalMutex::s_mutex; // 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(); #ifdef _WINDOWS ::WaitForSingleObject(s_mutex,INFINITE); #else ::pthread_mutex_lock(&s_mutex); #endif } void GlobalMutex::unlock() { init(); #ifdef _WINDOWS ::ReleaseMutex(s_mutex); #else ::pthread_mutex_unlock(&s_mutex); #endif } MutexPrivate::MutexPrivate(bool recursive) : m_refcount(1), m_locked(0), m_recursive(recursive), 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() { GlobalMutex::lock(); if (m_locked) { m_locked--; 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(); } bool MutexPrivate::lock(long maxwait) { bool rval = false; bool warn = false; bool dead = false; if (s_maxwait && (maxwait < 0)) { maxwait = (long)s_maxwait; warn = true; } GlobalMutex::lock(); ref(); Thread* thr = Thread::current(); if (thr) thr->m_locking = true; GlobalMutex::unlock(); #ifdef _WINDOWS DWORD ms = 0; if (maxwait < 0) ms = INFINITE; else if (maxwait > 0) { ms = (DWORD)(maxwait / 1000); } rval = (::WaitForSingleObject(m_mutex,ms) == WAIT_OBJECT_0); #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; do { if (dead = Thread::check(false)) break; rval = !::pthread_mutex_trylock(&m_mutex); if (rval) break; Thread::yield(); } while (t > Time::now()); } #endif GlobalMutex::lock(); if (thr) thr->m_locking = false; if (rval) { s_locks++; m_locked++; if (thr) { thr->m_locks++; m_owner = thr->name(); } else m_owner = 0; } else deref(); GlobalMutex::unlock(); if (dead) Thread::exit(); if (warn && !rval) Debug(DebugFail,"Thread '%s' could not take lock owned by '%s' for %lu usec!", Thread::currentName(),m_owner,maxwait); return rval; } void MutexPrivate::unlock() { // Hope we don't hit a bug related to the debug mutex! GlobalMutex::lock(); if (m_locked) { Thread* thr = Thread::current(); if (thr) thr->m_locks--; if (!--m_locked) m_owner = 0; if (--s_locks < 0) Debug(DebugFail,"MutexPrivate::locks() is %d [%p]",s_locks,this); #ifdef _WINDOWS ::ReleaseMutex(m_mutex); #else ::pthread_mutex_unlock(&m_mutex); #endif deref(); } else Debug(DebugFail,"MutexPrivate::unlock called on unlocked mutex [%p]",this); GlobalMutex::unlock(); } Mutex::Mutex() : m_private(0) { m_private = new MutexPrivate(false); } Mutex::Mutex(bool recursive) : m_private(0) { m_private = new MutexPrivate(recursive); } Mutex::Mutex(const Mutex &original) : 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) : false; } void Mutex::unlock() { if (m_private) m_private->unlock(); } bool Mutex::check(long maxwait) { bool ret = lock(maxwait); if (ret) unlock(); return ret; } bool Mutex::recursive() const { return m_private && m_private->recursive(); } bool Mutex::locked() const { return m_private && m_private->locked(); } int Mutex::count() { return MutexPrivate::s_count; } int Mutex::locks() { return MutexPrivate::s_locks; } void Mutex::wait(unsigned long maxwait) { s_maxwait = maxwait; } 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: */