void RTPGroup::run() { DDebug(DebugInfo,"RTPGroup::run() [%p]",this); bool ok = true; while (ok) { unsigned long msec = m_sleep; if (msec < s_sleep) msec = s_sleep; lock(); Time t; ObjList* l = &m_processors; m_listChanged = false; for (ok = false;l;l = l->next()) { RTPProcessor* p = static_cast<RTPProcessor*>(l->get()); if (p) { ok = true; p->timerTick(t); // the list is protected from other threads but can be changed // from this one so if it happened we just break out and try // again later rather than using an expensive ListIterator if (m_listChanged) break; } } unlock(); Thread::msleep(msec,true); } DDebug(DebugInfo,"RTPGroup::run() ran out of processors [%p]",this); }
bool HashList::resync() { XDebug(DebugAll,"HashList::resync() [%p]",this); bool moved = false; for (unsigned int n = 0; n < m_size; n++) { ObjList* l = m_lists[n]; while (l) { GenObject* obj = l->get(); if (obj) { unsigned int i = obj->toString().hash() % m_size; if (i != n) { bool autoDel = l->autoDelete(); m_lists[n]->remove(obj,false); if (!m_lists[i]) m_lists[i] = new ObjList; m_lists[i]->append(obj)->setDelete(autoDel); moved = true; continue; } } l = l->next(); } } return moved; }
GenObject* ObjList::remove(bool delobj) { GenObject *tmp = m_obj; if (m_next) { ObjList *n = m_next; m_next = n->next(); m_obj = n->get(); m_delete = n->m_delete; n->m_obj = 0; n->m_next = 0; n->destruct(); } else m_obj = 0; if (delobj && tmp) { XDebug(DebugInfo,"ObjList::remove() deleting %p",tmp); // Don't use TelEngine::destruct(): the compiler will call the non-template // function (which doesn't reset the pointer) tmp->destruct(); tmp = 0; } return tmp; }
void Socket::timerTick(const Time& when) { for (ObjList* l = &m_filters; l; l = l->next()) { SocketFilter* filter = static_cast<SocketFilter*>(l->get()); if (filter) filter->timerTick(when); } }
ObjList* ObjList::operator+(int index) const { if (index < 0) return 0; ObjList *obj = const_cast<ObjList*>(this); for (;obj;obj=obj->next(),index--) if (!index) break; return obj; }
ExpOperation* ExpEvaluator::popOpcode() { ObjList* l = &m_opcodes; for (ObjList* p = l; p; p = p->next()) { if (p->get()) l = p; } return static_cast<ExpOperation*>(l->remove(false)); }
ToneConsumer::ToneConsumer(const String& id, const String& name) : m_id(id), m_name(name), m_mode(Mono), m_detFax(true), m_detCont(false), m_detDtmf(true), m_detDnis(false), m_fax(s_paramsCNG), m_cont(s_paramsCOTv) { Debug(&plugin,DebugAll,"ToneConsumer::ToneConsumer(%s,'%s') [%p]", id.c_str(),name.c_str(),this); for (int i = 0; i < 4; i++) { m_dtmfL[i].assign(s_paramsDtmfL[i]); m_dtmfH[i].assign(s_paramsDtmfH[i]); } init(); String tmp = name; tmp.startSkip("tone/",false); if (tmp.startSkip("mixed/",false)) m_mode = Mixed; else if (tmp.startSkip("left/",false)) m_mode = Left; else if (tmp.startSkip("right/",false)) m_mode = Right; else tmp.startSkip("mono/",false); if (m_mode != Mono) m_format = "2*slin"; if (tmp && (tmp != "*")) { // individual detection requested m_detFax = m_detCont = m_detDtmf = m_detDnis = false; ObjList* k = tmp.split(',',false); for (ObjList* l = k; l; l = l->next()) { String* s = static_cast<String*>(l->get()); if (!s) continue; m_detFax = m_detFax || (*s == "fax"); m_detCont = m_detCont || (*s == "cotv"); m_detDtmf = m_detDtmf || (*s == "dtmf"); if (*s == "rfax") { // detection of receiving Fax requested m_fax.assign(s_paramsCED); m_detFax = true; } else if (*s == "cots") { // detection of COT Send tone requested m_cont.assign(s_paramsCOTs); m_detCont = true; } else if (*s == "callsetup") { // call setup info in the form *ANI*DNIS* m_detDnis = true; } } TelEngine::destruct(k); } s_mutex.lock(); s_count++; s_mutex.unlock(); }
SIPTransaction* SIPEngine::addMessage(SIPMessage* message) { DDebug(this,DebugInfo,"addMessage(%p) [%p]",message,this); if (!message) return 0; // make sure outgoing messages are well formed if (message->isOutgoing()) message->complete(this); // locate the branch parameter of last Via header - added by the UA const MimeHeaderLine* hl = message->getLastHeader("Via"); if (!hl) #ifdef SIP_STRICT return 0; #else Debug(this,DebugMild,"Received message with no Via header! (sender bug)"); #endif const NamedString* br = hl ? hl->getParam("branch") : 0; String branch; if (br && br->startsWith("z9hG4bK")) branch = *br; Lock lock(this); SIPTransaction* forked = 0; ObjList* l = &m_transList; for (; l; l = l->next()) { SIPTransaction* t = static_cast<SIPTransaction*>(l->get()); if (!t) continue; switch (t->processMessage(message,branch)) { case SIPTransaction::Matched: return t; case SIPTransaction::NoDialog: forked = t; break; case SIPTransaction::NoMatch: default: break; } } if (forked) return forkInvite(message,forked); if (message->isAnswer()) { Debug(this,DebugInfo,"Message %p was an unhandled answer [%p]",message,this); return 0; } if (message->isACK()) { DDebug(this,DebugAll,"Message %p was an unhandled ACK [%p]",message,this); return 0; } message->complete(this); return new SIPTransaction(message,this,message->isOutgoing()); }
bool Socket::applyFilters(void* buffer, int length, int flags, const struct sockaddr* addr, socklen_t adrlen) { if ((length <= 0) || !buffer) return false; if (!addr) adrlen = 0; for (ObjList* l = &m_filters; l; l = l->next()) { SocketFilter* filter = static_cast<SocketFilter*>(l->get()); if (filter && filter->received(buffer,length,flags,addr,adrlen)) return true; } return false; }
NamedList& NamedList::clearParam(const String& name, char childSep) { XDebug(DebugInfo,"NamedList::clearParam(\"%s\",'%.1s')", name.c_str(),&childSep); String tmp; if (childSep) tmp << name << childSep; ObjList *p = &m_params; while (p) { NamedString *s = static_cast<NamedString *>(p->get()); if (s && ((s->name() == name) || s->name().startsWith(tmp))) p->remove(); else p = p->next(); } return *this; }
void RTPGroup::cleanup() { DDebug(DebugInfo,"RTPGroup::cleanup() [%p]",this); lock(); m_listChanged = true; ObjList* l = &m_processors; while (l) { RTPProcessor* p = static_cast<RTPProcessor*>(l->get()); if (p) { p->group(0); if (p != static_cast<RTPProcessor*>(l->get())) continue; } l = l->next(); } m_processors.clear(); unlock(); }
GenObject* ObjList::remove(bool delobj) { GenObject *tmp = m_obj; if (m_next) { ObjList *n = m_next; m_next = n->next(); m_obj = n->get(); m_delete = n->m_delete; n->m_obj = 0; n->m_next = 0; n->destruct(); } else m_obj = 0; if (delobj && tmp) { XDebug(DebugInfo,"ObjList::remove() deleting %p",tmp); TelEngine::destruct(tmp); } return tmp; }
bool MessageDispatcher::install(MessageHandler* handler) { DDebug(DebugAll,"MessageDispatcher::install(%p)",handler); if (!handler) return false; Lock lock(this); ObjList *l = m_handlers.find(handler); if (l) return false; unsigned p = handler->priority(); int pos = 0; for (l=&m_handlers; l; l=l->next(),pos++) { MessageHandler *h = static_cast<MessageHandler *>(l->get()); if (!h) continue; if (h->priority() < p) continue; if (h->priority() > p) break; // at the same priority we sort them in pointer address order if (h > handler) break; } m_changes++; if (l) { XDebug(DebugAll,"Inserting handler [%p] on place #%d",handler,pos); l->insert(handler); } else { XDebug(DebugAll,"Appending handler [%p] on place #%d",handler,pos); m_handlers.append(handler); } handler->m_dispatcher = this; if (handler->null()) Debug(DebugInfo,"Registered broadcast message handler %p",handler); return true; }
bool MessageDispatcher::dispatch(Message& msg) { #ifdef XDEBUG Debugger debug("MessageDispatcher::dispatch","(%p) (\"%s\")",&msg,msg.c_str()); #endif #ifndef NDEBUG u_int64_t t = Time::now(); #endif bool retv = false; ObjList *l = &m_handlers; Lock mylock(this); for (; l; l=l->next()) { MessageHandler *h = static_cast<MessageHandler*>(l->get()); if (h && (h->null() || *h == msg)) { if (h->filter() && (*(h->filter()) != msg.getValue(h->filter()->name()))) continue; unsigned int c = m_changes; unsigned int p = h->priority(); if (trackParam() && h->trackName()) { NamedString* tracked = msg.getParam(trackParam()); if (tracked) tracked->append(h->trackName(),","); else msg.addParam(trackParam(),h->trackName()); } // mark handler as unsafe to destroy / uninstall h->m_unsafe++; mylock.drop(); #ifdef DEBUG u_int64_t tm = Time::now(); #endif retv = h->receivedInternal(msg) || retv; #ifdef DEBUG tm = Time::now() - tm; if (m_warnTime && (tm > m_warnTime)) Debug(DebugInfo,"Message '%s' [%p] passed through %p in " FMT64U " usec", msg.c_str(),&msg,h,tm); #endif if (retv && !msg.broadcast()) break; mylock.acquire(this); if (c == m_changes) continue; // the handler list has changed - find again NDebug(DebugAll,"Rescanning handler list for '%s' [%p] at priority %u", msg.c_str(),&msg,p); ObjList* l2 = &m_handlers; for (l = l2; l; l=l->next()) { MessageHandler *mh = static_cast<MessageHandler*>(l->get()); if (!mh) continue; if (mh == h) // exact match - silently continue where we left break; // gone past last handler priority - exit with last handler if ((mh->priority() > p) || ((mh->priority() == p) && (mh > h))) { Debug(DebugAll,"Handler list for '%s' [%p] changed, skipping from %p (%u) to %p (%u)", msg.c_str(),&msg,h,p,mh,mh->priority()); // l will advance in the outer for loop so use previous l = l2; break; } l2 = l; } if (!l) break; } } mylock.drop(); msg.dispatched(retv); #ifndef NDEBUG t = Time::now() - t; if (m_warnTime && (t > m_warnTime)) { unsigned n = msg.length(); String p; for (unsigned i = 0; i < n; i++) { NamedString *s = msg.getParam(i); if (s) p << "\n ['" << s->name() << "']='" << *s << "'"; } Debug("Performance",DebugMild,"Message %p '%s' retval '%s' returned %s in " FMT64U " usec%s", &msg,msg.c_str(),msg.retValue().c_str(),retv ? "true" : "false",t,p.safe()); } #endif l = &m_hooks; for (; l; l=l->next()) { MessagePostHook *h = static_cast<MessagePostHook*>(l->get()); if (h) h->dispatched(msg,retv); } return retv; }
bool MessageDispatcher::dispatch(Message& msg) { #ifdef XDEBUG Debugger debug("MessageDispatcher::dispatch","(%p) (\"%s\")",&msg,msg.c_str()); #endif #ifndef NDEBUG u_int64_t t = Time::now(); #endif bool retv = false; ObjList *l = &m_handlers; m_mutex.lock(); for (; l; l=l->next()) { MessageHandler *h = static_cast<MessageHandler*>(l->get()); if (h && (h->null() || *h == msg)) { if (h->filter() && (*(h->filter()) != msg.getValue(h->filter()->name()))) continue; unsigned int c = m_changes; unsigned int p = h->priority(); m_mutex.unlock(); #ifdef DEBUG u_int64_t tm = Time::now(); #endif retv = h->received(msg); #ifdef DEBUG tm = Time::now() - tm; if (m_warnTime && (tm > m_warnTime)) Debug(DebugInfo,"Message '%s' [%p] passed through %p in " FMT64U " usec", msg.c_str(),&msg,h,tm); #endif if (retv) break; m_mutex.lock(); if (c == m_changes) continue; // the handler list has changed - find again NDebug(DebugAll,"Rescanning handler list for '%s' [%p] at priority %u", msg.c_str(),&msg,p); for (l = &m_handlers; l; l=l->next()) { MessageHandler *mh = static_cast<MessageHandler*>(l->get()); if (!mh) continue; if (mh == h) // exact match - silently continue where we left break; // gone past last handler priority - exit with last handler if ((mh->priority() > p) || ((mh->priority() == p) && (mh > h))) { Debug(DebugAll,"Handler list for '%s' [%p] changed, skipping from %p (%u) to %p (%u)", msg.c_str(),&msg,h,p,mh,mh->priority()); break; } } } } if (!l) m_mutex.unlock(); msg.dispatched(retv); #ifndef NDEBUG t = Time::now() - t; if (m_warnTime && (t > m_warnTime)) { unsigned n = msg.length(); String p; for (unsigned i = 0; i < n; i++) { NamedString *s = msg.getParam(i); if (s) p << "\n ['" << s->name() << "']='" << *s << "'"; } Debug("Performance",DebugMild,"Message %p '%s' retval '%s' returned %s in " FMT64U " usec%s", &msg,msg.c_str(),msg.retValue().c_str(),retv ? "true" : "false",t,p.safe()); } #endif l = &m_hooks; for (; l; l=l->next()) { MessagePostHook *h = static_cast<MessagePostHook*>(l->get()); if (h) h->dispatched(msg,retv); } return retv; }
// Creates a SDP body from transport address and list of media descriptors // Use own list if given media list is 0 MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList) { DDebug(m_parser,DebugAll,"SDPSession::createSDP('%s',%p) [%p]",addr,mediaList,this); if (!mediaList) mediaList = m_rtpMedia; // if we got no media descriptors we simply create no SDP if (!mediaList) return 0; if (m_sdpSession) ++m_sdpVersion; else m_sdpVersion = m_sdpSession = Time::secNow(); // no address means on hold or muted String origin; origin << "yate " << m_sdpSession << " " << m_sdpVersion; origin << " IN IP4 " << (addr ? addr : m_host.safe()); String conn; conn << "IN IP4 " << (addr ? addr : "0.0.0.0"); MimeSdpBody* sdp = new MimeSdpBody; sdp->addLine("v","0"); sdp->addLine("o",origin); sdp->addLine("s",m_parser->m_sessionName); sdp->addLine("c",conn); sdp->addLine("t","0 0"); Lock lock(m_parser); bool defcodecs = m_parser->m_codecs.getBoolValue("default",true); for (ObjList* ml = mediaList->skipNull(); ml; ml = ml->skipNext()) { SDPMedia* m = static_cast<SDPMedia*>(ml->get()); String mline(m->fmtList()); ObjList* l = mline.split(',',false); mline = *m; mline << " " << (m->localPort() ? m->localPort().c_str() : "0") << " " << m->transport(); ObjList* map = m->mappings().split(',',false); ObjList rtpmap; String frm; int ptime = 0; ObjList* f = l; for (; f; f = f->next()) { String* s = static_cast<String*>(f->get()); if (s) { int mode = 0; if (*s == "ilbc20") ptime = mode = 20; else if (*s == "ilbc30") ptime = mode = 30; else if (*s == "g729b") continue; int payload = s->toInteger(SDPParser::s_payloads,-1); int defcode = payload; String tmp = *s; tmp << "="; for (ObjList* pl = map; pl; pl = pl->next()) { String* mapping = static_cast<String*>(pl->get()); if (!mapping) continue; if (mapping->startsWith(tmp)) { payload = -1; tmp = *mapping; tmp >> "=" >> payload; XDebug(m_parser,DebugAll,"RTP mapped payload %d for '%s' [%p]", payload,s->c_str(),this); break; } } if (payload >= 0) { if (defcode < 0) defcode = payload; const char* map = lookup(defcode,SDPParser::s_rtpmap); if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) { frm << " " << payload; String* temp = new String("rtpmap:"); *temp << payload << " " << map; rtpmap.append(temp); if (mode) { temp = new String("fmtp:"); *temp << payload << " mode=" << mode; rtpmap.append(temp); } if (*s == "g729") { temp = new String("fmtp:"); *temp << payload << " annexb=" << ((0 != l->find("g729b")) ? "yes" : "no"); rtpmap.append(temp); } else if (*s == "amr") { temp = new String("fmtp:"); *temp << payload << " octet-align=0"; rtpmap.append(temp); } else if (*s == "amr-o") { temp = new String("fmtp:"); *temp << payload << " octet-align=1"; rtpmap.append(temp); } if(s->length()) { String key("fmtp-"); key << *s; for(unsigned int i = 0; i < m->length(); ++i) { const NamedString *ns = m->getParam(i); if(ns && ns->name() == key) { temp = new String("fmtp:"); *temp << payload << " " << *ns; rtpmap.append(temp); } } } } } } } TelEngine::destruct(l); TelEngine::destruct(map); if ((m_rfc2833 >= 0) && frm && m->isAudio()) { int rfc2833 = m->rfc2833().toInteger(m_rfc2833); if (rfc2833 < 96 || rfc2833 > 127) rfc2833 = 101; // claim to support telephone events frm << " " << rfc2833; String* s = new String; *s << "rtpmap:" << rfc2833 << " telephone-event/8000"; rtpmap.append(s); } if (frm.null()) { if (m->isAudio() || !m->fmtList()) { Debug(m_parser,DebugMild,"No formats for '%s', excluding from SDP [%p]", m->c_str(),this); continue; } Debug(m_parser,DebugInfo,"Assuming formats '%s' for media '%s' [%p]", m->fmtList(),m->c_str(),this); frm << " " << m->fmtList(); // brutal but effective for (char* p = const_cast<char*>(frm.c_str()); *p; p++) { if (*p == ',') *p = ' '; } } if (ptime) { String* temp = new String("ptime:"); *temp << ptime; rtpmap.append(temp); } sdp->addLine("m",mline + frm); bool enc = false; if (m->isModified()) { unsigned int n = m->length(); for (unsigned int i = 0; i < n; i++) { const NamedString* param = m->getParam(i); if (param) { String tmp = param->name(); if (tmp.startsWith("fmtp-")) continue; if (*param) tmp << ":" << *param; sdp->addLine("a",tmp); enc = enc || (param->name() == "encryption"); } } } for (f = rtpmap.skipNull(); f; f = f->skipNext()) { String* s = static_cast<String*>(f->get()); if (s) sdp->addLine("a",*s); } if (addr && m->localCrypto()) { sdp->addLine("a","crypto:" + m->localCrypto()); if (!enc) sdp->addLine("a","encryption:optional"); } }
// Creates a SDP body from transport address and list of media descriptors // Use own list if given media list is 0 MimeSdpBody* SDPSession::createSDP(const char* addr, ObjList* mediaList) { DDebug(m_enabler,DebugAll,"SDPSession::createSDP('%s',%p) [%p]",addr,mediaList,m_ptr); if (!mediaList) mediaList = m_rtpMedia; // if we got no media descriptors we simply create no SDP if (!mediaList) return 0; if (m_sdpSession) ++m_sdpVersion; else m_sdpVersion = m_sdpSession = Time::secNow(); // override the address with the externally advertised if needed if (addr && m_rtpNatAddr) addr = m_rtpNatAddr; if (!m_originAddr) m_originAddr = addr ? addr : m_host.safe(); // no address means on hold or muted String origin; origin << "yate " << m_sdpSession << " " << m_sdpVersion; origin << " "; int f = addIP(origin,m_originAddr); String conn; addIP(conn,addr,f); MimeSdpBody* sdp = new MimeSdpBody; sdp->addLine("v","0"); sdp->addLine("o",origin); sdp->addLine("s",m_parser->m_sessionName); sdp->addLine("c",conn); sdp->addLine("t","0 0"); Lock lock(m_parser); bool defcodecs = m_parser->m_codecs.getBoolValue("default",true); for (ObjList* ml = mediaList->skipNull(); ml; ml = ml->skipNext()) { SDPMedia* m = static_cast<SDPMedia*>(ml->get()); int rfc2833 = 0; if ((m_rfc2833 >= 0) && m->isAudio()) { if (!m_rtpForward) { rfc2833 = m->rfc2833().toInteger(m_rfc2833); if (rfc2833 < 96 || rfc2833 > 127) rfc2833 = 101; } else if (m->rfc2833().toBoolean(true)) { rfc2833 = m->rfc2833().toInteger(); if (rfc2833 < 96 || rfc2833 > 127) rfc2833 = 0; } } String mline(m->fmtList()); ObjList* l = mline.split(',',false); mline = *m; mline << " " << (m->localPort() ? m->localPort().c_str() : "0") << " " << m->transport(); ObjList* map = m->mappings().split(',',false); ObjList rtpmap; ObjList* dest = &rtpmap; String frm; int ptime = 0; ObjList* f = l; for (; f; f = f->next()) { const String* s = static_cast<const String*>(f->get()); if (s) { int mode = 0; if (*s == "g729b") continue; int payload = s->toInteger(SDPParser::s_payloads,-1); int defcode = payload; String tmp = *s; tmp << "="; bool found = false; for (ObjList* pl = map; pl; pl = pl->next()) { const String* mapping = static_cast<const String*>(pl->get()); if (!mapping) continue; if (mapping->startsWith(tmp)) { payload = -1; tmp = *mapping; tmp >> "=" >> payload; found = true; XDebug(m_enabler,DebugAll,"RTP mapped payload %d for '%s' [%p]", payload,s->c_str(),m_ptr); break; } String tmp2 = *mapping; int pload; tmp2 >> "=" >> pload; if (payload == pload) { XDebug(m_enabler,DebugAll,"RTP conflict for payload %d, allocating new [%p]", payload,m_ptr); payload = -1; u_int32_t bmap = 0; for (ObjList* sl = map; sl; sl = sl->next()) { mapping = static_cast<const String*>(sl->get()); if (!mapping) continue; tmp2 = *mapping; pload = 0; tmp2 >> "=" >> pload; if (pload >= 96 && pload < 127) bmap |= 1 << (pload - 96); } // allocate free and non-standard is possible for (pload = 96; pload < 127; pload++) { if (pload == rfc2833) continue; if (lookup(pload,SDPParser::s_rtpmap)) continue; if ((bmap & (1 << (pload - 96))) == 0) { payload = pload; break; } } if (payload >= 0) break; // none free, allocate from "standard" ones too for (pload = 96; pload < 127; pload++) { if (pload == rfc2833) continue; if ((bmap & (1 << (pload - 96))) == 0) { payload = pload; break; } } break; } } if (payload >= 0) { if (!found) { tmp = *s; tmp << "=" << payload; map->append(new String(tmp)); } if (defcode < 0) defcode = payload; const char* map = lookup(defcode,SDPParser::s_rtpmap); if (map && m_parser->m_codecs.getBoolValue(*s,defcodecs && DataTranslator::canConvert(*s))) { if (*s == "ilbc20") ptime = mode = 20; else if (*s == "ilbc30") ptime = mode = 30; frm << " " << payload; String* temp = new String("rtpmap:"); *temp << payload << " " << map; dest = dest->append(temp); if (mode) { temp = new String("fmtp:"); *temp << payload << " mode=" << mode; dest = dest->append(temp); } if (*s == "g729") { temp = new String("fmtp:"); *temp << payload << " annexb=" << ((0 != l->find("g729b")) ? "yes" : "no"); dest = dest->append(temp); } else if (*s == "amr") { temp = new String("fmtp:"); *temp << payload << " octet-align=0"; dest = dest->append(temp); } else if (*s == "amr-o") { temp = new String("fmtp:"); *temp << payload << " octet-align=1"; dest = dest->append(temp); } } } } }
void ThreadPrivate::killall() { Debugger debug("ThreadPrivate::killall()"); ThreadPrivate *t; bool sledgehammer = false; s_tmutex.lock(); ThreadPrivate* crt = ThreadPrivate::current(); int c = s_threads.count(); if (crt) Debug(DebugNote,"Thread '%s' is soft cancelling other %d running threads",crt->m_name,c-1); else Debug(DebugNote,"Soft cancelling %d running threads",c); ObjList* l = &s_threads; while (l && (t = static_cast<ThreadPrivate *>(l->get())) != 0) { if (t != crt) { Debug(DebugInfo,"Stopping ThreadPrivate '%s' [%p]",t->m_name,t); t->cancel(false); } l = l->next(); } for (int w = 0; w < SOFT_WAITS; w++) { s_tmutex.unlock(); Thread::idle(); s_tmutex.lock(); c = s_threads.count(); // ignore the current thread if we have one if (crt && c) c--; if (!c) { s_tmutex.unlock(); return; } } Debug(DebugMild,"Hard cancelling %d remaining threads",c); l = &s_threads; c = 1; while (l && (t = static_cast<ThreadPrivate *>(l->get())) != 0) { if (t == crt) { l = l->next(); continue; } Debug(DebugInfo,"Trying to kill ThreadPrivate '%s' [%p], attempt %d",t->m_name,t,c); bool ok = t->cancel(true); if (ok) { int d = 0; // delay a little (exponentially) so threads have a chance to clean up for (int i=1; i<=KILL_WAIT; i<<=1) { s_tmutex.unlock(); Thread::msleep(i-d); d = i; s_tmutex.lock(); if (t != l->get()) break; } } if (t != l->get()) c = 1; else { if (ok) { #ifdef _WINDOWS Debug(DebugGoOn,"Could not kill %p but seems OK to delete it (library bug?)",t); s_tmutex.unlock(); t->destroy(); s_tmutex.lock(); if (t != l->get()) c = 1; #else Debug(DebugGoOn,"Could not kill cancelled %p so we'll abandon it (library bug?)",t); l->remove(t,false); c = 1; #endif continue; } Thread::msleep(1); if (++c >= HARD_KILLS) { Debug(DebugGoOn,"Could not kill %p, will use sledgehammer later.",t); sledgehammer = true; t->m_thread = 0; l = l->next(); c = 1; } } } s_tmutex.unlock(); // last solution - a REALLY BIG tool! // usually too big since many libraries have threads of their own... if (sledgehammer) { #ifdef THREAD_KILL Debug(DebugGoOn,"Brutally killing remaining threads!"); ::pthread_kill_other_threads_np(); #else Debug(DebugGoOn,"Aargh! I cannot kill remaining threads on this platform!"); #endif } }