diff options
Diffstat (limited to 'kftpgrabber/src/engine/speedlimiter.cpp')
-rw-r--r-- | kftpgrabber/src/engine/speedlimiter.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/kftpgrabber/src/engine/speedlimiter.cpp b/kftpgrabber/src/engine/speedlimiter.cpp new file mode 100644 index 0000000..85f2b72 --- /dev/null +++ b/kftpgrabber/src/engine/speedlimiter.cpp @@ -0,0 +1,240 @@ +/* + * This file is part of the KFTPGrabber project + * + * Copyright (C) 2003-2007 by the KFTPGrabber developers + * Copyright (C) 2003-2007 Jernej Kos <kostko@jweb-network.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 option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and + * NON-INFRINGEMENT. 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 Steet, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * + * You must obey the GNU General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ +#include "speedlimiter.h" +#include "misc/config.h" + +#include <kstaticdeleter.h> + +using namespace KFTPCore; + +namespace KFTPEngine { + +static const int tickDelay = 250; +static int bucketSize = 1000 / tickDelay; + +SpeedLimiter *SpeedLimiter::m_self = 0; +static KStaticDeleter<SpeedLimiter> staticSpeedLimiterDeleter; + +SpeedLimiter *SpeedLimiter::self() +{ + if (!m_self) { + staticSpeedLimiterDeleter.setObject(m_self, new SpeedLimiter()); + } + + return m_self; +} + +SpeedLimiter::SpeedLimiter() + : m_timer(false) +{ + // Reset limits and token debts + m_limits[0] = 0; + m_limits[1] = 0; + + m_tokenDebt[0] = 0; + m_tokenDebt[1] = 0; + + // Subscribe to config updates and update the limits + connect(Config::self(), SIGNAL(configChanged()), this, SLOT(updateLimits())); + updateLimits(); +} + +SpeedLimiter::~SpeedLimiter() +{ + if (m_self == this) + staticSpeedLimiterDeleter.setObject(m_self, 0, false); +} + +void SpeedLimiter::updateLimits() +{ + setLimit(SpeedLimiter::Download, Config::downloadSpeedLimit() * 1024); + setLimit(SpeedLimiter::Upload, Config::uploadSpeedLimit() * 1024); +} + +void SpeedLimiter::setLimit(Type type, int limit) +{ + m_limits[type] = limit; +} + +void SpeedLimiter::append(SpeedLimiterItem *item, Type type) +{ + m_objects[type].append(item); + + int limit = m_limits[type]; + if (limit > 0) { + int tokens = limit * tickDelay / 1000; + tokens /= m_objects[type].count(); + + if (m_tokenDebt[type] > 0) { + if (tokens >= m_tokenDebt[type]) { + tokens -= m_tokenDebt[type]; + m_tokenDebt[type] = 0; + } else { + tokens = 0; + } + } + + item->m_availableBytes = tokens; + } else { + item->m_availableBytes = -1; + } + + // Fire the timer if not running + if (!m_timer) { + startTimer(tickDelay); + m_timer = true; + } +} + +void SpeedLimiter::remove(SpeedLimiterItem *item) +{ + remove(item, Download); + remove(item, Upload); +} + +void SpeedLimiter::remove(SpeedLimiterItem *item, Type type) +{ + if (m_objects[type].containsRef(item)) { + int tokens = m_limits[type] * tickDelay / 1000; + tokens /= m_objects[type].count(); + + if (item->m_availableBytes < tokens) + m_tokenDebt[type] += tokens - item->m_availableBytes; + + m_objects[type].removeRef(item); + } + + item->m_availableBytes = -1; +} + +void SpeedLimiter::timerEvent(QTimerEvent*) +{ + QPtrList<SpeedLimiterItem> pendingWakeup; + + for (int i = 0; i < 2; i++) { + m_tokenDebt[i] = 0; + + int limit = m_limits[i]; + if (!limit) { + // There is no limit, reset all items + for (SpeedLimiterItem *item = m_objects[i].first(); item; item = m_objects[i].next()) { + item->m_availableBytes = -1; + } + + continue; + } + + // If there are no objects, just skip it + if (m_objects[i].isEmpty()) + continue; + + int tokens = limit * tickDelay / 1000; + if (!tokens) + tokens = 1; + + int maxTokens = tokens * bucketSize; + + // Get amount of tokens for each object + int tokensPerObject = tokens / m_objects[i].count(); + if (!tokensPerObject) + tokensPerObject = 1; + + tokens = 0; + + QPtrList<SpeedLimiterItem> unsaturatedObjects; + + for (SpeedLimiterItem *item = m_objects[i].first(); item; item = m_objects[i].next()) { + if (item->m_availableBytes == -1) { + item->m_availableBytes = tokensPerObject; + unsaturatedObjects.append(item); + } else { + item->m_availableBytes += tokensPerObject; + + if (item->m_availableBytes > maxTokens) { + tokens += item->m_availableBytes - maxTokens; + item->m_availableBytes = maxTokens; + } else { + unsaturatedObjects.append(item); + } + } + } + + // Assign any left-overs to unsaturated sources + while (tokens && !unsaturatedObjects.isEmpty()) { + tokensPerObject = tokens / unsaturatedObjects.count(); + if (!tokensPerObject) + break; + + tokens = 0; + + for (SpeedLimiterItem *item = unsaturatedObjects.first(); item; item = unsaturatedObjects.next()) { + item->m_availableBytes += tokensPerObject; + + if (item->m_availableBytes > maxTokens) { + tokens += item->m_availableBytes - maxTokens; + item->m_availableBytes = maxTokens; + unsaturatedObjects.removeRef(item); + } + } + } + } + + if (m_objects[0].isEmpty() && m_objects[1].isEmpty()) { + killTimers(); + m_timer = false; + } +} + +SpeedLimiterItem::SpeedLimiterItem() + : m_availableBytes(-1) +{ +} + +void SpeedLimiterItem::updateUsage(int bytes) +{ + // Ignore if there are no limits + if (m_availableBytes == -1) + return; + + if (bytes > m_availableBytes) + m_availableBytes = 0; + else + m_availableBytes -= bytes; +} + +} + +#include "speedlimiter.moc" |