diff --git a/libtransmission/bandwidth.cc b/libtransmission/bandwidth.cc index ea0bfee41..92d89748f 100644 --- a/libtransmission/bandwidth.cc +++ b/libtransmission/bandwidth.cc @@ -4,7 +4,8 @@ // License text can be found in the licenses/ folder. #include -#include // std::swap() +#include // for std::mt19937 +#include // for std::swap() #include #include @@ -12,7 +13,6 @@ #include "transmission.h" #include "bandwidth.h" -#include "crypto-utils.h" // tr_rand_int() #include "log.h" #include "peer-io.h" #include "tr-assert.h" @@ -166,31 +166,38 @@ void tr_bandwidth::allocateBandwidth( void tr_bandwidth::phaseOne(std::vector& peer_array, tr_direction dir) { - /* 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 */ + // First phase of IO. Tries to distribute bandwidth fairly to keep faster + // peers from starving the others. tr_logAddTrace(fmt::format("{} peers to go round-robin for {}", peer_array.size(), dir == TR_UP ? "upload" : "download")); - auto n = peer_array.size(); - while (n > 0) + // Shuffle the peers so they all have equal chance to be first in line. + thread_local auto random_engine = std::mt19937{ std::random_device{}() }; + std::shuffle(std::begin(peer_array), std::end(peer_array), random_engine); + + // Give each peer `Increment` bandwidth bytes to use. Repeat this + // process until we run out of bandwidth and/or peers that can use it. + for (size_t n_unfinished = std::size(peer_array); n_unfinished > 0U;) { - auto const i = tr_rand_int(n); /* pick a peer at random */ - - // value of 3000 bytes chosen so that when using µTP we'll send a full-size - // frame right away and leave enough buffered data for the next frame to go - // out in a timely manner. - static auto constexpr Increment = size_t{ 3000 }; - - auto const bytes_used = peer_array[i]->flush(dir, Increment); - - tr_logAddTrace(fmt::format("peer #{} of {} used {} bytes in this pass", i, n, bytes_used)); - - if (bytes_used != Increment) + for (size_t i = 0; i < n_unfinished;) { - // peer is done writing for now; move it to the end of the list - std::swap(peer_array[i], peer_array[n - 1]); - --n; + // Value of 3000 bytes chosen so that when using µTP we'll send a full-size + // frame right away and leave enough buffered data for the next frame to go + // out in a timely manner. + static auto constexpr Increment = size_t{ 3000 }; + + auto const bytes_used = peer_array[i]->flush(dir, Increment); + tr_logAddTrace(fmt::format("peer #{} of {} used {} bytes in this pass", i, n_unfinished, bytes_used)); + + if (bytes_used != Increment) + { + // peer is done writing for now; move it to the end of the list + std::swap(peer_array[i], peer_array[n_unfinished - 1]); + --n_unfinished; + } + else + { + ++i; + } } } }