Refactored locking and list iteration to solve deadlocks and race conditions.

git-svn-id: http://voip.null.ro/svn/yate@639 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2006-01-12 05:32:06 +00:00
parent 07ae2de4e1
commit 3490d2d891
13 changed files with 452 additions and 80 deletions

View File

@ -131,7 +131,7 @@ AC_MSG_RESULT([$want_inline])
AC_SUBST(INLINE_FLAGS)
FDSIZE_HACK=""
AC_ARG_WITH(fdsize,AC_HELP_STRING([--with-fdsize=NNNN],[set FD_SIZE to NNNN (default 4096)]),[ac_cv_use_fdsize=$withval],[ac_cv_use_fdsize=4096])
AC_ARG_WITH(fdsize,AC_HELP_STRING([--with-fdsize=NNNN],[set FD_SIZE to NNNN (default 8192)]),[ac_cv_use_fdsize=$withval],[ac_cv_use_fdsize=8192])
if [[ "x$ac_cv_use_fdsize" != "xno" ]]; then
FDSIZE_HACK="-DFDSIZE_HACK=$ac_cv_use_fdsize"
fi

View File

@ -52,6 +52,11 @@ CallEndpoint::~CallEndpoint()
m_data.clear();
}
Mutex& CallEndpoint::commonMutex()
{
return s_mutex;
}
void* CallEndpoint::getObject(const String& name) const
{
if (name == "CallEndpoint")
@ -293,6 +298,8 @@ void Channel::dropChan()
if (!m_driver)
return;
m_driver->lock();
if (!m_driver)
Debug(DebugFail,"Driver lost in dropChan! [%p]",this);
if (m_driver->channels().remove(this,false))
m_driver->changed();
m_driver->unlock();
@ -798,21 +805,19 @@ bool Driver::received(Message &msg, int id)
{
// check each channel for timeouts
lock();
ListIterator iter(m_chans);
Time t;
ObjList* l = &m_chans;
while (l) {
Channel* c = static_cast<Channel*>(l->get());
if (c) {
if (c->timeout() && (c->timeout() < t))
c->msgDrop(msg,"timeout");
else if (c->maxcall() && (c->maxcall() < t))
c->msgDrop(msg,"noanswer");
}
// advance the pointer only if not dropped synchronously
if (l->get() == c)
l = l->next();
for (;;) {
RefPointer<Channel> c = static_cast<Channel*>(iter.get());
unlock();
if (!c)
break;
if (c->timeout() && (c->timeout() < t))
c->msgDrop(msg,"timeout");
else if (c->maxcall() && (c->maxcall() < t))
c->msgDrop(msg,"noanswer");
lock();
}
unlock();
}
case Status:
case Level:
@ -853,8 +858,9 @@ bool Driver::received(Message &msg, int id)
return msgExecute(msg,dest);
}
Lock lock(this);
Channel* chan = find(dest);
lock();
RefPointer<Channel> chan = find(dest);
unlock();
if (!chan) {
DDebug(this,DebugMild,"Could not find channel '%s'",dest.c_str());
return false;
@ -890,20 +896,19 @@ bool Driver::received(Message &msg, int id)
void Driver::dropAll(Message &msg)
{
lock();
const char* reason = msg.getValue("reason");
ObjList* l = &m_chans;
while (l) {
Channel* c = static_cast<Channel*>(l->get());
if (c) {
DDebug(this,DebugAll,"Dropping %s channel %p [%p]",name().c_str(),c,this);
c->msgDrop(msg,reason);
if (l->get() != c)
continue;
}
l = l->next();
lock();
ListIterator iter(m_chans);
for (;;) {
RefPointer<Channel> c = static_cast<Channel*>(iter.get());
unlock();
if (!c)
break;
DDebug(this,DebugAll,"Dropping %s channel %p [%p]",
name().c_str(),static_cast<Channel*>(c),this);
c->msgDrop(msg,reason);
lock();
}
unlock();
}
bool Driver::canAccept(bool routers)

View File

@ -113,7 +113,7 @@ void HashList::clear()
bool HashList::resync(GenObject* obj)
{
XDebug(DebugAll,"HashList::resync(%p) [%p]",this);
XDebug(DebugAll,"HashList::resync(%p) [%p]",obj,this);
if (!obj)
return false;
unsigned int i = obj->toString().hash() % m_size;

102
engine/Iterator.cpp Normal file
View File

@ -0,0 +1,102 @@
/**
* Iterator.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, 2005 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "yateclass.h"
using namespace TelEngine;
ListIterator::ListIterator(ObjList& list)
: m_objList(&list), m_hashList(0),
m_objects(0), m_length(0), m_current(0)
{
m_length = list.count();
if (!m_length)
return;
m_objects = new GenObject* [m_length];
unsigned int i = 0;
for (ObjList* l = list.skipNull(); i < m_length; l = l->skipNext()) {
if (!l)
break;
m_objects[i++] = l->get();
}
while (i < m_length)
m_objects[i++] = 0;
}
ListIterator::ListIterator(HashList& list)
: m_objList(0), m_hashList(&list),
m_objects(0), m_length(0), m_current(0)
{
m_length = list.count();
if (!m_length)
return;
m_objects = new GenObject* [m_length];
unsigned int i = 0;
for (unsigned int n = 0; n < list.length(); n++) {
ObjList* l = list.getList(n);
if (!l)
continue;
for (l = l->skipNull(); i < m_length; l = l->skipNext()) {
if (!l)
break;
m_objects[i++] = l->get();
}
}
while (i < m_length)
m_objects[i++] = 0;
}
ListIterator::~ListIterator()
{
m_length = 0;
delete m_objects;
}
GenObject* ListIterator::get(unsigned int index) const
{
if ((index >= m_length) || !m_objects)
return 0;
GenObject* obj = m_objects[index];
if (!obj)
return 0;
if (m_objList) {
if (m_objList->find(obj) && obj->alive())
return obj;
}
else if (m_hashList) {
if (m_hashList->find(obj) && obj->alive())
return obj;
}
return 0;
}
GenObject* ListIterator::get()
{
while (m_current < m_length) {
GenObject* obj = get(m_current++);
if (obj)
return obj;
}
return 0;
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -22,7 +22,7 @@ PINC := $(EINC) @top_srcdir@/yatephone.h
CLINC:= $(PINC) @top_srcdir@/yatecbase.h
LIBS :=
CLSOBJS := TelEngine.o ObjList.o HashList.o String.o DataBlock.o NamedList.o \
Array.o YMD5.o Mutex.o Thread.o Socket.o
Array.o Iterator.o YMD5.o Mutex.o Thread.o Socket.o
ENGOBJS := Configuration.o Message.o Plugin.o Engine.o
TELOBJS := DataFormat.o Channel.o
CLIOBJS := Client.o

View File

@ -68,6 +68,7 @@ private:
int m_refcount;
volatile unsigned int m_locked;
bool m_recursive;
const char* m_owner;
};
class GlobalMutex {
@ -138,8 +139,9 @@ void GlobalMutex::unlock()
#endif
}
MutexPrivate::MutexPrivate(bool recursive)
: m_refcount(1), m_locked(0), m_recursive(recursive)
: m_refcount(1), m_locked(0), m_recursive(recursive), m_owner(0)
{
GlobalMutex::lock();
s_count++;
@ -220,12 +222,14 @@ bool MutexPrivate::lock(long maxwait)
if (rval) {
s_locks++;
m_locked++;
m_owner = Thread::currentName();
}
else
deref();
GlobalMutex::unlock();
if (warn && !rval)
Debug(DebugFail,"Mutex lock failed for %lu usec!",maxwait);
Debug(DebugFail,"Thread '%s' could not take lock owned by '%s' for %lu usec!",
Thread::currentName(),m_owner,maxwait);
return rval;
}
@ -234,7 +238,8 @@ void MutexPrivate::unlock()
// Hope we don't hit a bug related to the debug mutex!
GlobalMutex::lock();
if (m_locked) {
m_locked--;
if (!--m_locked)
m_owner = 0;
if (--s_locks < 0)
Debug(DebugFail,"MutexPrivate::locks() is %d [%p]",s_locks,this);
#ifdef _WINDOWS
@ -249,6 +254,7 @@ void MutexPrivate::unlock()
GlobalMutex::unlock();
}
Mutex::Mutex()
: m_private(0)
{
@ -334,4 +340,46 @@ 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: */

View File

@ -379,6 +379,17 @@ void Time::toTimeval(struct timeval* tv, u_int64_t usec)
}
}
bool GenObject::alive() const
{
return true;
}
void GenObject::destruct()
{
delete this;
}
static Mutex s_refmutex;
RefObject::~RefObject()
@ -387,40 +398,61 @@ RefObject::~RefObject()
Debug(DebugFail,"RefObject [%p] destroyed with count=%d",this,m_refcount);
}
int RefObject::ref()
bool RefObject::alive() const
{
return m_refcount > 0;
}
void RefObject::destruct()
{
deref();
}
bool RefObject::ref()
{
s_refmutex.lock();
int i = ++m_refcount;
bool ret = (m_refcount > 0);
if (ret)
++m_refcount;
s_refmutex.unlock();
return i;
return ret;
}
bool RefObject::deref()
{
s_refmutex.lock();
int i = --m_refcount;
int i = m_refcount;
if (i > 0)
--m_refcount;
s_refmutex.unlock();
if (i == 0)
if (i == 1)
zeroRefs();
return (i <= 0);
return (i <= 1);
}
void RefObject::zeroRefs()
{
s_refmutex.lock();
m_refcount = -1;
s_refmutex.unlock();
delete this;
}
bool RefObject::resurrect()
{
s_refmutex.lock();
bool ret = (0 == m_refcount);
if (ret)
m_refcount = 1;
s_refmutex.unlock();
return ret;
}
void RefPointerBase::assign(RefObject* oldptr, RefObject* newptr, void* pointer)
{
if (oldptr == newptr)
return;
// Always reference the new object before dereferencing the old one
if (newptr)
newptr->ref();
m_pointer = pointer;
// and also don't keep pointers to objects that fail referencing
m_pointer = (newptr && newptr->ref()) ? pointer : 0;
if (oldptr)
oldptr->deref();
}

View File

@ -453,6 +453,11 @@ bool Thread::running() const
return m_private ? m_private->m_started : false;
}
const char* Thread::name() const
{
return m_private ? m_private->m_name : 0;
}
bool Thread::startup()
{
if (!m_private)
@ -467,6 +472,12 @@ Thread *Thread::current()
return t ? t->m_thread : 0;
}
const char* Thread::currentName()
{
ThreadPrivate* t = ThreadPrivate::current();
return t ? t->m_name : 0;
}
int Thread::count()
{
Lock lock(tmutex);

View File

@ -79,7 +79,7 @@ static void replaceParams(String& str, const NamedList &lst)
if (p2 > 0) {
String v = str.substr(p1+2,p2-p1-2);
v.trimBlanks();
DDebug(&module,DebugAll,"Replacing parameter '%s'",v.c_str());
DDebug(DebugAll,"Replacing parameter '%s'",v.c_str());
String tmp = String::sqlEscape(lst.getValue(v));
str = str.substr(0,p1) + tmp + str.substr(p2+1);
}

View File

@ -86,7 +86,6 @@ using namespace TelEngine;
static bool s_externalRtp;
static bool s_passtrough;
static Mutex s_mutex(true);
static Configuration s_cfg;
@ -925,22 +924,18 @@ YateH323Connection::YateH323Connection(YateH323EndPoint& endpoint,
YateH323Connection::~YateH323Connection()
{
Debug(&hplugin,DebugAll,"YateH323Connection::~YateH323Connection() [%p]",this);
s_mutex.lock();
YateH323Chan* tmp = m_chan;
m_chan = 0;
if (tmp)
tmp->finish();
s_mutex.unlock();
cleanups();
}
void YateH323Connection::CleanUpOnCallEnd()
{
Debug(&hplugin,DebugAll,"YateH323Connection::CleanUpOnCallEnd() [%p]",this);
s_mutex.lock();
if (m_chan)
m_chan->stopDataLinks();
s_mutex.unlock();
H323Connection::CleanUpOnCallEnd();
}
@ -1087,7 +1082,6 @@ void YateH323Connection::answerCall(AnswerCallResponse response)
void YateH323Connection::OnEstablished()
{
TelEngine::Lock lock(s_mutex);
Debug(m_chan,DebugInfo,"YateH323Connection::OnEstablished() [%p]",this);
if (!m_chan)
return;
@ -1098,7 +1092,6 @@ void YateH323Connection::OnEstablished()
m_chan->status("answered");
m_chan->maxcall(0);
Message *m = m_chan->message("call.answered",false,true);
lock.drop();
if (m_passtrough) {
if (m_remotePort) {
m->addParam("rtp_forward","yes");
@ -1120,23 +1113,19 @@ void YateH323Connection::OnCleared()
int reason = GetCallEndReason();
const char* rtext = CallEndReasonText(reason);
const char* error = lookup(reason,dict_errors);
s_mutex.lock();
Debug(m_chan,DebugInfo,"YateH323Connection::OnCleared() error: '%s' reason: %s (%d) [%p]",
error,rtext,reason,this);
if (m_chan)
m_chan->disconnect(error ? error : rtext);
s_mutex.unlock();
}
BOOL YateH323Connection::OnAlerting(const H323SignalPDU &alertingPDU, const PString &user)
{
TelEngine::Lock lock(s_mutex);
Debug(m_chan,DebugInfo,"YateH323Connection::OnAlerting '%s' [%p]",(const char *)user,this);
if (!m_chan)
return FALSE;
m_chan->status("ringing");
Message *m = m_chan->message("call.ringing",false,true);
lock.drop();
if (hasRemoteAddress()) {
m->addParam("rtp_forward","yes");
m->addParam("rtp_addr",m_remoteAddr);
@ -1152,12 +1141,10 @@ BOOL YateH323Connection::OnReceivedProgress(const H323SignalPDU& pdu)
Debug(m_chan,DebugInfo,"YateH323Connection::OnReceivedProgress [%p]",this);
if (!H323Connection::OnReceivedProgress(pdu))
return FALSE;
TelEngine::Lock lock(s_mutex);
if (!m_chan)
return FALSE;
m_chan->status("progressing");
Message *m = m_chan->message("call.progress",false,true);
lock.drop();
if (hasRemoteAddress()) {
m->addParam("rtp_forward","yes");
m->addParam("rtp_addr",m_remoteAddr);
@ -1394,7 +1381,6 @@ BOOL YateH323Connection::startExternalRTP(const char* remoteIP, WORD remotePort,
void YateH323Connection::stoppedExternal(H323Channel::Directions dir)
{
TelEngine::Lock lock(s_mutex);
Debug(m_chan,DebugInfo,"YateH323Connection::stoppedExternal(%s) chan=%p [%p]",
lookup(dir,dict_h323_dir),m_chan,this);
if (!m_chan)
@ -1791,7 +1777,6 @@ YateH323Chan::YateH323Chan(YateH323Connection* conn,Message* msg,const char* add
YateH323Chan::~YateH323Chan()
{
s_mutex.lock();
Debug(this,DebugAll,"YateH323Chan::~YateH323Chan() %s %s [%p]",
m_status.c_str(),id().c_str(),this);
stopDataLinks();
@ -1800,7 +1785,6 @@ YateH323Chan::~YateH323Chan()
hangup();
if (m_conn)
Debug(this,DebugFail,"Still having a connection %p [%p]",m_conn,this);
s_mutex.unlock();
}
void YateH323Chan::zeroRefs()

View File

@ -134,7 +134,6 @@ public:
virtual bool msgAnswered(Message& msg);
virtual bool msgTone(Message& msg, const char* tone);
virtual bool msgText(Message& msg, const char* text);
virtual bool msgDrop(Message& msg, const char* reason);
void startAudio(int format,int capability);
void sourceAudio(void *buffer, int len, int format);
void sendVoice(char* buffer, int len, int format);
@ -848,13 +847,6 @@ bool IAXConnection::msgText(Message& msg, const char* text)
return true;
}
bool IAXConnection::msgDrop(Message& msg, const char* reason)
{
Debug(this,DebugInfo,"Dropping IAX call '%s' [%p]",id().c_str(),this);
disconnect(reason);
return true;
}
bool IAXDriver::msgExecute(Message& msg, String& dest)
{
if (!msg.userData()) {

View File

@ -536,11 +536,18 @@ public:
*/
virtual ~GenObject() { }
/**
* Check if the object is still valid and safe to access.
* Note that you should not trust this result unless the object is locked
* by other means.
* @return True if the object is still useable
*/
virtual bool alive() const;
/**
* Destroys the object, disposes the memory.
*/
virtual void destruct()
{ delete this; }
virtual void destruct();
/**
* Get a string representation of this object
@ -578,10 +585,18 @@ public:
virtual ~RefObject();
/**
* Increments the reference counter
* @return The new reference count
* Check if the object is still referenced and safe to access.
* Note that you should not trust this result unless the object is locked
* by other means.
* @return True if the object is referenced and safe to access
*/
int ref();
virtual bool alive() const;
/**
* Increments the reference counter if not already zero
* @return True if the object was successfully referenced and is safe to access
*/
bool ref();
/**
* Decrements the reference counter, destroys the object if it reaches zero
@ -589,7 +604,7 @@ public:
* // Deref this object, return quickly if the object was deleted
* if (deref()) return;
* </pre>
* @return True if the object was deleted, false if it still exists
* @return True if the object may have been deleted, false if it still exists and is safe to access
*/
bool deref();
@ -604,8 +619,7 @@ public:
* Refcounted objects should just have the counter decremented.
* That will destroy them only when the refcount reaches zero.
*/
virtual void destruct()
{ deref(); }
virtual void destruct();
protected:
/**
@ -614,6 +628,13 @@ protected:
*/
virtual void zeroRefs();
/**
* Bring the object back alive by setting the reference counter to one.
* Note that it works only if the counter was zero previously
* @return True if the object was resurrected - its name may be Lazarus ;-)
*/
bool resurrect();
private:
int m_refcount;
};
@ -898,6 +919,7 @@ private:
* Data is organized in columns - the main ObjList holds pointers to one
* ObjList for each column.
* This class has been written by Diana
* @short A list based Array
*/
class YATE_API Array : public RefObject
{
@ -1863,6 +1885,84 @@ private:
ObjList** m_lists;
};
/**
* An ObjList or HashList iterator that can be used even when list elements
* are changed while iterating. Note that it will not detect that an item was
* removed and another with the same address was inserted back in list.
* @short Class used to iterate the items of a list
*/
class YATE_API ListIterator
{
public:
/**
* Constructor used to iterate trough an ObjList.
* The image of the list is frozen at the time the constructor executes
* @param list List to get the objects from
*/
ListIterator(ObjList& list);
/**
* Constructor used to iterate trough a HashList.
* The image of the list is frozen at the time the constructor executes
* @param list List to get the objects from
*/
ListIterator(HashList& list);
/**
* Destructor - frees the allocated memory
*/
~ListIterator();
/**
* Get the number of elements in the list
* @return Count of items in the internal list
*/
inline unsigned int length() const
{ return m_length; }
/**
* Get an arbitrary element in the iterator's list image.
* Items that were removed from list or are not alive are not returned.
* @param index Position to get the item from
* @return Pointer to the list item or NULL if out of range or item removed
*/
GenObject* get(unsigned int index) const;
/**
* Get the current element and advance the current index.
* Items that were removed from list or are not alive are skipped over.
* An example of typical usage:
* <pre>
* ListIterator iter(list);
* while (GenObject* obj = iter.get()) {
* do_something_with(obj);
* }
* </pre>
* @return Pointer to a list item or NULL if advanced past end (eof)
*/
GenObject* get();
/**
* Check if the current pointer is past the end of the list
* @return True if there are no more entries left
*/
inline bool eof() const
{ return m_current >= m_length; }
/**
* Reset the iterator index to the first position in the list
*/
inline void reset()
{ m_current = 0; }
private:
ObjList* m_objList;
HashList* m_hashList;
GenObject** m_objects;
unsigned int m_length;
unsigned int m_current;
};
/**
* The Time class holds a time moment with microsecond accuracy
* @short A time holding class
@ -2469,7 +2569,7 @@ public:
/**
* Create the lock, try to lock the mutex
* @param mutex Reference to the mutex to lock
* @param maxait Time in microseconds to wait for the mutex, -1 wait forever
* @param maxwait Time in microseconds to wait for the mutex, -1 wait forever
*/
inline Lock(Mutex& mutex, long maxwait = -1)
{ m_mutex = mutex.lock(maxwait) ? &mutex : 0; }
@ -2477,7 +2577,7 @@ public:
/**
* Create the lock, try to lock the mutex
* @param mutex Pointer to the mutex to lock
* @param maxait Time in microseconds to wait for the mutex, -1 wait forever
* @param maxwait Time in microseconds to wait for the mutex, -1 wait forever
*/
inline Lock(Mutex* mutex, long maxwait = -1)
{ m_mutex = (mutex && mutex->lock(maxwait)) ? mutex : 0; }
@ -2514,6 +2614,86 @@ private:
inline Lock(const Lock&);
};
/**
* A dual lock is a stack allocated (automatic) object that locks a pair
* of mutexes on creation and unlocks them on destruction. The mutexes are
* always locked in the same order to prevent trivial deadlocks
* @short Ephemeral double mutex locking object
*/
class YATE_API Lock2
{
public:
/**
* Create the dual lock, try to lock each mutex
* @param mx1 Pointer to the first mutex to lock
* @param mx2 Pointer to the second mutex to lock
* @param maxwait Time in microseconds to wait for each mutex, -1 wait forever
*/
inline Lock2(Mutex* mx1, Mutex* mx2, long maxwait = -1)
: m_mx1(0), m_mx2(0)
{ lock(mx1,mx2,maxwait); }
/**
* Create the dual lock, try to lock each mutex
* @param mx1 Reference to the first mutex to lock
* @param mx2 Reference to the second mutex to lock
* @param maxwait Time in microseconds to wait for each mutex, -1 wait forever
*/
inline Lock2(Mutex& mx1, Mutex& mx2, long maxwait = -1)
: m_mx1(0), m_mx2(0)
{ lock(&mx1,&mx2); }
/**
* Destroy the lock, unlock the mutex if it was locked
*/
inline ~Lock2()
{ drop(); }
/**
* Check if the locking succeeded
* @return True if all mutexes were locked
*/
inline bool locked() const
{ return m_mx1 != 0; }
/**
* Lock in a new pair of mutexes. Any existing locks are dropped
* @param mx1 Pointer to the first mutex to lock
* @param mx2 Pointer to the second mutex to lock
* @param maxwait Time in microseconds to wait for each mutex, -1 wait forever
* @return True on success - non-NULL mutexes locked
*/
bool lock(Mutex* mx1, Mutex* mx2, long maxwait = -1);
/**
* Lock in a new pair of mutexes
* @param mx1 Reference to the first mutex to lock
* @param mx2 Reference to the second mutex to lock
* @param maxwait Time in microseconds to wait for each mutex, -1 wait forever
* @return True on success - both locked
*/
inline bool lock(Mutex& mx1, Mutex& mx2, long maxwait = -1)
{ return lock(&mx1,&mx2); }
/**
* Unlock both mutexes if they were locked and drop the references
*/
void drop();
private:
Mutex* m_mx1;
Mutex* m_mx2;
/** Make sure no Lock2 is ever created on heap */
inline void* operator new(size_t);
/** Never allocate an array of this class */
inline void* operator new[](size_t);
/** No copy constructor */
inline Lock2(const Lock2&);
};
/**
* This class holds the action to execute a certain task, usually in a
* different execution thread.
@ -2578,6 +2758,18 @@ public:
*/
bool running() const;
/**
* Get the name of this thread
* @return The pointer that was passed in the constructor
*/
const char* name() const;
/**
* Get the name of the currently running thread
* @return The pointer that was passed in the thread's constructor
*/
static const char* currentName();
/**
* Give up the currently running timeslice. Note that on some platforms
* it also sleeps for the operating system's scheduler resolution

View File

@ -843,6 +843,12 @@ public:
inline Mutex* mutex() const
{ return m_mutex; }
/**
* Get the big mutex that serializes access to all call endpoints
* @return A reference to the mutex
*/
static Mutex& commonMutex();
/**
* Connect the call endpoint to a peer.
* @param peer Pointer to the peer call endpoint.