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