/* * lftp - file transfer program * * Copyright (c) 1996-2012 by Alexander V. Lukyanov (lav@yars.free.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 3 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, see . */ #include #include "RateLimit.h" #include "ResMgr.h" #include "SMTask.h" xmap_p *RateLimit::total; void RateLimit::AddXfer(int add) { xfer_number+=add; assert(xfer_number>=0); if(parent) parent->AddXfer(add); } void RateLimit::init(level_e lvl,const char *c) { level=lvl; xfer_number=(level==PER_CONN?1:0); parent=0; Reconfig(0,c); if(level==TOTAL) // has no parent return; level_e parent_level=level_e(level+1); if(parent_level==TOTAL) c=""; // no closure on top level xstring parent_key(c); if(!total) total=new xmap_p(); if(total->exists(parent_key)) { parent=total->lookup(parent_key); if(parent->xfer_number==0) parent->Reconfig(0,c); // it was not used for a white, refresh config } else { parent=new RateLimit(parent_level,c); total->add(parent_key,parent); } parent->AddXfer(xfer_number); } RateLimit::~RateLimit() { if(parent && xfer_number) parent->AddXfer(-xfer_number); } #define LARGE 0x10000000 #define DEFAULT_MAX_COEFF 2 void RateLimit::BytesPool::AdjustTime() { double dif=TimeDiff(SMTask::now,t); if(dif>0) { // prevent overflow if((LARGE-pool)/dif < rate) pool = pool_max; else pool += int(dif*rate+0.5); if(pool>pool_max) pool=pool_max; t=SMTask::now; } } int RateLimit::BytesAllowed(dir_t dir) { int parent_allowed = parent?parent->BytesAllowed(dir):LARGE; if(pool[dir].rate==0) // unlimited return parent_allowed; pool[dir].AdjustTime(); int allowed = pool[dir].pool/xfer_number; if(allowed>parent_allowed) allowed=parent_allowed; return allowed; } bool RateLimit::Relaxed(dir_t dir) { bool parent_relaxed = parent?parent->Relaxed(dir):true; if(pool[dir].rate==0) // unlimited return parent_relaxed; pool[dir].AdjustTime(); if(pool[dir].rate>0 && pool[dir].pool < pool[dir].pool_max/2) return false; return parent_relaxed; } void RateLimit::BytesPool::Used(int bytes) { if(poolBytesUsed(bytes,dir); pool[dir].Used(bytes); } void RateLimit::Reset() { pool[GET].Reset(); pool[PUT].Reset(); } void RateLimit::BytesPool::Reset() { pool=rate; t=SMTask::now; } void RateLimit::Reconfig(const char *name,const char *c) { if(name && strncmp(name,"net:limit-",10)) return; // not relevant bool config_total=(!name || !strncmp(name,"net:limit-total-",16)); const char *setting_rate="net:limit-rate"; const char *setting_max="net:limit-max"; if(level>PER_CONN) { if(!config_total) return; // not relevant if(level==TOTAL) c=0; // aggregates everything setting_rate="net:limit-total-rate"; setting_max="net:limit-total-max"; } ResMgr::Query(setting_rate,c).ToNumberPair(pool[GET].rate,pool[PUT].rate); ResMgr::Query(setting_max,c).ToNumberPair(pool[GET].pool_max,pool[PUT].pool_max); if(pool[GET].pool_max==0) pool[GET].pool_max=pool[GET].rate*DEFAULT_MAX_COEFF; if(pool[PUT].pool_max==0) pool[PUT].pool_max=pool[PUT].rate*DEFAULT_MAX_COEFF; Reset(); if(config_total && parent) parent->Reconfig(name,c); } int RateLimit::LimitBufferSize(int size,dir_t d) const { if(pool[d].rate!=0 && size>pool[d].pool_max) size=pool[d].pool_max; return size; } void RateLimit::SetBufferSize(IOBuffer *buf,int size) const { dir_t d = (buf->GetDirection()==buf->GET ? GET : PUT); buf->SetMaxBuffered(LimitBufferSize(size,d)); } void RateLimit::ClassCleanup() { if(!total) return; for(RateLimit *t=total->each_begin(); t; t=total->each_next()) t->parent=0; delete total; total=0; }