You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvirc/kernel/kvi_lagmeter.cpp

267 lines
8.8 KiB

//=============================================================================
//
// File : kvi_lagmeter.cpp
// Creation date : Fri Oct 18 13:31:36 CEST 2002 by Juanjo <20>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<KviLagCheck>;
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<KviLagCheck> 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"