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) AC_SUBST(INLINE_FLAGS)
FDSIZE_HACK="" 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 if [[ "x$ac_cv_use_fdsize" != "xno" ]]; then
FDSIZE_HACK="-DFDSIZE_HACK=$ac_cv_use_fdsize" FDSIZE_HACK="-DFDSIZE_HACK=$ac_cv_use_fdsize"
fi fi

View File

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

View File

@ -113,7 +113,7 @@ void HashList::clear()
bool HashList::resync(GenObject* obj) bool HashList::resync(GenObject* obj)
{ {
XDebug(DebugAll,"HashList::resync(%p) [%p]",this); XDebug(DebugAll,"HashList::resync(%p) [%p]",obj,this);
if (!obj) if (!obj)
return false; return false;
unsigned int i = obj->toString().hash() % m_size; 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 CLINC:= $(PINC) @top_srcdir@/yatecbase.h
LIBS := LIBS :=
CLSOBJS := TelEngine.o ObjList.o HashList.o String.o DataBlock.o NamedList.o \ 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 ENGOBJS := Configuration.o Message.o Plugin.o Engine.o
TELOBJS := DataFormat.o Channel.o TELOBJS := DataFormat.o Channel.o
CLIOBJS := Client.o CLIOBJS := Client.o

View File

@ -68,6 +68,7 @@ private:
int m_refcount; int m_refcount;
volatile unsigned int m_locked; volatile unsigned int m_locked;
bool m_recursive; bool m_recursive;
const char* m_owner;
}; };
class GlobalMutex { class GlobalMutex {
@ -138,8 +139,9 @@ void GlobalMutex::unlock()
#endif #endif
} }
MutexPrivate::MutexPrivate(bool recursive) 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(); GlobalMutex::lock();
s_count++; s_count++;
@ -220,12 +222,14 @@ bool MutexPrivate::lock(long maxwait)
if (rval) { if (rval) {
s_locks++; s_locks++;
m_locked++; m_locked++;
m_owner = Thread::currentName();
} }
else else
deref(); deref();
GlobalMutex::unlock(); GlobalMutex::unlock();
if (warn && !rval) 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; return rval;
} }
@ -234,7 +238,8 @@ void MutexPrivate::unlock()
// Hope we don't hit a bug related to the debug mutex! // Hope we don't hit a bug related to the debug mutex!
GlobalMutex::lock(); GlobalMutex::lock();
if (m_locked) { if (m_locked) {
m_locked--; if (!--m_locked)
m_owner = 0;
if (--s_locks < 0) if (--s_locks < 0)
Debug(DebugFail,"MutexPrivate::locks() is %d [%p]",s_locks,this); Debug(DebugFail,"MutexPrivate::locks() is %d [%p]",s_locks,this);
#ifdef _WINDOWS #ifdef _WINDOWS
@ -249,6 +254,7 @@ void MutexPrivate::unlock()
GlobalMutex::unlock(); GlobalMutex::unlock();
} }
Mutex::Mutex() Mutex::Mutex()
: m_private(0) : m_private(0)
{ {
@ -334,4 +340,46 @@ void Mutex::wait(unsigned long maxwait)
s_maxwait = 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: */ /* 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; static Mutex s_refmutex;
RefObject::~RefObject() RefObject::~RefObject()
@ -387,40 +398,61 @@ RefObject::~RefObject()
Debug(DebugFail,"RefObject [%p] destroyed with count=%d",this,m_refcount); 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(); s_refmutex.lock();
int i = ++m_refcount; bool ret = (m_refcount > 0);
if (ret)
++m_refcount;
s_refmutex.unlock(); s_refmutex.unlock();
return i; return ret;
} }
bool RefObject::deref() bool RefObject::deref()
{ {
s_refmutex.lock(); s_refmutex.lock();
int i = --m_refcount; int i = m_refcount;
if (i > 0)
--m_refcount;
s_refmutex.unlock(); s_refmutex.unlock();
if (i == 0) if (i == 1)
zeroRefs(); zeroRefs();
return (i <= 0); return (i <= 1);
} }
void RefObject::zeroRefs() void RefObject::zeroRefs()
{ {
s_refmutex.lock();
m_refcount = -1;
s_refmutex.unlock();
delete this; 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) void RefPointerBase::assign(RefObject* oldptr, RefObject* newptr, void* pointer)
{ {
if (oldptr == newptr) if (oldptr == newptr)
return; return;
// Always reference the new object before dereferencing the old one // Always reference the new object before dereferencing the old one
if (newptr) // and also don't keep pointers to objects that fail referencing
newptr->ref(); m_pointer = (newptr && newptr->ref()) ? pointer : 0;
m_pointer = pointer;
if (oldptr) if (oldptr)
oldptr->deref(); oldptr->deref();
} }

View File

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

View File

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

View File

@ -134,7 +134,6 @@ public:
virtual bool msgAnswered(Message& msg); virtual bool msgAnswered(Message& msg);
virtual bool msgTone(Message& msg, const char* tone); virtual bool msgTone(Message& msg, const char* tone);
virtual bool msgText(Message& msg, const char* text); virtual bool msgText(Message& msg, const char* text);
virtual bool msgDrop(Message& msg, const char* reason);
void startAudio(int format,int capability); void startAudio(int format,int capability);
void sourceAudio(void *buffer, int len, int format); void sourceAudio(void *buffer, int len, int format);
void sendVoice(char* 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; 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) bool IAXDriver::msgExecute(Message& msg, String& dest)
{ {
if (!msg.userData()) { if (!msg.userData()) {

View File

@ -536,11 +536,18 @@ public:
*/ */
virtual ~GenObject() { } 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. * Destroys the object, disposes the memory.
*/ */
virtual void destruct() virtual void destruct();
{ delete this; }
/** /**
* Get a string representation of this object * Get a string representation of this object
@ -578,10 +585,18 @@ public:
virtual ~RefObject(); virtual ~RefObject();
/** /**
* Increments the reference counter * Check if the object is still referenced and safe to access.
* @return The new reference count * 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 * 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 * // Deref this object, return quickly if the object was deleted
* if (deref()) return; * if (deref()) return;
* </pre> * </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(); bool deref();
@ -604,8 +619,7 @@ public:
* Refcounted objects should just have the counter decremented. * Refcounted objects should just have the counter decremented.
* That will destroy them only when the refcount reaches zero. * That will destroy them only when the refcount reaches zero.
*/ */
virtual void destruct() virtual void destruct();
{ deref(); }
protected: protected:
/** /**
@ -614,6 +628,13 @@ protected:
*/ */
virtual void zeroRefs(); 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: private:
int m_refcount; int m_refcount;
}; };
@ -898,6 +919,7 @@ private:
* Data is organized in columns - the main ObjList holds pointers to one * Data is organized in columns - the main ObjList holds pointers to one
* ObjList for each column. * ObjList for each column.
* This class has been written by Diana * This class has been written by Diana
* @short A list based Array
*/ */
class YATE_API Array : public RefObject class YATE_API Array : public RefObject
{ {
@ -1863,6 +1885,84 @@ private:
ObjList** m_lists; 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 * The Time class holds a time moment with microsecond accuracy
* @short A time holding class * @short A time holding class
@ -2469,7 +2569,7 @@ public:
/** /**
* Create the lock, try to lock the mutex * Create the lock, try to lock the mutex
* @param mutex Reference to the mutex to lock * @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) inline Lock(Mutex& mutex, long maxwait = -1)
{ m_mutex = mutex.lock(maxwait) ? &mutex : 0; } { m_mutex = mutex.lock(maxwait) ? &mutex : 0; }
@ -2477,7 +2577,7 @@ public:
/** /**
* Create the lock, try to lock the mutex * Create the lock, try to lock the mutex
* @param mutex Pointer to the mutex to lock * @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) inline Lock(Mutex* mutex, long maxwait = -1)
{ m_mutex = (mutex && mutex->lock(maxwait)) ? mutex : 0; } { m_mutex = (mutex && mutex->lock(maxwait)) ? mutex : 0; }
@ -2514,6 +2614,86 @@ private:
inline Lock(const Lock&); 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 * This class holds the action to execute a certain task, usually in a
* different execution thread. * different execution thread.
@ -2578,6 +2758,18 @@ public:
*/ */
bool running() const; 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 * Give up the currently running timeslice. Note that on some platforms
* it also sleeps for the operating system's scheduler resolution * it also sleeps for the operating system's scheduler resolution

View File

@ -843,6 +843,12 @@ public:
inline Mutex* mutex() const inline Mutex* mutex() const
{ return m_mutex; } { 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. * Connect the call endpoint to a peer.
* @param peer Pointer to the peer call endpoint. * @param peer Pointer to the peer call endpoint.