diff --git a/configure.in b/configure.in index 3122f72e..bcc5ecf4 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/engine/Channel.cpp b/engine/Channel.cpp index 2cec46a4..2f9db2f4 100644 --- a/engine/Channel.cpp +++ b/engine/Channel.cpp @@ -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(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 c = static_cast(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 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(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 c = static_cast(iter.get()); + unlock(); + if (!c) + break; + DDebug(this,DebugAll,"Dropping %s channel %p [%p]", + name().c_str(),static_cast(c),this); + c->msgDrop(msg,reason); + lock(); } - unlock(); } bool Driver::canAccept(bool routers) diff --git a/engine/HashList.cpp b/engine/HashList.cpp index ac0bcc7a..57b0e7e9 100644 --- a/engine/HashList.cpp +++ b/engine/HashList.cpp @@ -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; diff --git a/engine/Iterator.cpp b/engine/Iterator.cpp new file mode 100644 index 00000000..15afd082 --- /dev/null +++ b/engine/Iterator.cpp @@ -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: */ diff --git a/engine/Makefile.in b/engine/Makefile.in index 75118ce2..582f37b6 100644 --- a/engine/Makefile.in +++ b/engine/Makefile.in @@ -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 diff --git a/engine/Mutex.cpp b/engine/Mutex.cpp index 1232b080..439ec718 100644 --- a/engine/Mutex.cpp +++ b/engine/Mutex.cpp @@ -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: */ diff --git a/engine/TelEngine.cpp b/engine/TelEngine.cpp index 585279b4..55fd5235 100644 --- a/engine/TelEngine.cpp +++ b/engine/TelEngine.cpp @@ -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(); } diff --git a/engine/Thread.cpp b/engine/Thread.cpp index 26ac60be..c5fd4c0c 100644 --- a/engine/Thread.cpp +++ b/engine/Thread.cpp @@ -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); diff --git a/modules/dbpbx.cpp b/modules/dbpbx.cpp index 2d6bf271..104bb2f4 100644 --- a/modules/dbpbx.cpp +++ b/modules/dbpbx.cpp @@ -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); } diff --git a/modules/h323chan.cpp b/modules/h323chan.cpp index 72041511..30cea3e4 100644 --- a/modules/h323chan.cpp +++ b/modules/h323chan.cpp @@ -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() diff --git a/modules/iaxchan.cpp b/modules/iaxchan.cpp index 6e4a1c7d..e4916605 100644 --- a/modules/iaxchan.cpp +++ b/modules/iaxchan.cpp @@ -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()) { diff --git a/yateclass.h b/yateclass.h index 1e7112e2..61ecdbdd 100644 --- a/yateclass.h +++ b/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; * - * @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: + *
+     * ListIterator iter(list);
+     * while (GenObject* obj = iter.get()) {
+     *     do_something_with(obj);
+     * }
+     * 
+ * @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 diff --git a/yatephone.h b/yatephone.h index 4dcc675c..265f8b45 100644 --- a/yatephone.h +++ b/yatephone.h @@ -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.