mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 12:28:52 +00:00
Modernize bandwidth.cc: Moved functions inside struct (#1895)
* Modernize bandwidth.cc: Move functions inside struct * Modernize bandwidth.cc: Review notes - remove extraneous checks, move small functions into .H file, etc * Modernize bandwidth.cc: Construction and destruction is now via C++ new/delete * Modernize bandwidth.cc: Rename used() to notifyBandwidthConsumed() * Modernize bandwidth.cc: Children is now unordered_set * Modernize bandwidth.cc: Allocation functions for peer bandwidth are now using std::vector instead of ptrArrays * Modernize bandwidth.cc: Code formatting * Modernize bandwidth.cc: Private fields after public; Minor review notes * Modernize bandwidth.cc: Ungroup enums into constants; Docs minor update * Modernize bitfield.cc: Docs comments changes; Using constexpr instead of const Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
committed by
GitHub
parent
1ba64684f9
commit
c287b82c00
@@ -23,7 +23,7 @@
|
||||
****
|
||||
***/
|
||||
|
||||
static unsigned int getSpeed_Bps(struct bratecontrol const* r, unsigned int interval_msec, uint64_t now)
|
||||
unsigned int tr_bandwidth::getSpeed_Bps(struct bratecontrol const* r, unsigned int interval_msec, uint64_t now)
|
||||
{
|
||||
if (now == 0)
|
||||
{
|
||||
@@ -34,7 +34,7 @@ static unsigned int getSpeed_Bps(struct bratecontrol const* r, unsigned int inte
|
||||
{
|
||||
uint64_t bytes = 0;
|
||||
uint64_t const cutoff = now - interval_msec;
|
||||
struct bratecontrol* rvolatile = (struct bratecontrol*)r;
|
||||
auto* rvolatile = (struct bratecontrol*)r;
|
||||
|
||||
for (int i = r->newest; r->transfers[i].date > cutoff;)
|
||||
{
|
||||
@@ -58,7 +58,7 @@ static unsigned int getSpeed_Bps(struct bratecontrol const* r, unsigned int inte
|
||||
return r->cache_val;
|
||||
}
|
||||
|
||||
static void bytesUsed(uint64_t const now, struct bratecontrol* r, size_t size)
|
||||
void tr_bandwidth::notifyBandwidthConsumedBytes(uint64_t const now, struct bratecontrol* r, size_t size)
|
||||
{
|
||||
if (r->transfers[r->newest].date + GRANULARITY_MSEC >= now)
|
||||
{
|
||||
@@ -79,69 +79,43 @@ static void bytesUsed(uint64_t const now, struct bratecontrol* r, size_t size)
|
||||
r->cache_time = 0;
|
||||
}
|
||||
|
||||
/******
|
||||
*******
|
||||
*******
|
||||
******/
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static int compareBandwidth(void const* va, void const* vb)
|
||||
tr_bandwidth::tr_bandwidth(tr_bandwidth* newParent)
|
||||
: band{}
|
||||
, parent{ nullptr }
|
||||
, children{}
|
||||
, peer{ nullptr }
|
||||
{
|
||||
auto const* a = static_cast<tr_bandwidth const*>(va);
|
||||
auto const* b = static_cast<tr_bandwidth const*>(vb);
|
||||
return a->uniqueKey - b->uniqueKey;
|
||||
this->children = {};
|
||||
this->band[TR_UP].honorParentLimits = true;
|
||||
this->band[TR_DOWN].honorParentLimits = true;
|
||||
this->setParent(newParent);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void tr_bandwidthConstruct(tr_bandwidth* b, tr_bandwidth* parent)
|
||||
void tr_bandwidth::setParent(tr_bandwidth* newParent)
|
||||
{
|
||||
static unsigned int uniqueKey = 0;
|
||||
TR_ASSERT(this != newParent);
|
||||
|
||||
b->children = {};
|
||||
b->magicNumber = BANDWIDTH_MAGIC_NUMBER;
|
||||
b->uniqueKey = uniqueKey++;
|
||||
b->band[TR_UP].honorParentLimits = true;
|
||||
b->band[TR_DOWN].honorParentLimits = true;
|
||||
tr_bandwidthSetParent(b, parent);
|
||||
}
|
||||
|
||||
void tr_bandwidthDestruct(tr_bandwidth* b)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
|
||||
tr_bandwidthSetParent(b, nullptr);
|
||||
tr_ptrArrayDestruct(&b->children, nullptr);
|
||||
|
||||
memset(b, ~0, sizeof(tr_bandwidth));
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void tr_bandwidthSetParent(tr_bandwidth* b, tr_bandwidth* parent)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(b != parent);
|
||||
|
||||
if (b->parent != nullptr)
|
||||
if (this->parent != nullptr)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b->parent));
|
||||
tr_ptrArrayRemoveSortedPointer(&b->parent->children, b, compareBandwidth);
|
||||
b->parent = nullptr;
|
||||
this->parent->children.erase(this);
|
||||
this->parent = nullptr;
|
||||
}
|
||||
|
||||
if (parent != nullptr)
|
||||
if (newParent != nullptr)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(parent));
|
||||
TR_ASSERT(parent->parent != b);
|
||||
TR_ASSERT(newParent->parent != this);
|
||||
TR_ASSERT(newParent->children.find(this) == newParent->children.end()); // does not exist
|
||||
|
||||
TR_ASSERT(tr_ptrArrayFindSorted(&parent->children, b, compareBandwidth) == nullptr);
|
||||
tr_ptrArrayInsertSorted(&parent->children, b, compareBandwidth);
|
||||
TR_ASSERT(tr_ptrArrayFindSorted(&parent->children, b, compareBandwidth) == b);
|
||||
b->parent = parent;
|
||||
newParent->children.insert(this);
|
||||
this->parent = newParent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,54 +123,46 @@ void tr_bandwidthSetParent(tr_bandwidth* b, tr_bandwidth* parent)
|
||||
****
|
||||
***/
|
||||
|
||||
static void allocateBandwidth(
|
||||
tr_bandwidth* b,
|
||||
void tr_bandwidth::allocateBandwidth(
|
||||
tr_priority_t parent_priority,
|
||||
tr_direction dir,
|
||||
unsigned int period_msec,
|
||||
tr_ptrArray* peer_pool)
|
||||
std::vector<tr_peerIo*>& peer_pool)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
tr_priority_t const priority = std::max(parent_priority, b->priority);
|
||||
tr_priority_t const priority_ = std::max(parent_priority, this->priority);
|
||||
|
||||
/* set the available bandwidth */
|
||||
if (b->band[dir].isLimited)
|
||||
if (this->band[dir].isLimited)
|
||||
{
|
||||
uint64_t const nextPulseSpeed = b->band[dir].desiredSpeed_Bps;
|
||||
b->band[dir].bytesLeft = nextPulseSpeed * period_msec / 1000U;
|
||||
uint64_t const nextPulseSpeed = this->band[dir].desiredSpeed_Bps;
|
||||
this->band[dir].bytesLeft = nextPulseSpeed * period_msec / 1000U;
|
||||
}
|
||||
|
||||
/* add this bandwidth's peer, if any, to the peer pool */
|
||||
if (b->peer != nullptr)
|
||||
if (this->peer != nullptr)
|
||||
{
|
||||
b->peer->priority = priority;
|
||||
tr_ptrArrayAppend(peer_pool, b->peer);
|
||||
this->peer->priority = priority_;
|
||||
peer_pool.push_back(this->peer);
|
||||
}
|
||||
|
||||
/* traverse & repeat for the subtree */
|
||||
struct tr_bandwidth** children = (struct tr_bandwidth**)tr_ptrArrayBase(&b->children);
|
||||
struct tr_bandwidth** const end = children + tr_ptrArraySize(&b->children);
|
||||
for (; children != end; ++children)
|
||||
// traverse & repeat for the subtree
|
||||
for (auto child : this->children)
|
||||
{
|
||||
allocateBandwidth(*children, priority, dir, period_msec, peer_pool);
|
||||
child->allocateBandwidth(priority_, dir, period_msec, peer_pool);
|
||||
}
|
||||
}
|
||||
|
||||
static void phaseOne(tr_ptrArray const* peerArray, tr_direction dir)
|
||||
void tr_bandwidth::phaseOne(std::vector<tr_peerIo*>& peerArray, tr_direction dir)
|
||||
{
|
||||
int n;
|
||||
int peerCount = tr_ptrArraySize(peerArray);
|
||||
struct tr_peerIo** peers = (struct tr_peerIo**)tr_ptrArrayBase(peerArray);
|
||||
|
||||
/* First phase of IO. Tries to distribute bandwidth fairly to keep faster
|
||||
* peers from starving the others. Loop through the peers, giving each a
|
||||
* small chunk of bandwidth. Keep looping until we run out of bandwidth
|
||||
* and/or peers that can use it */
|
||||
n = peerCount;
|
||||
dbgmsg("%d peers to go round-robin for %s", n, dir == TR_UP ? "upload" : "download");
|
||||
dbgmsg("%lu peers to go round-robin for %s", peerArray.size(), dir == TR_UP ? "upload" : "download");
|
||||
|
||||
size_t n = peerArray.size();
|
||||
while (n > 0)
|
||||
{
|
||||
int const i = tr_rand_int_weak(n); /* pick a peer at random */
|
||||
@@ -206,56 +172,48 @@ static void phaseOne(tr_ptrArray const* peerArray, tr_direction dir)
|
||||
* out in a timely manner. */
|
||||
size_t const increment = 3000;
|
||||
|
||||
int const bytesUsed = tr_peerIoFlush(peers[i], dir, increment);
|
||||
int const bytesUsed = tr_peerIoFlush(peerArray[i], dir, increment);
|
||||
|
||||
dbgmsg("peer #%d of %d used %d bytes in this pass", i, n, bytesUsed);
|
||||
dbgmsg("peer #%d of %zu used %d bytes in this pass", i, n, bytesUsed);
|
||||
|
||||
if (bytesUsed != (int)increment)
|
||||
{
|
||||
/* peer is done writing for now; move it to the end of the list */
|
||||
tr_peerIo* pio = peers[i];
|
||||
peers[i] = peers[n - 1];
|
||||
peers[n - 1] = pio;
|
||||
std::swap(peerArray[i], peerArray[n - 1]);
|
||||
--n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tr_bandwidthAllocate(tr_bandwidth* b, tr_direction dir, unsigned int period_msec)
|
||||
void tr_bandwidth::allocate(tr_direction dir, unsigned int period_msec)
|
||||
{
|
||||
int peerCount;
|
||||
auto tmp = tr_ptrArray{};
|
||||
auto low = tr_ptrArray{};
|
||||
auto high = tr_ptrArray{};
|
||||
auto normal = tr_ptrArray{};
|
||||
struct tr_peerIo** peers;
|
||||
std::vector<tr_peerIo*> tmp;
|
||||
std::vector<tr_peerIo*> low;
|
||||
std::vector<tr_peerIo*> normal;
|
||||
std::vector<tr_peerIo*> high;
|
||||
|
||||
/* allocateBandwidth () is a helper function with two purposes:
|
||||
* 1. allocate bandwidth to b and its subtree
|
||||
* 2. accumulate an array of all the peerIos from b and its subtree. */
|
||||
allocateBandwidth(b, TR_PRI_LOW, dir, period_msec, &tmp);
|
||||
peers = (struct tr_peerIo**)tr_ptrArrayBase(&tmp);
|
||||
peerCount = tr_ptrArraySize(&tmp);
|
||||
this->allocateBandwidth(TR_PRI_LOW, dir, period_msec, tmp);
|
||||
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
for (auto io : tmp)
|
||||
{
|
||||
tr_peerIo* io = peers[i];
|
||||
tr_peerIoRef(io);
|
||||
|
||||
tr_peerIoFlushOutgoingProtocolMsgs(io);
|
||||
|
||||
switch (io->priority)
|
||||
{
|
||||
case TR_PRI_HIGH:
|
||||
tr_ptrArrayAppend(&high, io);
|
||||
high.push_back(io);
|
||||
[[fallthrough]];
|
||||
|
||||
case TR_PRI_NORMAL:
|
||||
tr_ptrArrayAppend(&normal, io);
|
||||
normal.push_back(io);
|
||||
[[fallthrough]];
|
||||
|
||||
default:
|
||||
tr_ptrArrayAppend(&low, io);
|
||||
low.push_back(io);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,153 +221,113 @@ void tr_bandwidthAllocate(tr_bandwidth* b, tr_direction dir, unsigned int period
|
||||
* peers from starving the others. Loop through the peers, giving each a
|
||||
* small chunk of bandwidth. Keep looping until we run out of bandwidth
|
||||
* and/or peers that can use it */
|
||||
phaseOne(&high, dir);
|
||||
phaseOne(&normal, dir);
|
||||
phaseOne(&low, dir);
|
||||
phaseOne(high, dir);
|
||||
phaseOne(normal, dir);
|
||||
phaseOne(low, dir);
|
||||
|
||||
/* Second phase of IO. To help us scale in high bandwidth situations,
|
||||
* enable on-demand IO for peers with bandwidth left to burn.
|
||||
* This on-demand IO is enabled until (1) the peer runs out of bandwidth,
|
||||
* or (2) the next tr_bandwidthAllocate () call, when we start over again. */
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
* or (2) the next tr_bandwidth::allocate () call, when we start over again. */
|
||||
for (auto io : tmp)
|
||||
{
|
||||
tr_peerIoSetEnabled(peers[i], dir, tr_peerIoHasBandwidthLeft(peers[i], dir));
|
||||
tr_peerIoSetEnabled(io, dir, tr_peerIoHasBandwidthLeft(io, dir));
|
||||
}
|
||||
|
||||
for (int i = 0; i < peerCount; ++i)
|
||||
for (auto io : tmp)
|
||||
{
|
||||
tr_peerIoUnref(peers[i]);
|
||||
tr_peerIoUnref(io);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_ptrArrayDestruct(&normal, nullptr);
|
||||
tr_ptrArrayDestruct(&high, nullptr);
|
||||
tr_ptrArrayDestruct(&low, nullptr);
|
||||
tr_ptrArrayDestruct(&tmp, nullptr);
|
||||
}
|
||||
|
||||
void tr_bandwidthSetPeer(tr_bandwidth* b, tr_peerIo* peer)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(peer == nullptr || tr_isPeerIo(peer));
|
||||
|
||||
b->peer = peer;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
static unsigned int bandwidthClamp(tr_bandwidth const* b, uint64_t now, tr_direction dir, unsigned int byteCount)
|
||||
unsigned int tr_bandwidth::clamp(uint64_t now, tr_direction dir, unsigned int byteCount) const
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
if (b != nullptr)
|
||||
if (this->band[dir].isLimited)
|
||||
{
|
||||
if (b->band[dir].isLimited)
|
||||
byteCount = std::min(byteCount, this->band[dir].bytesLeft);
|
||||
|
||||
/* if we're getting close to exceeding the speed limit,
|
||||
* clamp down harder on the bytes available */
|
||||
if (byteCount > 0)
|
||||
{
|
||||
byteCount = std::min(byteCount, b->band[dir].bytesLeft);
|
||||
double current;
|
||||
double desired;
|
||||
double r;
|
||||
|
||||
/* if we're getting close to exceeding the speed limit,
|
||||
* clamp down harder on the bytes available */
|
||||
if (byteCount > 0)
|
||||
if (now == 0)
|
||||
{
|
||||
double current;
|
||||
double desired;
|
||||
double r;
|
||||
now = tr_time_msec();
|
||||
}
|
||||
|
||||
if (now == 0)
|
||||
{
|
||||
now = tr_time_msec();
|
||||
}
|
||||
current = this->getRawSpeed_Bps(now, TR_DOWN);
|
||||
desired = this->getDesiredSpeed_Bps(TR_DOWN);
|
||||
r = desired >= 1 ? current / desired : 0;
|
||||
|
||||
current = tr_bandwidthGetRawSpeed_Bps(b, now, TR_DOWN);
|
||||
desired = tr_bandwidthGetDesiredSpeed_Bps(b, TR_DOWN);
|
||||
r = desired >= 1 ? current / desired : 0;
|
||||
|
||||
if (r > 1.0)
|
||||
{
|
||||
byteCount = 0;
|
||||
}
|
||||
else if (r > 0.9)
|
||||
{
|
||||
byteCount *= 0.8;
|
||||
}
|
||||
else if (r > 0.8)
|
||||
{
|
||||
byteCount *= 0.9;
|
||||
}
|
||||
if (r > 1.0)
|
||||
{
|
||||
byteCount = 0;
|
||||
}
|
||||
else if (r > 0.9)
|
||||
{
|
||||
byteCount = static_cast<unsigned int>(byteCount * 0.8);
|
||||
}
|
||||
else if (r > 0.8)
|
||||
{
|
||||
byteCount = static_cast<unsigned int>(byteCount * 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (b->parent != nullptr && b->band[dir].honorParentLimits && byteCount > 0)
|
||||
{
|
||||
byteCount = bandwidthClamp(b->parent, now, dir, byteCount);
|
||||
}
|
||||
if (this->parent != nullptr && this->band[dir].honorParentLimits && byteCount > 0)
|
||||
{
|
||||
byteCount = this->parent->clamp(now, dir, byteCount);
|
||||
}
|
||||
|
||||
return byteCount;
|
||||
}
|
||||
|
||||
unsigned int tr_bandwidthClamp(tr_bandwidth const* b, tr_direction dir, unsigned int byteCount)
|
||||
void tr_bandwidth::notifyBandwidthConsumed(tr_direction dir, size_t byteCount, bool isPieceData, uint64_t now)
|
||||
{
|
||||
return bandwidthClamp(b, 0, dir, byteCount);
|
||||
}
|
||||
|
||||
unsigned int tr_bandwidthGetRawSpeed_Bps(tr_bandwidth const* b, uint64_t const now, tr_direction const dir)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return getSpeed_Bps(&b->band[dir].raw, HISTORY_MSEC, now);
|
||||
}
|
||||
struct tr_band* band_ = &this->band[dir];
|
||||
|
||||
unsigned int tr_bandwidthGetPieceSpeed_Bps(tr_bandwidth const* b, uint64_t const now, tr_direction const dir)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return getSpeed_Bps(&b->band[dir].piece, HISTORY_MSEC, now);
|
||||
}
|
||||
|
||||
void tr_bandwidthUsed(tr_bandwidth* b, tr_direction dir, size_t byteCount, bool isPieceData, uint64_t now)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(b));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
struct tr_band* band = &b->band[dir];
|
||||
|
||||
if (band->isLimited && isPieceData)
|
||||
if (band_->isLimited && isPieceData)
|
||||
{
|
||||
band->bytesLeft -= std::min(size_t{ band->bytesLeft }, byteCount);
|
||||
band_->bytesLeft -= std::min(size_t{ band_->bytesLeft }, byteCount);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DIRECTION
|
||||
|
||||
if (dir == DEBUG_DIRECTION && band->isLimited)
|
||||
if (dir == DEBUG_DIRECTION && band_->isLimited)
|
||||
{
|
||||
fprintf(
|
||||
stderr,
|
||||
"%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
|
||||
b,
|
||||
this,
|
||||
byteCount,
|
||||
isPieceData ? "piece" : "raw",
|
||||
oldBytesLeft,
|
||||
band->bytesLeft);
|
||||
band_->bytesLeft);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bytesUsed(now, &band->raw, byteCount);
|
||||
notifyBandwidthConsumedBytes(now, &band_->raw, byteCount);
|
||||
|
||||
if (isPieceData)
|
||||
{
|
||||
bytesUsed(now, &band->piece, byteCount);
|
||||
notifyBandwidthConsumedBytes(now, &band_->piece, byteCount);
|
||||
}
|
||||
|
||||
if (b->parent != nullptr)
|
||||
if (this->parent != nullptr)
|
||||
{
|
||||
tr_bandwidthUsed(b->parent, dir, byteCount, isPieceData, now);
|
||||
this->parent->notifyBandwidthConsumed(dir, byteCount, isPieceData, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
#error only libtransmission should #include this header.
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "ptrarray.h"
|
||||
#include "tr-assert.h"
|
||||
#include "utils.h" /* tr_new(), tr_free() */
|
||||
|
||||
@@ -26,14 +28,10 @@ struct tr_peerIo;
|
||||
|
||||
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
|
||||
* it's included in the header for inlining and composition. */
|
||||
enum
|
||||
{
|
||||
HISTORY_MSEC = 2000U,
|
||||
INTERVAL_MSEC = HISTORY_MSEC,
|
||||
GRANULARITY_MSEC = 200,
|
||||
HISTORY_SIZE = (INTERVAL_MSEC / GRANULARITY_MSEC),
|
||||
BANDWIDTH_MAGIC_NUMBER = 43143
|
||||
};
|
||||
constexpr size_t HISTORY_MSEC = 2000U;
|
||||
constexpr size_t INTERVAL_MSEC = HISTORY_MSEC;
|
||||
constexpr size_t GRANULARITY_MSEC = 200;
|
||||
constexpr size_t HISTORY_SIZE = (INTERVAL_MSEC / GRANULARITY_MSEC);
|
||||
|
||||
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
|
||||
* it's included in the header for inlining and composition. */
|
||||
@@ -88,149 +86,170 @@ struct tr_band
|
||||
*
|
||||
* CONSTRAINING
|
||||
*
|
||||
* Call tr_bandwidthAllocate() periodically. tr_bandwidth knows its current
|
||||
* Call tr_bandwidth::allocate() periodically. tr_bandwidth knows its current
|
||||
* speed and will decide how many bytes to make available over the
|
||||
* user-specified period to reach the user-specified desired speed.
|
||||
* If appropriate, it notifies its peer-ios that new bandwidth is available.
|
||||
*
|
||||
* tr_bandwidthAllocate() operates on the tr_bandwidth subtree, so usually
|
||||
* tr_bandwidth::allocate() operates on the tr_bandwidth subtree, so usually
|
||||
* you'll only need to invoke it for the top-level tr_session bandwidth.
|
||||
*
|
||||
* The peer-ios all have a pointer to their associated tr_bandwidth object,
|
||||
* and call tr_bandwidthClamp() before performing I/O to see how much
|
||||
* and call tr_bandwidth::clamp() before performing I/O to see how much
|
||||
* bandwidth they can safely use.
|
||||
*/
|
||||
struct tr_bandwidth
|
||||
{
|
||||
/* these are PRIVATE IMPLEMENTATION details that should not be touched.
|
||||
* it's included in the header for inlining and composition. */
|
||||
public:
|
||||
explicit tr_bandwidth(tr_bandwidth* newParent);
|
||||
|
||||
struct tr_band band[2];
|
||||
tr_bandwidth()
|
||||
: tr_bandwidth(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~tr_bandwidth()
|
||||
{
|
||||
this->setParent(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets new peer, nullptr is allowed.
|
||||
*/
|
||||
void setPeer(tr_peerIo* newPeer)
|
||||
{
|
||||
this->peer = newPeer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Notify the bandwidth object that some of its allocated bandwidth has been consumed.
|
||||
* This is is usually invoked by the peer-io after a read or write.
|
||||
*/
|
||||
void notifyBandwidthConsumed(tr_direction dir, size_t byteCount, bool isPieceData, uint64_t now);
|
||||
|
||||
/**
|
||||
* @brief allocate the next period_msec's worth of bandwidth for the peer-ios to consume
|
||||
*/
|
||||
void allocate(tr_direction dir, unsigned int period_msec);
|
||||
|
||||
void setParent(tr_bandwidth* newParent);
|
||||
|
||||
[[nodiscard]] tr_priority_t getPriority() const
|
||||
{
|
||||
return this->priority;
|
||||
}
|
||||
|
||||
void setPriority(tr_priority_t prio)
|
||||
{
|
||||
this->priority = prio;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief clamps byteCount down to a number that this bandwidth will allow to be consumed
|
||||
*/
|
||||
[[nodiscard]] unsigned int clamp(tr_direction dir, unsigned int byteCount) const
|
||||
{
|
||||
return this->clamp(0, dir, byteCount);
|
||||
}
|
||||
|
||||
/** @brief Get the raw total of bytes read or sent by this bandwidth subtree. */
|
||||
[[nodiscard]] unsigned int getRawSpeed_Bps(uint64_t const now, tr_direction const dir) const
|
||||
{
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return getSpeed_Bps(&this->band[dir].raw, HISTORY_MSEC, now);
|
||||
}
|
||||
|
||||
/** @brief Get the number of piece data bytes read or sent by this bandwidth subtree. */
|
||||
[[nodiscard]] unsigned int getPieceSpeed_Bps(uint64_t const now, tr_direction const dir) const
|
||||
{
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return getSpeed_Bps(&this->band[dir].piece, HISTORY_MSEC, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the desired speed for this bandwidth subtree.
|
||||
* @see tr_bandwidth::allocate
|
||||
* @see tr_bandwidth::getDesiredSpeed
|
||||
*/
|
||||
constexpr bool setDesiredSpeed_Bps(tr_direction dir, unsigned int desiredSpeed)
|
||||
{
|
||||
unsigned int* value = &this->band[dir].desiredSpeed_Bps;
|
||||
bool const didChange = desiredSpeed != *value;
|
||||
*value = desiredSpeed;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the desired speed for the bandwidth subtree.
|
||||
* @see tr_bandwidth::setDesiredSpeed
|
||||
*/
|
||||
[[nodiscard]] constexpr double getDesiredSpeed_Bps(tr_direction dir) const
|
||||
{
|
||||
return this->band[dir].desiredSpeed_Bps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set whether or not this bandwidth should throttle its peer-io's speeds
|
||||
*/
|
||||
constexpr bool setLimited(tr_direction dir, bool isLimited)
|
||||
{
|
||||
bool* value = &this->band[dir].isLimited;
|
||||
bool const didChange = isLimited != *value;
|
||||
*value = isLimited;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return nonzero if this bandwidth throttles its peer-ios speeds
|
||||
*/
|
||||
[[nodiscard]] constexpr bool isLimited(tr_direction dir) const
|
||||
{
|
||||
return this->band[dir].isLimited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Almost all the time we do want to honor a parents' bandwidth cap, so that
|
||||
* (for example) a peer is constrained by a per-torrent cap and the global cap.
|
||||
* But when we set a torrent's speed mode to TR_SPEEDLIMIT_UNLIMITED, then
|
||||
* in that particular case we want to ignore the global speed limit...
|
||||
*/
|
||||
constexpr bool honorParentLimits(tr_direction direction, bool isEnabled)
|
||||
{
|
||||
bool* value = &this->band[direction].honorParentLimits;
|
||||
bool const didChange = isEnabled != *value;
|
||||
*value = isEnabled;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool areParentLimitsHonored(tr_direction direction) const
|
||||
{
|
||||
TR_ASSERT(tr_isDirection(direction));
|
||||
|
||||
return this->band[direction].honorParentLimits;
|
||||
}
|
||||
|
||||
private:
|
||||
static unsigned int getSpeed_Bps(struct bratecontrol const* r, unsigned int interval_msec, uint64_t now);
|
||||
|
||||
static void notifyBandwidthConsumedBytes(uint64_t now, struct bratecontrol* r, size_t size);
|
||||
|
||||
[[nodiscard]] unsigned int clamp(uint64_t now, tr_direction dir, unsigned int byteCount) const;
|
||||
|
||||
static void phaseOne(std::vector<tr_peerIo*>& peerArray, tr_direction dir);
|
||||
|
||||
void allocateBandwidth(
|
||||
tr_priority_t parent_priority,
|
||||
tr_direction dir,
|
||||
unsigned int period_msec,
|
||||
std::vector<tr_peerIo*>& peer_pool);
|
||||
|
||||
tr_priority_t priority = 0;
|
||||
std::array<struct tr_band, 2> band;
|
||||
struct tr_bandwidth* parent;
|
||||
tr_priority_t priority;
|
||||
int magicNumber;
|
||||
unsigned int uniqueKey;
|
||||
tr_ptrArray children; /* struct tr_bandwidth */
|
||||
std::unordered_set<tr_bandwidth*> children;
|
||||
struct tr_peerIo* peer;
|
||||
};
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
void tr_bandwidthConstruct(tr_bandwidth* bandwidth, tr_bandwidth* parent);
|
||||
|
||||
void tr_bandwidthDestruct(tr_bandwidth* bandwidth);
|
||||
|
||||
/** @brief test to see if the pointer refers to a live bandwidth object */
|
||||
constexpr bool tr_isBandwidth(tr_bandwidth const* b)
|
||||
{
|
||||
return b != nullptr && b->magicNumber == BANDWIDTH_MAGIC_NUMBER;
|
||||
}
|
||||
|
||||
/******
|
||||
*******
|
||||
******/
|
||||
|
||||
/**
|
||||
* @brief Set the desired speed for this bandwidth subtree.
|
||||
* @see tr_bandwidthAllocate
|
||||
* @see tr_bandwidthGetDesiredSpeed
|
||||
*/
|
||||
constexpr bool tr_bandwidthSetDesiredSpeed_Bps(tr_bandwidth* bandwidth, tr_direction dir, unsigned int desiredSpeed)
|
||||
{
|
||||
unsigned int* value = &bandwidth->band[dir].desiredSpeed_Bps;
|
||||
bool const didChange = desiredSpeed != *value;
|
||||
*value = desiredSpeed;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the desired speed for the bandwidth subtree.
|
||||
* @see tr_bandwidthSetDesiredSpeed
|
||||
*/
|
||||
constexpr double tr_bandwidthGetDesiredSpeed_Bps(tr_bandwidth const* bandwidth, tr_direction dir)
|
||||
{
|
||||
return bandwidth->band[dir].desiredSpeed_Bps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set whether or not this bandwidth should throttle its peer-io's speeds
|
||||
*/
|
||||
constexpr bool tr_bandwidthSetLimited(tr_bandwidth* bandwidth, tr_direction dir, bool isLimited)
|
||||
{
|
||||
bool* value = &bandwidth->band[dir].isLimited;
|
||||
bool const didChange = isLimited != *value;
|
||||
*value = isLimited;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return nonzero if this bandwidth throttles its peer-ios speeds
|
||||
*/
|
||||
constexpr bool tr_bandwidthIsLimited(tr_bandwidth const* bandwidth, tr_direction dir)
|
||||
{
|
||||
return bandwidth->band[dir].isLimited;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief allocate the next period_msec's worth of bandwidth for the peer-ios to consume
|
||||
*/
|
||||
void tr_bandwidthAllocate(tr_bandwidth* bandwidth, tr_direction direction, unsigned int period_msec);
|
||||
|
||||
/**
|
||||
* @brief clamps byteCount down to a number that this bandwidth will allow to be consumed
|
||||
*/
|
||||
unsigned int tr_bandwidthClamp(tr_bandwidth const* bandwidth, tr_direction direction, unsigned int byteCount);
|
||||
|
||||
/******
|
||||
*******
|
||||
******/
|
||||
|
||||
/** @brief Get the raw total of bytes read or sent by this bandwidth subtree. */
|
||||
unsigned int tr_bandwidthGetRawSpeed_Bps(tr_bandwidth const* bandwidth, uint64_t const now, tr_direction const direction);
|
||||
|
||||
/** @brief Get the number of piece data bytes read or sent by this bandwidth subtree. */
|
||||
unsigned int tr_bandwidthGetPieceSpeed_Bps(tr_bandwidth const* bandwidth, uint64_t const now, tr_direction const direction);
|
||||
|
||||
/**
|
||||
* @brief Notify the bandwidth object that some of its allocated bandwidth has been consumed.
|
||||
* This is is usually invoked by the peer-io after a read or write.
|
||||
*/
|
||||
void tr_bandwidthUsed(tr_bandwidth* bandwidth, tr_direction direction, size_t byteCount, bool isPieceData, uint64_t now);
|
||||
|
||||
/******
|
||||
*******
|
||||
******/
|
||||
|
||||
void tr_bandwidthSetParent(tr_bandwidth* bandwidth, tr_bandwidth* parent);
|
||||
|
||||
/**
|
||||
* Almost all the time we do want to honor a parents' bandwidth cap, so that
|
||||
* (for example) a peer is constrained by a per-torrent cap and the global cap.
|
||||
* But when we set a torrent's speed mode to TR_SPEEDLIMIT_UNLIMITED, then
|
||||
* in that particular case we want to ignore the global speed limit...
|
||||
*/
|
||||
constexpr bool tr_bandwidthHonorParentLimits(tr_bandwidth* bandwidth, tr_direction direction, bool isEnabled)
|
||||
{
|
||||
bool* value = &bandwidth->band[direction].honorParentLimits;
|
||||
bool const didChange = isEnabled != *value;
|
||||
*value = isEnabled;
|
||||
return didChange;
|
||||
}
|
||||
|
||||
constexpr bool tr_bandwidthAreParentLimitsHonored(tr_bandwidth const* bandwidth, tr_direction direction)
|
||||
{
|
||||
TR_ASSERT(tr_isBandwidth(bandwidth));
|
||||
TR_ASSERT(tr_isDirection(direction));
|
||||
|
||||
return bandwidth->band[direction].honorParentLimits;
|
||||
}
|
||||
|
||||
/******
|
||||
*******
|
||||
******/
|
||||
|
||||
void tr_bandwidthSetPeer(tr_bandwidth* bandwidth, struct tr_peerIo* peerIo);
|
||||
|
||||
/* @} */
|
||||
|
||||
@@ -164,11 +164,11 @@ static void didWriteWrapper(tr_peerIo* io, unsigned int bytes_transferred)
|
||||
unsigned int const overhead = io->socket.type == TR_PEER_SOCKET_TYPE_TCP ? guessPacketOverhead(payload) : 0;
|
||||
uint64_t const now = tr_time_msec();
|
||||
|
||||
tr_bandwidthUsed(&io->bandwidth, TR_UP, payload, next->isPieceData, now);
|
||||
io->bandwidth->notifyBandwidthConsumed(TR_UP, payload, next->isPieceData, now);
|
||||
|
||||
if (overhead > 0)
|
||||
{
|
||||
tr_bandwidthUsed(&io->bandwidth, TR_UP, overhead, false, now);
|
||||
io->bandwidth->notifyBandwidthConsumed(TR_UP, overhead, false, now);
|
||||
}
|
||||
|
||||
if (io->didWrite != nullptr)
|
||||
@@ -220,18 +220,18 @@ static void canReadWrapper(tr_peerIo* io)
|
||||
{
|
||||
if (piece != 0)
|
||||
{
|
||||
tr_bandwidthUsed(&io->bandwidth, TR_DOWN, piece, true, now);
|
||||
io->bandwidth->notifyBandwidthConsumed(TR_DOWN, piece, true, now);
|
||||
}
|
||||
|
||||
if (used != piece)
|
||||
{
|
||||
tr_bandwidthUsed(&io->bandwidth, TR_DOWN, used - piece, false, now);
|
||||
io->bandwidth->notifyBandwidthConsumed(TR_DOWN, used - piece, false, now);
|
||||
}
|
||||
}
|
||||
|
||||
if (overhead > 0)
|
||||
{
|
||||
tr_bandwidthUsed(&io->bandwidth, TR_UP, overhead, false, now);
|
||||
io->bandwidth->notifyBandwidthConsumed(TR_UP, overhead, false, now);
|
||||
}
|
||||
|
||||
switch (ret)
|
||||
@@ -285,7 +285,7 @@ static void event_read_cb(evutil_socket_t fd, short event, void* vio)
|
||||
|
||||
curlen = evbuffer_get_length(io->inbuf);
|
||||
howmuch = curlen >= max ? 0 : max - curlen;
|
||||
howmuch = tr_bandwidthClamp(&io->bandwidth, TR_DOWN, howmuch);
|
||||
howmuch = io->bandwidth->clamp(TR_DOWN, howmuch);
|
||||
|
||||
dbgmsg(io, "libevent says this peer is ready to read");
|
||||
|
||||
@@ -378,7 +378,7 @@ static void event_write_cb(evutil_socket_t fd, short event, void* vio)
|
||||
|
||||
/* Write as much as possible, since the socket is non-blocking, write() will
|
||||
* return if it can't write any more data without blocking */
|
||||
howmuch = tr_bandwidthClamp(&io->bandwidth, dir, evbuffer_get_length(io->outbuf));
|
||||
howmuch = io->bandwidth->clamp(dir, evbuffer_get_length(io->outbuf));
|
||||
|
||||
/* if we don't have any bandwidth left, stop writing */
|
||||
if (howmuch < 1)
|
||||
@@ -496,7 +496,7 @@ static size_t utp_get_rb_size(void* vio)
|
||||
|
||||
TR_ASSERT(tr_isPeerIo(io));
|
||||
|
||||
size_t bytes = tr_bandwidthClamp(&io->bandwidth, TR_DOWN, UTP_READ_BUFFER_SIZE);
|
||||
size_t bytes = io->bandwidth->clamp(TR_DOWN, UTP_READ_BUFFER_SIZE);
|
||||
|
||||
dbgmsg(io, "utp_get_rb_size is saying it's ready to read %zu bytes", bytes);
|
||||
return UTP_READ_BUFFER_SIZE - bytes;
|
||||
@@ -577,7 +577,7 @@ static void utp_on_overhead(void* vio, bool send, size_t count, int type)
|
||||
|
||||
dbgmsg(io, "utp_on_overhead -- count is %zu", count);
|
||||
|
||||
tr_bandwidthUsed(&io->bandwidth, send ? TR_UP : TR_DOWN, count, false, tr_time_msec());
|
||||
io->bandwidth->notifyBandwidthConsumed(send ? TR_UP : TR_DOWN, count, false, tr_time_msec());
|
||||
}
|
||||
|
||||
static auto utp_function_table = UTPFunctionTable{
|
||||
@@ -680,8 +680,8 @@ static tr_peerIo* tr_peerIoNew(
|
||||
io->timeCreated = tr_time();
|
||||
io->inbuf = evbuffer_new();
|
||||
io->outbuf = evbuffer_new();
|
||||
tr_bandwidthConstruct(&io->bandwidth, parent);
|
||||
tr_bandwidthSetPeer(&io->bandwidth, io);
|
||||
io->bandwidth = new tr_bandwidth(parent);
|
||||
io->bandwidth->setPeer(io);
|
||||
dbgmsg(io, "bandwidth is %p; its parent is %p", (void*)&io->bandwidth, (void*)parent);
|
||||
|
||||
switch (socket.type)
|
||||
@@ -921,7 +921,7 @@ static void io_dtor(void* vio)
|
||||
|
||||
dbgmsg(io, "in tr_peerIo destructor");
|
||||
event_disable(io, EV_READ | EV_WRITE);
|
||||
tr_bandwidthDestruct(&io->bandwidth);
|
||||
delete io->bandwidth;
|
||||
evbuffer_free(io->outbuf);
|
||||
evbuffer_free(io->inbuf);
|
||||
io_close_socket(io);
|
||||
@@ -1094,7 +1094,7 @@ static unsigned int getDesiredOutputBufferSize(tr_peerIo const* io, uint64_t now
|
||||
* being large enough to hold the next 20 seconds' worth of input,
|
||||
* or a few blocks, whichever is bigger.
|
||||
* It's okay to tweak this as needed */
|
||||
unsigned int const currentSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&io->bandwidth, now, TR_UP);
|
||||
unsigned int const currentSpeed_Bps = io->bandwidth->getPieceSpeed_Bps(now, TR_UP);
|
||||
unsigned int const period = 15U; /* arbitrary */
|
||||
/* the 3 is arbitrary; the .5 is to leave room for messages */
|
||||
static auto const ceiling = (unsigned int)(MAX_BLOCK_SIZE * 3.5);
|
||||
@@ -1316,7 +1316,7 @@ static int tr_peerIoTryRead(tr_peerIo* io, size_t howmuch)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if ((howmuch = tr_bandwidthClamp(&io->bandwidth, TR_DOWN, howmuch)) != 0)
|
||||
if ((howmuch = io->bandwidth->clamp(TR_DOWN, howmuch)) != 0)
|
||||
{
|
||||
switch (io->socket.type)
|
||||
{
|
||||
@@ -1389,7 +1389,7 @@ static int tr_peerIoTryWrite(tr_peerIo* io, size_t howmuch)
|
||||
howmuch = old_len;
|
||||
}
|
||||
|
||||
if ((howmuch = tr_bandwidthClamp(&io->bandwidth, TR_UP, howmuch)) != 0)
|
||||
if ((howmuch = io->bandwidth->clamp(TR_UP, howmuch)) != 0)
|
||||
{
|
||||
switch (io->socket.type)
|
||||
{
|
||||
@@ -1454,7 +1454,7 @@ int tr_peerIoFlush(tr_peerIo* io, tr_direction dir, size_t limit)
|
||||
bytesUsed = tr_peerIoTryWrite(io, limit);
|
||||
}
|
||||
|
||||
dbgmsg(io, "flushing peer-io, direction %d, limit %zu, bytesUsed %d", (int)dir, limit, bytesUsed);
|
||||
dbgmsg(io, "flushing peer-io, direction %d, limit %zu, notifyBandwidthConsumedBytes %d", (int)dir, limit, bytesUsed);
|
||||
return bytesUsed;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,9 @@ struct tr_peerIo
|
||||
tr_net_error_cb gotError;
|
||||
void* userData;
|
||||
|
||||
struct tr_bandwidth bandwidth;
|
||||
// Changed to non-owning pointer temporarily till tr_peerIo becomes C++-constructible and destructible
|
||||
// TODO: change tr_bandwidth* to owning pointer to the bandwidth, or remove * and own the value
|
||||
struct tr_bandwidth* bandwidth;
|
||||
tr_crypto crypto;
|
||||
|
||||
struct evbuffer* inbuf;
|
||||
@@ -134,8 +136,7 @@ void tr_peerIoUnrefImpl(char const* file, int line, tr_peerIo* io);
|
||||
|
||||
constexpr bool tr_isPeerIo(tr_peerIo const* io)
|
||||
{
|
||||
return io != nullptr && io->magicNumber == PEER_IO_MAGIC_NUMBER && io->refCount >= 0 && tr_isBandwidth(&io->bandwidth) &&
|
||||
tr_address_is_valid(&io->addr);
|
||||
return io != nullptr && io->magicNumber == PEER_IO_MAGIC_NUMBER && io->refCount >= 0 && tr_address_is_valid(&io->addr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,19 +303,19 @@ static inline void tr_peerIoSetParent(tr_peerIo* io, struct tr_bandwidth* parent
|
||||
{
|
||||
TR_ASSERT(tr_isPeerIo(io));
|
||||
|
||||
tr_bandwidthSetParent(&io->bandwidth, parent);
|
||||
io->bandwidth->setParent(parent);
|
||||
}
|
||||
|
||||
void tr_peerIoBandwidthUsed(tr_peerIo* io, tr_direction direction, size_t byteCount, int isPieceData);
|
||||
|
||||
static inline bool tr_peerIoHasBandwidthLeft(tr_peerIo const* io, tr_direction dir)
|
||||
{
|
||||
return tr_bandwidthClamp(&io->bandwidth, dir, 1024) > 0;
|
||||
return io->bandwidth->clamp(dir, 1024) > 0;
|
||||
}
|
||||
|
||||
static inline unsigned int tr_peerIoGetPieceSpeed_Bps(tr_peerIo const* io, uint64_t now, tr_direction dir)
|
||||
{
|
||||
return tr_bandwidthGetPieceSpeed_Bps(&io->bandwidth, now, dir);
|
||||
return io->bandwidth->getPieceSpeed_Bps(now, dir);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2054,7 +2054,7 @@ static bool myHandshakeDoneCB(
|
||||
|
||||
/* this steals its refcount too, which is balanced by our unref in peerDelete() */
|
||||
tr_peerIo* stolen = tr_handshakeStealIO(handshake);
|
||||
tr_peerIoSetParent(stolen, &s->tor->bandwidth);
|
||||
tr_peerIoSetParent(stolen, s->tor->bandwidth);
|
||||
createBitTorrentPeer(s->tor, stolen, atom, client);
|
||||
|
||||
success = true;
|
||||
@@ -2116,7 +2116,7 @@ void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address* addr, tr_port port,
|
||||
tr_peerIo* io;
|
||||
tr_handshake* handshake;
|
||||
|
||||
io = tr_peerIoNewIncoming(session, &session->bandwidth, addr, port, socket);
|
||||
io = tr_peerIoNewIncoming(session, session->bandwidth, addr, port, socket);
|
||||
|
||||
handshake = tr_handshakeNew(io, session->encryptionMode, myHandshakeDoneCB, manager);
|
||||
|
||||
@@ -3185,14 +3185,14 @@ static int getRate(tr_torrent const* tor, struct peer_atom* atom, uint64_t now)
|
||||
|
||||
static inline bool isBandwidthMaxedOut(tr_bandwidth const* b, uint64_t const now_msec, tr_direction dir)
|
||||
{
|
||||
if (!tr_bandwidthIsLimited(b, dir))
|
||||
if (!b->isLimited(dir))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int const got = tr_bandwidthGetPieceSpeed_Bps(b, now_msec, dir);
|
||||
unsigned int const want = tr_bandwidthGetDesiredSpeed_Bps(b, dir);
|
||||
unsigned int const got = b->getPieceSpeed_Bps(now_msec, dir);
|
||||
unsigned int const want = b->getDesiredSpeed_Bps(dir);
|
||||
return got >= want;
|
||||
}
|
||||
}
|
||||
@@ -3206,7 +3206,7 @@ static void rechokeUploads(tr_swarm* s, uint64_t const now)
|
||||
struct ChokeData* choke = tr_new0(struct ChokeData, peerCount);
|
||||
tr_session const* session = s->manager->session;
|
||||
bool const chokeAll = !tr_torrentIsPieceTransferAllowed(s->tor, TR_CLIENT_TO_PEER);
|
||||
bool const isMaxedOut = isBandwidthMaxedOut(&s->tor->bandwidth, now, TR_UP);
|
||||
bool const isMaxedOut = isBandwidthMaxedOut(s->tor->bandwidth, now, TR_UP);
|
||||
|
||||
/* an optimistic unchoke peer's "optimistic"
|
||||
* state lasts for N calls to rechokeUploads(). */
|
||||
@@ -3805,8 +3805,8 @@ static void bandwidthPulse(evutil_socket_t fd, short what, void* vmgr)
|
||||
pumpAllPeers(mgr);
|
||||
|
||||
/* allocate bandwidth to the peers */
|
||||
tr_bandwidthAllocate(&session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC);
|
||||
tr_bandwidthAllocate(&session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC);
|
||||
session->bandwidth->allocate(TR_UP, BANDWIDTH_PERIOD_MSEC);
|
||||
session->bandwidth->allocate(TR_DOWN, BANDWIDTH_PERIOD_MSEC);
|
||||
|
||||
/* torrent upkeep */
|
||||
for (auto* tor : session->torrents)
|
||||
@@ -4213,7 +4213,7 @@ static struct peer_candidate* getPeerCandidates(tr_session* session, int* candid
|
||||
}
|
||||
|
||||
/* if we've already got enough speed in this torrent... */
|
||||
if (seeding && isBandwidthMaxedOut(&tor->bandwidth, now_msec, TR_UP))
|
||||
if (seeding && isBandwidthMaxedOut(tor->bandwidth, now_msec, TR_UP))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -4268,7 +4268,7 @@ static void initiateConnection(tr_peerMgr* mgr, tr_swarm* s, struct peer_atom* a
|
||||
|
||||
io = tr_peerIoNewOutgoing(
|
||||
mgr->session,
|
||||
&mgr->session->bandwidth,
|
||||
mgr->session->bandwidth,
|
||||
&atom->addr,
|
||||
atom->port,
|
||||
s->tor->info.hash,
|
||||
|
||||
@@ -636,7 +636,7 @@ tr_session* tr_sessionInit(char const* configDir, bool messageQueuingEnabled, tr
|
||||
session->cache = tr_cacheNew(1024 * 1024 * 2);
|
||||
session->magicNumber = SESSION_MAGIC_NUMBER;
|
||||
session->session_id = tr_session_id_new();
|
||||
tr_bandwidthConstruct(&session->bandwidth, nullptr);
|
||||
session->bandwidth = new tr_bandwidth(nullptr);
|
||||
tr_variantInitList(&session->removedTorrents, 0);
|
||||
|
||||
/* nice to start logging at the very beginning */
|
||||
@@ -1494,9 +1494,9 @@ static void updateBandwidth(tr_session* session, tr_direction dir)
|
||||
bool const isLimited = tr_sessionGetActiveSpeedLimit_Bps(session, dir, &limit_Bps);
|
||||
bool const zeroCase = isLimited && limit_Bps == 0;
|
||||
|
||||
tr_bandwidthSetLimited(&session->bandwidth, dir, isLimited && !zeroCase);
|
||||
session->bandwidth->setLimited(dir, isLimited && !zeroCase);
|
||||
|
||||
tr_bandwidthSetDesiredSpeed_Bps(&session->bandwidth, dir, limit_Bps);
|
||||
session->bandwidth->setDesiredSpeed_Bps(dir, limit_Bps);
|
||||
}
|
||||
|
||||
enum
|
||||
@@ -1891,12 +1891,12 @@ bool tr_sessionGetDeleteSource(tr_session const* session)
|
||||
|
||||
unsigned int tr_sessionGetPieceSpeed_Bps(tr_session const* session, tr_direction dir)
|
||||
{
|
||||
return tr_isSession(session) ? tr_bandwidthGetPieceSpeed_Bps(&session->bandwidth, 0, dir) : 0;
|
||||
return tr_isSession(session) ? session->bandwidth->getPieceSpeed_Bps(0, dir) : 0;
|
||||
}
|
||||
|
||||
static unsigned int tr_sessionGetRawSpeed_Bps(tr_session const* session, tr_direction dir)
|
||||
{
|
||||
return tr_isSession(session) ? tr_bandwidthGetRawSpeed_Bps(&session->bandwidth, 0, dir) : 0;
|
||||
return tr_isSession(session) ? session->bandwidth->getRawSpeed_Bps(0, dir) : 0;
|
||||
}
|
||||
|
||||
double tr_sessionGetRawSpeed_KBps(tr_session const* session, tr_direction dir)
|
||||
@@ -2112,7 +2112,7 @@ void tr_sessionClose(tr_session* session)
|
||||
|
||||
/* free the session memory */
|
||||
tr_variantFree(&session->removedTorrents);
|
||||
tr_bandwidthDestruct(&session->bandwidth);
|
||||
delete session->bandwidth;
|
||||
tr_bitfieldDestruct(&session->turtle.minutes);
|
||||
tr_session_id_free(session->session_id);
|
||||
tr_lockFree(session->lock);
|
||||
|
||||
@@ -240,7 +240,9 @@ struct tr_session
|
||||
struct event* saveTimer;
|
||||
|
||||
/* monitors the "global pool" speeds */
|
||||
struct tr_bandwidth bandwidth;
|
||||
// Changed to non-owning pointer temporarily till tr_session becomes C++-constructible and destructible
|
||||
// TODO: change tr_bandwidth* to owning pointer to the bandwidth, or remove * and own the value
|
||||
struct tr_bandwidth* bandwidth;
|
||||
|
||||
float desiredRatio;
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ void tr_torrentSetSpeedLimit_Bps(tr_torrent* tor, tr_direction dir, unsigned int
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
if (tr_bandwidthSetDesiredSpeed_Bps(&tor->bandwidth, dir, Bps))
|
||||
if (tor->bandwidth->setDesiredSpeed_Bps(dir, Bps))
|
||||
{
|
||||
tr_torrentSetDirty(tor);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ unsigned int tr_torrentGetSpeedLimit_Bps(tr_torrent const* tor, tr_direction dir
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
return tr_bandwidthGetDesiredSpeed_Bps(&tor->bandwidth, dir);
|
||||
return tor->bandwidth->getDesiredSpeed_Bps(dir);
|
||||
}
|
||||
|
||||
unsigned int tr_torrentGetSpeedLimit_KBps(tr_torrent const* tor, tr_direction dir)
|
||||
@@ -228,7 +228,7 @@ void tr_torrentUseSpeedLimit(tr_torrent* tor, tr_direction dir, bool do_use)
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tr_isDirection(dir));
|
||||
|
||||
if (tr_bandwidthSetLimited(&tor->bandwidth, dir, do_use))
|
||||
if (tor->bandwidth->setLimited(dir, do_use))
|
||||
{
|
||||
tr_torrentSetDirty(tor);
|
||||
}
|
||||
@@ -238,19 +238,14 @@ bool tr_torrentUsesSpeedLimit(tr_torrent const* tor, tr_direction dir)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
return tr_bandwidthIsLimited(&tor->bandwidth, dir);
|
||||
return tor->bandwidth->isLimited(dir);
|
||||
}
|
||||
|
||||
void tr_torrentUseSessionLimits(tr_torrent* tor, bool doUse)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
bool changed;
|
||||
|
||||
changed = tr_bandwidthHonorParentLimits(&tor->bandwidth, TR_UP, doUse);
|
||||
changed |= tr_bandwidthHonorParentLimits(&tor->bandwidth, TR_DOWN, doUse);
|
||||
|
||||
if (changed)
|
||||
if (tor->bandwidth->honorParentLimits(TR_UP, doUse) || tor->bandwidth->honorParentLimits(TR_DOWN, doUse))
|
||||
{
|
||||
tr_torrentSetDirty(tor);
|
||||
}
|
||||
@@ -260,7 +255,7 @@ bool tr_torrentUsesSessionLimits(tr_torrent const* tor)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
return tr_bandwidthAreParentLimitsHonored(&tor->bandwidth, TR_UP);
|
||||
return tor->bandwidth->areParentLimitsHonored(TR_UP);
|
||||
}
|
||||
|
||||
/***
|
||||
@@ -886,9 +881,9 @@ static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
|
||||
tor->incompleteDir = tr_strdup(dir);
|
||||
}
|
||||
|
||||
tr_bandwidthConstruct(&tor->bandwidth, &session->bandwidth);
|
||||
tor->bandwidth = new tr_bandwidth(session->bandwidth);
|
||||
|
||||
tor->bandwidth.priority = tr_ctorGetBandwidthPriority(ctor);
|
||||
tor->bandwidth->setPriority(tr_ctorGetBandwidthPriority(ctor));
|
||||
tor->error = TR_STAT_OK;
|
||||
tor->finishedSeedingByIdle = false;
|
||||
|
||||
@@ -1301,10 +1296,10 @@ tr_stat const* tr_torrentStat(tr_torrent* tor)
|
||||
s->peersFrom[i] = swarm_stats.peerFromCount[i];
|
||||
}
|
||||
|
||||
s->rawUploadSpeed_KBps = toSpeedKBps(tr_bandwidthGetRawSpeed_Bps(&tor->bandwidth, now, TR_UP));
|
||||
s->rawDownloadSpeed_KBps = toSpeedKBps(tr_bandwidthGetRawSpeed_Bps(&tor->bandwidth, now, TR_DOWN));
|
||||
pieceUploadSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&tor->bandwidth, now, TR_UP);
|
||||
pieceDownloadSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&tor->bandwidth, now, TR_DOWN);
|
||||
s->rawUploadSpeed_KBps = toSpeedKBps(tor->bandwidth->getRawSpeed_Bps(now, TR_UP));
|
||||
s->rawDownloadSpeed_KBps = toSpeedKBps(tor->bandwidth->getRawSpeed_Bps(now, TR_DOWN));
|
||||
pieceUploadSpeed_Bps = tor->bandwidth->getPieceSpeed_Bps(now, TR_UP);
|
||||
pieceDownloadSpeed_Bps = tor->bandwidth->getPieceSpeed_Bps(now, TR_DOWN);
|
||||
s->pieceUploadSpeed_KBps = toSpeedKBps(pieceUploadSpeed_Bps);
|
||||
s->pieceDownloadSpeed_KBps = toSpeedKBps(pieceDownloadSpeed_Bps);
|
||||
|
||||
@@ -1638,7 +1633,7 @@ static void freeTorrent(tr_torrent* tor)
|
||||
|
||||
TR_ASSERT(queueIsSequenced(session));
|
||||
|
||||
tr_bandwidthDestruct(&tor->bandwidth);
|
||||
delete tor->bandwidth;
|
||||
|
||||
tr_metainfoFree(inf);
|
||||
delete tor;
|
||||
@@ -2444,7 +2439,7 @@ tr_priority_t tr_torrentGetPriority(tr_torrent const* tor)
|
||||
{
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
|
||||
return tor->bandwidth.priority;
|
||||
return tor->bandwidth->getPriority();
|
||||
}
|
||||
|
||||
void tr_torrentSetPriority(tr_torrent* tor, tr_priority_t priority)
|
||||
@@ -2452,9 +2447,9 @@ void tr_torrentSetPriority(tr_torrent* tor, tr_priority_t priority)
|
||||
TR_ASSERT(tr_isTorrent(tor));
|
||||
TR_ASSERT(tr_isPriority(priority));
|
||||
|
||||
if (tor->bandwidth.priority != priority)
|
||||
if (tor->bandwidth->getPriority() != priority)
|
||||
{
|
||||
tor->bandwidth.priority = priority;
|
||||
tor->bandwidth->setPriority(priority);
|
||||
|
||||
tr_torrentSetDirty(tor);
|
||||
}
|
||||
|
||||
@@ -257,7 +257,9 @@ struct tr_torrent
|
||||
|
||||
int uniqueId;
|
||||
|
||||
struct tr_bandwidth bandwidth;
|
||||
// Changed to non-owning pointer temporarily till tr_torrent becomes C++-constructible and destructible
|
||||
// TODO: change tr_bandwidth* to owning pointer to the bandwidth, or remove * and own the value
|
||||
struct tr_bandwidth* bandwidth;
|
||||
|
||||
struct tr_swarm* swarm;
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ static size_t writeFunc(void* ptr, size_t size, size_t nmemb, void* vtask)
|
||||
{
|
||||
tr_torrent const* const tor = tr_torrentFindFromId(task->session, task->torrentId);
|
||||
|
||||
if (tor != nullptr && tr_bandwidthClamp(&tor->bandwidth, TR_DOWN, nmemb) == 0)
|
||||
if (tor != nullptr && tor->bandwidth->clamp(TR_DOWN, nmemb) == 0)
|
||||
{
|
||||
task->session->web->paused_easy_handles.insert(task->curl_easy);
|
||||
return CURL_WRITEFUNC_PAUSE;
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
, base_url{ url }
|
||||
, callback{ callback_in }
|
||||
, callback_data{ callback_data_in }
|
||||
, bandwidth(tor->bandwidth)
|
||||
{
|
||||
// init parent bits
|
||||
tr_bitfieldSetHasAll(&have);
|
||||
@@ -71,7 +72,6 @@ public:
|
||||
|
||||
file_urls.resize(tr_torrentInfo(tor)->fileCount);
|
||||
|
||||
tr_bandwidthConstruct(&bandwidth, &tor->bandwidth);
|
||||
timer = evtimer_new(session->event_base, webseed_timer_func, this);
|
||||
tr_timerAddMsec(timer, TR_IDLE_TIMER_MSEC);
|
||||
}
|
||||
@@ -83,7 +83,6 @@ public:
|
||||
tasks.clear();
|
||||
|
||||
event_free(timer);
|
||||
tr_bandwidthDestruct(&bandwidth);
|
||||
}
|
||||
|
||||
bool is_transferring_pieces(uint64_t now, tr_direction direction, unsigned int* setme_Bps) const override
|
||||
@@ -94,7 +93,7 @@ public:
|
||||
if (direction == TR_DOWN)
|
||||
{
|
||||
is_active = !std::empty(tasks);
|
||||
Bps = tr_bandwidthGetPieceSpeed_Bps(&bandwidth, now, direction);
|
||||
Bps = bandwidth.getPieceSpeed_Bps(now, direction);
|
||||
}
|
||||
|
||||
if (setme_Bps != nullptr)
|
||||
@@ -110,7 +109,7 @@ public:
|
||||
tr_peer_callback const callback;
|
||||
void* const callback_data;
|
||||
|
||||
tr_bandwidth bandwidth = {};
|
||||
tr_bandwidth bandwidth;
|
||||
std::set<tr_webseed_task*> tasks;
|
||||
struct event* timer = nullptr;
|
||||
int consecutive_failures = 0;
|
||||
@@ -288,7 +287,7 @@ static void on_content_changed(struct evbuffer* buf, struct evbuffer_cb_info con
|
||||
uint32_t len;
|
||||
struct tr_webseed* w = task->webseed;
|
||||
|
||||
tr_bandwidthUsed(&w->bandwidth, TR_DOWN, n_added, true, tr_time_msec());
|
||||
w->bandwidth.notifyBandwidthConsumed(TR_DOWN, n_added, true, tr_time_msec());
|
||||
fire_client_got_piece_data(w, n_added);
|
||||
len = evbuffer_get_length(buf);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user