/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #include "squid.h" #if USE_DELAY_POOLS #include "BandwidthBucket.h" #include "ClientInfo.h" #include "comm/Connection.h" #include "debug/Stream.h" #include "fde.h" BandwidthBucket::BandwidthBucket(const int speed, const int initialLevelPercent, const double sizeLimit) : bucketLevel( sizeLimit * (initialLevelPercent / 100.0)), selectWaiting(false), writeSpeedLimit(speed), bucketSizeLimit(sizeLimit) { getCurrentTime(); /* put current time to have something sensible here */ prevTime = current_dtime; } void BandwidthBucket::refillBucket() { if (noLimit()) return; // all these times are in seconds, with double precision const double currTime = current_dtime; const double timePassed = currTime - prevTime; // Calculate allowance for the time passed. Use double to avoid // accumulating rounding errors for small intervals. For example, always // adding 1 byte instead of 1.4 results in 29% bandwidth allocation error. const double gain = timePassed * writeSpeedLimit; // to further combat error accumulation during micro updates, // quit before updating time if we cannot add at least one byte if (gain < 1.0) return; prevTime = currTime; // for "first" connections, drain initial fat before refilling but keep // updating prevTime to avoid bursts after the fat is gone if (bucketLevel > bucketSizeLimit) { debugs(77, 4, "not refilling while draining initial fat"); return; } bucketLevel += gain; // obey quota limits if (bucketLevel > bucketSizeLimit) bucketLevel = bucketSizeLimit; } bool BandwidthBucket::applyQuota(int &nleft, Comm::IoCallback *state) { const int q = quota(); if (!q) return false; else if (q < 0) return true; const int nleft_corrected = min(nleft, q); if (nleft != nleft_corrected) { debugs(77, 5, state->conn << " writes only " << nleft_corrected << " out of " << nleft); nleft = nleft_corrected; } return true; } void BandwidthBucket::reduceBucket(const int len) { if (len <= 0 || noLimit()) return; bucketLevel -= len; if (bucketLevel < 0.0) { debugs(77, DBG_IMPORTANT, "drained too much"); // should not happen bucketLevel = 0; } } BandwidthBucket * BandwidthBucket::SelectBucket(fde *f) { BandwidthBucket *bucket = f->writeQuotaHandler.getRaw(); if (!bucket) { ClientInfo *clientInfo = f->clientInfo; if (clientInfo && clientInfo->writeLimitingActive) bucket = clientInfo; } return bucket; } #endif /* USE_DELAY_POOLS */