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:
parent
07ae2de4e1
commit
3490d2d891
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: */
|
|
@ -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
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()) {
|
||||
|
|
212
yateclass.h
212
yateclass.h
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue