/* */ #include "BtLeecherStateChoke.h" #include #include "Peer.h" #include "Logger.h" #include "LogFactory.h" #include "SimpleRandomizer.h" #include "wallclock.h" #include "fmt.h" namespace aria2 { BtLeecherStateChoke::BtLeecherStateChoke() : round_(0), lastRound_(Timer::zero()) { } BtLeecherStateChoke::~BtLeecherStateChoke() = default; BtLeecherStateChoke::PeerEntry::PeerEntry(const std::shared_ptr& peer) : peer_(peer), downloadSpeed_(peer->calculateDownloadSpeed()), // peer must be interested to us and sent block in the last 30 seconds regularUnchoker_( peer->peerInterested() && peer->getLastDownloadUpdate().difference(global::wallclock()) < 30_s) { } BtLeecherStateChoke::PeerEntry::PeerEntry(const PeerEntry& c) = default; void BtLeecherStateChoke::PeerEntry::swap(PeerEntry& c) { using std::swap; swap(peer_, c.peer_); swap(downloadSpeed_, c.downloadSpeed_); swap(regularUnchoker_, c.regularUnchoker_); } BtLeecherStateChoke::PeerEntry& BtLeecherStateChoke::PeerEntry:: operator=(const PeerEntry& c) { if (this != &c) { peer_ = c.peer_; downloadSpeed_ = c.downloadSpeed_; regularUnchoker_ = c.regularUnchoker_; } return *this; } BtLeecherStateChoke::PeerEntry::~PeerEntry() = default; const std::shared_ptr& BtLeecherStateChoke::PeerEntry::getPeer() const { return peer_; } int BtLeecherStateChoke::PeerEntry::getDownloadSpeed() const { return downloadSpeed_; } bool BtLeecherStateChoke::PeerEntry::isRegularUnchoker() const { return regularUnchoker_; } void BtLeecherStateChoke::PeerEntry::enableChokingRequired() { peer_->chokingRequired(true); } void BtLeecherStateChoke::PeerEntry::disableChokingRequired() { peer_->chokingRequired(false); } void BtLeecherStateChoke::PeerEntry::enableOptUnchoking() { peer_->optUnchoking(true); } void BtLeecherStateChoke::PeerEntry::disableOptUnchoking() { peer_->optUnchoking(false); } bool BtLeecherStateChoke::PeerEntry::operator<(const PeerEntry& peerEntry) const { return downloadSpeed_ > peerEntry.downloadSpeed_; } void swap(BtLeecherStateChoke::PeerEntry& a, BtLeecherStateChoke::PeerEntry& b) { a.swap(b); } bool BtLeecherStateChoke::PeerFilter:: operator()(const PeerEntry& peerEntry) const { return peerEntry.getPeer()->amChoking() == amChoking_ && peerEntry.getPeer()->peerInterested() == peerInterested_; } void BtLeecherStateChoke::plannedOptimisticUnchoke( std::vector& peerEntries) { std::for_each(std::begin(peerEntries), std::end(peerEntries), std::mem_fn(&PeerEntry::disableOptUnchoking)); auto i = std::partition(std::begin(peerEntries), std::end(peerEntries), PeerFilter(true, true)); if (i != std::begin(peerEntries)) { std::shuffle(std::begin(peerEntries), i, *SimpleRandomizer::getInstance()); auto& ent = *std::begin(peerEntries); auto& peer = ent.getPeer(); ent.enableOptUnchoking(); A2_LOG_INFO( fmt("POU: %s:%u", peer->getIPAddress().c_str(), peer->getPort())); } } void BtLeecherStateChoke::regularUnchoke(std::vector& peerEntries) { auto rest = std::partition(std::begin(peerEntries), std::end(peerEntries), std::mem_fn(&PeerEntry::isRegularUnchoker)); std::sort(std::begin(peerEntries), rest); std::shuffle(rest, std::end(peerEntries), *SimpleRandomizer::getInstance()); // the number of regular unchokers int count = 3; bool fastOptUnchoker = false; auto peerIter = std::begin(peerEntries); for (; peerIter != std::end(peerEntries) && count; ++peerIter, --count) { auto& peer = peerIter->getPeer(); if (!peer->peerInterested()) { continue; } peerIter->disableChokingRequired(); A2_LOG_INFO(fmt("RU: %s:%u, dlspd=%d", peer->getIPAddress().c_str(), peer->getPort(), (*peerIter).getDownloadSpeed())); if (peer->optUnchoking()) { fastOptUnchoker = true; peerIter->disableOptUnchoking(); } } if (fastOptUnchoker) { for (auto& p : peerEntries) { if (!p.getPeer()->peerInterested()) { continue; } p.enableOptUnchoking(); auto& peer = p.getPeer(); A2_LOG_INFO( fmt("OU: %s:%u", peer->getIPAddress().c_str(), peer->getPort())); break; } } } void BtLeecherStateChoke::executeChoke(const PeerSet& peerSet) { A2_LOG_INFO(fmt("Leecher state, %d choke round started", round_)); lastRound_ = global::wallclock(); std::vector peerEntries; for (const auto& p : peerSet) { if (!p->isActive()) { continue; } p->chokingRequired(true); if (p->snubbing()) { p->optUnchoking(false); continue; } peerEntries.push_back(PeerEntry(p)); } // planned optimistic unchoke if (round_ == 0) { plannedOptimisticUnchoke(peerEntries); } regularUnchoke(peerEntries); if (++round_ == 3) { round_ = 0; } } const Timer& BtLeecherStateChoke::getLastRound() const { return lastRound_; } } // namespace aria2