/* */ #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_(0) {} BtLeecherStateChoke::~BtLeecherStateChoke() {} 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) {} BtLeecherStateChoke::PeerEntry::PeerEntry(const PeerEntry& c) : peer_(c.peer_), downloadSpeed_(c.downloadSpeed_), regularUnchoker_(c.regularUnchoker_) {} 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() {} 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(peerEntries.begin(), peerEntries.end(), std::mem_fn(&PeerEntry::disableOptUnchoking)); auto i = std::partition(peerEntries.begin(), peerEntries.end(), PeerFilter(true, true)); if(i != peerEntries.begin()) { std::random_shuffle(peerEntries.begin(), i, *SimpleRandomizer::getInstance()); (*peerEntries.begin()).enableOptUnchoking(); A2_LOG_INFO(fmt("POU: %s", (*peerEntries.begin()).getPeer()->getIPAddress().c_str())); } } void BtLeecherStateChoke::regularUnchoke(std::vector& peerEntries) { auto rest = std::partition(peerEntries.begin(), peerEntries.end(), std::mem_fn(&PeerEntry::isRegularUnchoker)); std::sort(peerEntries.begin(), rest); // the number of regular unchokers int count = 3; bool fastOptUnchoker = false; auto peerIter = peerEntries.begin(); for(;peerIter != rest && count; ++peerIter, --count) { peerIter->disableChokingRequired(); A2_LOG_INFO(fmt("RU: %s, dlspd=%d", (*peerIter).getPeer()->getIPAddress().c_str(), (*peerIter).getDownloadSpeed())); if(peerIter->getPeer()->optUnchoking()) { fastOptUnchoker = true; peerIter->disableOptUnchoking(); } } if(fastOptUnchoker) { std::random_shuffle(peerIter, peerEntries.end(), *SimpleRandomizer::getInstance()); for (auto& p : peerEntries) { if(p.getPeer()->peerInterested()) { p.enableOptUnchoking(); A2_LOG_INFO(fmt("OU: %s", p.getPeer()->getIPAddress().c_str())); break; } else { p.disableChokingRequired(); A2_LOG_INFO(fmt("OU: %s", p.getPeer()->getIPAddress().c_str())); } } } } 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() && !p->snubbing()) { p->chokingRequired(true); 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