//============================================================================= // // File : kvi_lagmeter.cpp // Creation date : Fri Oct 18 13:31:36 CEST 2002 by Juanjo �varez // // This file is part of the KVirc irc client distribution // Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net) // // 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 opinion) 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. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #define __KVIRC__ #include "kvi_lagmeter.h" #include "kvi_options.h" #include "kvi_kvs_eventtriggers.h" #include "kvi_parameterlist.h" #include "kvi_ircconnection.h" #include "kvi_irccontext.h" #include "kvi_frame.h" #include "kvi_console.h" #include "kvi_time.h" #include "kvi_ircconnectionuserinfo.h" #include "kvi_ircconnectionserverinfo.h" #include "kvi_out.h" #include "kvi_locale.h" KviLagMeter::KviLagMeter(KviIrcConnection * c) : TQObject() { m_pConnection = c; m_pCheckList = new KviPointerList; m_pCheckList->setAutoDelete(true); m_uLag = 0; m_uLastEmittedLag = 0; m_uLastReliability = 0; m_tLastCompleted = 0; m_tLastOwnCheck = 0; m_tFirstOwnCheck = 0; m_bOnAlarm = false; m_pDeletionSignal = 0; // FIXME: We could use the KviIrcConnection::heartbeat() here! if(KVI_OPTION_UINT(KviOption_uintLagMeterHeartbeat) < 2000) KVI_OPTION_UINT(KviOption_uintLagMeterHeartbeat) = 2000; // kinda absurd if(KVI_OPTION_UINT(KviOption_uintLagMeterHeartbeat) > 10000) KVI_OPTION_UINT(KviOption_uintLagMeterHeartbeat) = 10000; // kinda absurd startTimer(KVI_OPTION_UINT(KviOption_uintLagMeterHeartbeat)); // 5 seconds by default } KviLagMeter::~KviLagMeter() { if(m_pDeletionSignal)*m_pDeletionSignal = true; #ifndef COMPILE_USE_QT4 killTimers(); #endif delete m_pCheckList; } unsigned int KviLagMeter::secondsSinceLastCompleted() { struct timeval tv; kvi_gettimeofday(&tv,0); return tv.tv_sec - m_tLastCompleted; } void KviLagMeter::timerEvent(TQTimerEvent *) { if(m_pConnection->state() != KviIrcConnection::Connected)return; // do nothing atm // If the lag has changed emit our signals if((m_uLag / 10) != (m_uLastEmittedLag / 10)) { m_uLastEmittedLag = m_uLag; g_pFrame->childConnectionLagChange(m_pConnection); KviStr szLag(KviStr::Format,"%u",m_uLag); bool bDeletionSignal = false; m_pDeletionSignal = &bDeletionSignal; if((!m_bOnAlarm) && (m_uLag > KVI_OPTION_UINT(KviOption_uintLagAlarmTime))) { KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnLagAlarmTimeUp, m_pConnection->console(),m_pConnection->serverInfo()->name(),TQString(szLag.ptr())); if(bDeletionSignal)return; // killed , probably by a quit -f -u m_bOnAlarm = true; } else if(m_bOnAlarm) { KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnLagAlarmTimeDown, m_pConnection->console(),m_pConnection->serverInfo()->name(),TQString(szLag.ptr())); if(bDeletionSignal)return; // killed , probably by a quit -f -u m_bOnAlarm = false; } KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnLagCheck, m_pConnection->console(),m_pConnection->serverInfo()->name(),TQString(szLag.ptr())); if(bDeletionSignal)return; // killed , probably by a quit -f -u m_pDeletionSignal = 0; } // get current time struct timeval tv; kvi_gettimeofday(&tv,0); unsigned int uDiff = tv.tv_sec - m_tLastCompleted; unsigned int uHeartbeat = KVI_OPTION_UINT(KviOption_uintLagMeterHeartbeat) / 1000; if(uHeartbeat < 2)uHeartbeat = 2; // we keep the last lag value for an amount of time // depending on its reliability. // Since reliability ranges from 10 to 100 we keep the lags // for (hrtbt * 4) + (reliability / 2) seconds (which means from 25 to 70 seconds by default) if(uDiff <= ((uHeartbeat * 4) + (m_uLastReliability / 2)))return; // nothing to do, the actual value is accurate // the last completed check has been completed a lot of time ago // do we have some checks on the queue ? if(m_pCheckList->count() > 0) { // if the first registered check is not too outdated // we wait a little more for it to return KviLagCheck * c = m_pCheckList->first(); if(c) { if((tv.tv_sec - c->lSecs) <= 10)return; } // the first check was registered more than 10 secs before if(m_tLastOwnCheck > 0) { // hm.. we have already sent our own (reliable) check after the last completed // make the lag grow (we're pretty sure it's growing) uDiff = (tv.tv_sec - m_tFirstOwnCheck) * 1000; if(m_uLag < uDiff)m_uLag = uDiff; // the lag grows for sure uDiff = tv.tv_sec - m_tLastOwnCheck; if(uDiff < (uHeartbeat * 4))return; // wait a bit...send own checks only every 20 secs (by default) at this point } } // or we have no checks in the queue at all // or it's really time to do something... if(m_tFirstOwnCheck == 0) { if(_OUTPUT_PARANOIC) m_pConnection->console()->output(KVI_OUT_VERBOSE,__tr2qs("Sending out PING based lag probe")); // this is the first our own lag check since the last succesfull one: use the ping lagCheckRegister("@ping@",70); // the ping may be fooled easily m_pConnection->sendFmtData("PING %s %s", m_pConnection->encodeText( m_pConnection->userInfo()->nickName() ).data(), m_pConnection->encodeText( m_pConnection->serverInfo()->name() ).data() ); m_tFirstOwnCheck = tv.tv_sec; } else { if(_OUTPUT_PARANOIC) m_pConnection->console()->output(KVI_OUT_VERBOSE,__tr2qs("Sending out CTCP based lag probe")); // we have already sent a ping but we got no reply // try with another method... even if this will reset our idle time KviStr tmp(KviStr::Format,"%d%d-yeah-:)",tv.tv_sec,tv.tv_usec); lagCheckRegister(tmp.ptr(),100); // almost impossible to fool m_pConnection->sendFmtData("NOTICE %s :%cLAGCHECK %s%c", m_pConnection->encodeText( m_pConnection->userInfo()->nickName() ).data(), 0x01, tmp.ptr(), 0x01); } m_tLastOwnCheck = tv.tv_sec; } void KviLagMeter::lagCheckRegister(const char * key,unsigned int uReliability) { if(uReliability < 10)return; // what the heck of a lag check is this ? // store the lagcheck structure and just return if(_OUTPUT_PARANOIC) m_pConnection->console()->output(KVI_OUT_VERBOSE,__tr2qs("Registered lag check with reliability %u (%s)"),uReliability,key); KviLagCheck * c = new KviLagCheck; c->szKey = key; struct timeval tv; kvi_gettimeofday(&tv,0); c->lSecs = tv.tv_sec; c->lUSecs = tv.tv_usec; c->uReliability = uReliability <= 100 ? uReliability : 100; m_pCheckList->append(c); while(m_pCheckList->count() > 30) { // we're fried :/ // either our ping mechanism is not working // or the server is stoned... m_pCheckList->removeFirst(); } } bool KviLagMeter::lagCheckComplete(const char * key) { // find this lag check KviLagCheck * c; for(c = m_pCheckList->first();c;c = m_pCheckList->next()) { if(kvi_strEqualCS(c->szKey.ptr(),key))break; } if(!c)return false; // not found // kill any earlier lag checks (IRC is a sequential proto) while(m_pCheckList->first() != c)m_pCheckList->removeFirst(); if(_OUTPUT_PARANOIC) m_pConnection->console()->output(KVI_OUT_VERBOSE,__tr2qs("Lag check completed (%s)"),key); struct timeval tv; kvi_gettimeofday(&tv,0); unsigned int uLag = ((tv.tv_sec - c->lSecs) * 1000); if(tv.tv_usec < c->lUSecs)uLag -= ((c->lUSecs - tv.tv_usec) / 1000); else uLag += ((tv.tv_usec - c->lUSecs) / 1000); // now check the reliability if(m_uLastReliability > c->uReliability) { // the actual data is more reliable than the new one :/ // change the real lag only by a certain amount // c->uRel : 100 = uLag : m_uLag m_uLag = ((uLag * c->uReliability) + (m_uLag * m_uLastReliability)) / (c->uReliability + m_uLastReliability); } else { // the actual data is less reliable than the new one m_uLag = uLag; } m_tLastCompleted = tv.tv_sec; // now m_tLastOwnCheck = 0; m_tFirstOwnCheck = 0; m_uLastReliability = c->uReliability; m_pCheckList->removeFirst(); return true; } void KviLagMeter::lagCheckAbort(const char * key) { KviPointerList l; l.setAutoDelete(false); KviLagCheck * c; if(_OUTPUT_PARANOIC) m_pConnection->console()->output(KVI_OUT_VERBOSE,__tr2qs("Lag check aborted (%s)"),key); for(c = m_pCheckList->first();c;c = m_pCheckList->next()) if(kvi_strEqualCS(c->szKey.ptr(),key))l.append(c); for(c = l.first();c;c = l.next())m_pCheckList->removeRef(c); } #include "kvi_lagmeter.moc"