Added Semaphore class, made Mutex derive from (new class) Lockable, made Lock able to get a lock on any Lockable - Mutex or Semaphore.

Use pthread_mutex_timedlock and sem_timedwait if available on platform.
Fixed a bug in the IAX channel exposed by Mutex having virtual methods.


git-svn-id: http://voip.null.ro/svn/yate@2761 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2009-07-22 12:57:14 +00:00
parent 25450eaaff
commit 09b07f4b00
10 changed files with 505 additions and 83 deletions

View File

@ -82,7 +82,7 @@ AC_MSG_RESULT([$ld_unresolved_symbols])
# Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS([fcntl.h arpa/inet.h netdb.h netinet/in.h sys/ioctl.h sys/socket.h sys/time.h], , [AC_MSG_ERROR([This header file is required.])])
AC_CHECK_HEADERS([fcntl.h semaphore.h arpa/inet.h netdb.h netinet/in.h sys/ioctl.h sys/socket.h sys/time.h], , [AC_MSG_ERROR([This header file is required.])])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
@ -133,11 +133,12 @@ AC_SUBST(DLOPEN_LIB)
LIBS="$SAVE_LIBS"
MUTEX_HACK=""
AC_MSG_CHECKING([for pthread_mutexattr_settype declaration])
AC_LANG_SAVE
AC_LANG_C
SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wall -Werror"
AC_MSG_CHECKING([for pthread_mutexattr_settype declaration])
AC_TRY_COMPILE([
#include <unistd.h>
#include <pthread.h>
@ -148,12 +149,46 @@ pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
have_mutex_settype="yes",
have_mutex_settype="no"
)
CFLAGS="$SAVE_CFLAGS"
AC_LANG_RESTORE
if [[ "$have_mutex_settype" = "no" ]]; then
MUTEX_HACK="-DMUTEX_HACK"
fi
AC_MSG_RESULT([$have_mutex_settype])
AC_MSG_CHECKING([for pthread_mutex_timedlock declaration])
AC_TRY_COMPILE([
#include <unistd.h>
#include <pthread.h>
],[
pthread_mutex_t mutex;
struct timespec tout;
pthread_mutex_timedlock(&mutex,&tout);
],
have_mutex_timedlock="yes",
have_mutex_timedlock="no"
)
if [[ "$have_mutex_timedlock" = "yes" ]]; then
MUTEX_HACK="$MUTEX_HACK -DHAVE_TIMEDLOCK"
fi
AC_MSG_RESULT([$have_mutex_timedlock])
AC_MSG_CHECKING([for sem_timedwait declaration])
AC_TRY_COMPILE([
#include <semaphore.h>
],[
sem_t sem;
struct timespec tout;
sem_timedwait(&sem,&tout);
],
have_sem_timedwait="yes",
have_sem_timedwait="no"
)
if [[ "$have_sem_timedwait" = "yes" ]]; then
MUTEX_HACK="$MUTEX_HACK -DHAVE_TIMEDWAIT"
fi
AC_MSG_RESULT([$have_sem_timedwait])
CFLAGS="$SAVE_CFLAGS"
AC_LANG_RESTORE
AC_SUBST(MUTEX_HACK)
THREAD_KILL=""

View File

@ -161,7 +161,7 @@ bool CallEndpoint::disconnect(bool final, const char* reason, bool notify, const
DDebug(DebugAll,"CallEndpoint '%s' disconnecting peer %p from [%p]",m_id.c_str(),m_peer,this);
Lock lock(s_mutex,5000000);
if (!lock.mutex()) {
if (!lock.locked()) {
Debug(DebugFail,"Call disconnect failed - deadlock on call endpoint mutex!");
Engine::restart(0);
return false;

View File

@ -515,7 +515,7 @@ unsigned long DataSource::Forward(const DataBlock& data, unsigned long tStamp, u
{
Lock mylock(this,100000);
// we DON'T refcount here, we rely on the mutex to keep us safe
if (!(mylock.mutex() && alive())) {
if (!(mylock.locked() && alive())) {
DDebug(DebugInfo,"Forwarding on a dead DataSource! [%p]",this);
return 0;
}
@ -627,7 +627,7 @@ void DataSource::clear()
void DataSource::synchronize(unsigned long tStamp)
{
Lock mylock(this,100000);
if (!(mylock.mutex() && alive())) {
if (!(mylock.locked() && alive())) {
DDebug(DebugInfo,"Synchronizing on a dead DataSource! [%p]",this);
return;
}

View File

@ -302,6 +302,8 @@ bool EngineStatusHandler::received(Message &msg)
msg.retValue() << ",workers=" << EnginePrivate::count;
msg.retValue() << ",mutexes=" << Mutex::count();
msg.retValue() << ",locks=" << Mutex::locks();
msg.retValue() << ",semaphores=" << Semaphore::count();
msg.retValue() << ",waiting=" << Semaphore::locks();
msg.retValue() << "\r\n";
return false;
}
@ -1500,7 +1502,7 @@ int Engine::main(int argc, const char** argv, const char** env, RunMode mode, bo
const char* usrpath = 0;
int debug_level = debugLevel();
Mutex::startUsingNow();
Lockable::startUsingNow();
const char* cfgfile = ::strrchr(argv[0],'/');
if (!cfgfile)
@ -1680,7 +1682,7 @@ int Engine::main(int argc, const char** argv, const char** env, RunMode mode, bo
s_lateabrt = true;
break;
case 'm':
Mutex::wait(10000000);
Lockable::wait(10000000);
break;
#ifdef RTLD_GLOBAL
case 'l':

View File

@ -25,10 +25,12 @@
#ifdef _WINDOWS
typedef HANDLE HMUTEX;
typedef HANDLE HSEMAPHORE;
#else
#include <pthread.h>
#include <semaphore.h>
#ifdef MUTEX_HACK
extern "C" {
@ -42,6 +44,7 @@ extern int pthread_mutexattr_settype(pthread_mutexattr_t *__attr, int __kind) __
#endif
typedef pthread_mutex_t HMUTEX;
typedef sem_t HSEMAPHORE;
#endif /* ! _WINDOWS */
@ -69,7 +72,7 @@ public:
bool locked() const
{ return (m_locked > 0); }
bool lock(long maxwait);
void unlock();
bool unlock();
static volatile int s_count;
static volatile int s_locks;
private:
@ -81,6 +84,30 @@ private:
const char* m_owner;
};
class SemaphorePrivate {
public:
SemaphorePrivate(unsigned int maxcount, const char* name);
~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();
@ -103,6 +130,8 @@ static bool s_unsafe = MUTEX_STATIC_UNSAFE;
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!!!
@ -211,7 +240,6 @@ 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;
@ -226,9 +254,8 @@ bool MutexPrivate::lock(long maxwait)
DWORD ms = 0;
if (maxwait < 0)
ms = INFINITE;
else if (maxwait > 0) {
else if (maxwait > 0)
ms = (DWORD)(maxwait / 1000);
}
rval = s_unsafe || (::WaitForSingleObject(m_mutex,ms) == WAIT_OBJECT_0);
#else
if (s_unsafe)
@ -239,6 +266,15 @@ bool MutexPrivate::lock(long 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);
@ -251,8 +287,9 @@ bool MutexPrivate::lock(long maxwait)
break;
Thread::yield();
} while (t > Time::now());
#endif // HAVE_TIMEDLOCK
}
#endif
#endif // _WINDOWS
GlobalMutex::lock();
if (thr)
thr->m_locking = false;
@ -275,8 +312,9 @@ bool MutexPrivate::lock(long maxwait)
return rval;
}
void MutexPrivate::unlock()
bool MutexPrivate::unlock()
{
bool ok = false;
// Hope we don't hit a bug related to the debug mutex!
GlobalMutex::lock();
if (m_locked) {
@ -304,10 +342,169 @@ void MutexPrivate::unlock()
::pthread_mutex_unlock(&m_mutex);
#endif
deref();
ok = true;
}
else
Debug(DebugFail,"MutexPrivate::unlock called on unlocked '%s' [%p]",m_name,this);
GlobalMutex::unlock();
return ok;
}
SemaphorePrivate::SemaphorePrivate(unsigned int maxcount, const char* name)
: m_refcount(1), m_waiting(0), m_maxcount(maxcount),
m_name(name)
{
GlobalMutex::lock();
s_count++;
#ifdef _WINDOWS
m_semaphore = ::CreateSemaphore(NULL,1,maxcount,NULL);
#else
::sem_init(&m_semaphore,0,1);
#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;
}
GlobalMutex::lock();
ref();
s_locks++;
m_waiting++;
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 = 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
GlobalMutex::lock();
m_waiting--;
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);
}
if (thr)
thr->m_locking = false;
if (!rval)
deref();
GlobalMutex::unlock();
if (warn && !rval)
Debug(DebugFail,"Thread '%s' could not lock semaphore '%s' for %lu usec!",
Thread::currentName(),m_name,maxwait);
return rval;
}
bool SemaphorePrivate::unlock()
{
if (!s_unsafe) {
#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
}
GlobalMutex::lock();
deref();
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::wait(unsigned long maxwait)
{
s_maxwait = maxwait;
}
@ -326,7 +523,7 @@ Mutex::Mutex(const Mutex &original)
Mutex::~Mutex()
{
MutexPrivate *priv = m_private;
MutexPrivate* priv = m_private;
m_private = 0;
if (priv)
priv->deref();
@ -334,14 +531,14 @@ Mutex::~Mutex()
Mutex& Mutex::operator=(const Mutex& original)
{
MutexPrivate *priv = m_private;
MutexPrivate* priv = m_private;
m_private = original.privDataCopy();
if (priv)
priv->deref();
return *this;
}
MutexPrivate *Mutex::privDataCopy() const
MutexPrivate* Mutex::privDataCopy() const
{
if (m_private)
m_private->ref();
@ -350,21 +547,12 @@ MutexPrivate *Mutex::privDataCopy() const
bool Mutex::lock(long maxwait)
{
return m_private ? m_private->lock(maxwait) : false;
return m_private && m_private->lock(maxwait);
}
void Mutex::unlock()
bool Mutex::unlock()
{
if (m_private)
m_private->unlock();
}
bool Mutex::check(long maxwait)
{
bool ret = lock(maxwait);
if (ret)
unlock();
return ret;
return m_private && m_private->unlock();
}
bool Mutex::recursive() const
@ -387,14 +575,86 @@ int Mutex::locks()
return MutexPrivate::s_locks;
}
void Mutex::startUsingNow()
bool Mutex::efficientTimedLock()
{
s_unsafe = false;
#if defined(_WINDOWS) || defined(HAVE_TIMEDLOCK)
return true;
#else
return false;
#endif
}
void Mutex::wait(unsigned long maxwait)
Semaphore::Semaphore(unsigned int maxcount, const char* name)
: m_private(0)
{
s_maxwait = maxwait;
if (!name)
name = "?";
if (maxcount)
m_private = new SemaphorePrivate(maxcount,name);
}
Semaphore::Semaphore(const Semaphore &original)
: 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 SemaphorePrivate::s_locks;
}
bool Semaphore::efficientTimedLock()
{
#if defined(_WINDOWS) || defined(HAVE_TIMEDWAIT)
return true;
#else
return false;
#endif
}

View File

@ -485,7 +485,7 @@ void ForkModule::initialize()
bool ForkModule::unload()
{
Lock lock(s_mutex,500000);
if (!lock.mutex())
if (!lock.locked())
return false;
if (s_calls.count())
return false;

View File

@ -980,7 +980,7 @@ bool ConferenceDriver::checkRoom(String& room, bool existing, bool counted)
bool ConferenceDriver::unload()
{
Lock lock(this,500000);
if (!lock.mutex())
if (!lock.locked())
return false;
if (isBusy() || s_rooms.count())
return false;

View File

@ -360,7 +360,7 @@ void MuxSource::consume(MuxConsumer& consumer, const DataBlock& data, unsigned l
if (!data.length() || consumer.m_owner != this)
return;
Lock lock(m_lock,100000);
if (!(lock.mutex() && alive())) {
if (!(lock.locked() && alive())) {
Debug(this,DebugMild,"Locking failed, dropping %u bytes [%p]",data.length(),this);
return;
}

View File

@ -524,9 +524,9 @@ private:
* Local data
*/
static Configuration s_cfg; // Configuration file
static YIAXDriver iplugin; // Init the driver
static YIAXLineContainer s_lines; // Lines
static Thread::Priority s_priority = Thread::Normal; // Threads priority
static YIAXDriver iplugin; // Init the driver
/*

View File

@ -3563,13 +3563,77 @@ protected:
};
class MutexPrivate;
class SemaphorePrivate;
class ThreadPrivate;
/**
* An abstract base class for implementing lockable objects
* @short Abstract interface for lockable objects
*/
class YATE_API Lockable
{
public:
/**
* Destructor
*/
virtual ~Lockable();
/**
* Attempt to lock the object and eventually wait for it
* @param maxwait Time in microseconds to wait, -1 wait forever
* @return True if successfully locked, false on failure
*/
virtual bool lock(long maxwait = -1) = 0;
/**
* Unlock the object, does never wait
* @return True if successfully unlocked the object
*/
virtual bool unlock() = 0;
/**
* Check if the object is currently locked - as it's asynchronous it
* guarantees nothing if other thread changes the status
* @return True if the object was locked when the function was called
*/
virtual bool locked() const = 0;
/**
* Check if the object is unlocked (try to lock and unlock it)
* @param maxwait Time in microseconds to wait, -1 to wait forever
* @return True if successfully locked and unlocked, false on failure
*/
virtual bool check(long maxwait = -1);
/**
* Fully unlock the object, even if it was previously multiple locked.
* There is no guarantee about the object status after the function returns.
* This function should be used only if you understand it very well
* @return True if the object was fully unlocked
*/
virtual bool unlockAll();
/**
* Set a maximum wait time for debugging purposes
* @param maxwait Maximum time in microseconds to wait for any lockable
* object when no time limit was requested, zero to disable limit
*/
static void wait(unsigned long maxwait);
/**
* Start actually using lockables, for platforms where these objects are not
* usable in global object constructors.
* This method must be called at least once somewhere from main() but
* before creating any threads and without holding any object locked.
*/
static void startUsingNow();
};
/**
* A simple mutual exclusion for locking access between threads
* @short Mutex support
*/
class YATE_API Mutex
class YATE_API Mutex : public Lockable
{
friend class MutexPrivate;
public:
@ -3582,7 +3646,7 @@ public:
Mutex(bool recursive = false, const char* name = 0);
/**
* Copy constructor creates a shared mutex
* Copy constructor, creates a shared mutex
* @param original Reference of the mutex to share
*/
Mutex(const Mutex& original);
@ -3603,26 +3667,20 @@ public:
* @param maxwait Time in microseconds to wait for the mutex, -1 wait forever
* @return True if successfully locked, false on failure
*/
bool lock(long maxwait = -1);
virtual bool lock(long maxwait = -1);
/**
* Unlock the mutex, does never wait
* @return True if successfully unlocked the mutex
*/
void unlock();
virtual bool unlock();
/**
* Check if the mutex is currently locked - as it's asynchronous it
* guarantees nothing if other thread changes the mutex's status
* @return True if the mutex was locked when the function was called
*/
bool locked() const;
/**
* Check if the mutex is unlocked (try to lock and unlock the mutex)
* @param maxwait Time in microseconds to wait for the mutex, -1 wait forever
* @return True if successfully locked and unlocked, false on failure
*/
bool check(long maxwait = -1);
virtual bool locked() const;
/**
* Check if this mutex is recursive or not
@ -3643,19 +3701,10 @@ public:
static int locks();
/**
* Set a maximum mutex wait time for debugging purposes
* @param maxwait Maximum time in microseconds to wait for any mutex
* when no time limit was requested, zero to disable limit
* Check if a timed lock() is efficient on this platform
* @return True if a lock with a maxwait parameter is efficiently implemented
*/
static void wait(unsigned long maxwait);
/**
* Start actually using mutexes, for platforms where mutexes are not
* usable in global object constructors.
* This method must be called at least once somewhere from main() but
* before creating any threads and without holding any mutex locked.
*/
static void startUsingNow();
static bool efficientTimedLock();
private:
MutexPrivate* privDataCopy() const;
@ -3663,50 +3712,125 @@ private:
};
/**
* A lock is a stack allocated (automatic) object that locks a mutex on
* creation and unlocks it on destruction - typically when exiting a block
* @short Ephemeral mutex locking object
* A semaphore object for synchronizing threads, can also be used as a token bucket
* @short Semaphore implementation
*/
class YATE_API Semaphore : public Lockable
{
friend class SemaphorePrivate;
public:
/**
* Construct a new unlocked semaphore
* @param maxcount Maximum unlock count, must be strictly positive
* @param name Static name of the semaphore (for debugging purpose only)
*/
Semaphore(unsigned int maxcount = 1, const char* name = 0);
/**
* Copy constructor, creates a shared semaphore
* @param original Reference of the semaphore to share
*/
Semaphore(const Semaphore& original);
/**
* Destroy the semaphore
*/
~Semaphore();
/**
* Assignment operator makes the semaphore shared with the original
* @param original Reference of the semaphore to share
*/
Semaphore& operator=(const Semaphore& original);
/**
* Attempt to get a lock on the semaphore and eventually wait for it
* @param maxwait Time in microseconds to wait, -1 wait forever
* @return True if successfully locked, false on failure
*/
virtual bool lock(long maxwait = -1);
/**
* Unlock the semaphore, does never wait nor get over counter maximum
* @return True if successfully unlocked
*/
virtual bool unlock();
/**
* Check if the semaphore is currently locked (waiting) - as it's
* asynchronous it guarantees nothing if other thread changes status
* @return True if the semaphore was locked when the function was called
*/
virtual bool locked() const;
/**
* Get the number of semaphores counting the shared ones only once
* @return Count of individual semaphores
*/
static int count();
/**
* Get the number of currently locked (waiting) semaphores
* @return Count of locked semaphores, should be zero at program exit
*/
static int locks();
/**
* Check if a timed lock() is efficient on this platform
* @return True if a lock with a maxwait parameter is efficiently implemented
*/
static bool efficientTimedLock();
private:
SemaphorePrivate* privDataCopy() const;
SemaphorePrivate* m_private;
};
/**
* A lock is a stack allocated (automatic) object that locks a lockable object
* on creation and unlocks it on destruction - typically when exiting a block
* @short Ephemeral mutex or semaphore locking object
*/
class YATE_API Lock
{
public:
/**
* Create the lock, try to lock the mutex
* @param mutex Reference to the mutex to lock
* @param maxwait Time in microseconds to wait for the mutex, -1 wait forever
* Create the lock, try to lock the object
* @param lck Reference to the object to lock
* @param maxwait Time in microseconds to wait, -1 wait forever
*/
inline Lock(Mutex& mutex, long maxwait = -1)
{ m_mutex = mutex.lock(maxwait) ? &mutex : 0; }
inline Lock(Lockable& lck, long maxwait = -1)
{ m_lock = lck.lock(maxwait) ? &lck : 0; }
/**
* Create the lock, try to lock the mutex
* @param mutex Pointer to the mutex to lock
* @param maxwait Time in microseconds to wait for the mutex, -1 wait forever
* Create the lock, try to lock the object
* @param lck Pointer to the object to lock
* @param maxwait Time in microseconds to wait, -1 wait forever
*/
inline Lock(Mutex* mutex, long maxwait = -1)
{ m_mutex = (mutex && mutex->lock(maxwait)) ? mutex : 0; }
inline Lock(Lockable* lck, long maxwait = -1)
{ m_lock = (lck && lck->lock(maxwait)) ? lck : 0; }
/**
* Destroy the lock, unlock the mutex if it was locked
*/
inline ~Lock()
{ if (m_mutex) m_mutex->unlock(); }
{ if (m_lock) m_lock->unlock(); }
/**
* Return a pointer to the mutex this lock holds
* @return A mutex pointer or NULL if locking failed
* Return a pointer to the lockable object this lock holds
* @return A pointer to a Lockable or NULL if locking failed
*/
inline Mutex* mutex() const
{ return m_mutex; }
inline Lockable* locked() const
{ return m_lock; }
/**
* Unlock the mutex if it was locked and drop the reference to it
* Unlock the object if it was locked and drop the reference to it
*/
inline void drop()
{ if (m_mutex) m_mutex->unlock(); m_mutex = 0; }
{ if (m_lock) m_lock->unlock(); m_lock = 0; }
private:
Mutex* m_mutex;
Lockable* m_lock;
/** Make sure no Lock is ever created on heap */
inline void* operator new(size_t);
@ -3828,6 +3952,7 @@ class YATE_API Thread : public Runnable
{
friend class ThreadPrivate;
friend class MutexPrivate;
friend class SemaphorePrivate;
public:
/**
* Running priorities, their mapping is operating system dependent