/* */ #include "BtLeecherStateChoke.h" #include #include "Peer.h" #include "Logger.h" #include "LogFactory.h" #include "a2time.h" #include "SimpleRandomizer.h" namespace aria2 { BtLeecherStateChoke::BtLeecherStateChoke(): _round(0), _lastRound(0), _logger(LogFactory::getInstance()) {} BtLeecherStateChoke::~BtLeecherStateChoke() {} BtLeecherStateChoke::PeerEntry::PeerEntry (const SharedHandle& peer, const struct timeval& now): _peer(peer), _downloadSpeed(peer->calculateDownloadSpeed(now)), // peer must be interested to us and sent block in the last 30 seconds _regularUnchoker(peer->peerInterested() && !peer->getLastDownloadUpdate().elapsed(30)) {} const SharedHandle& BtLeecherStateChoke::PeerEntry::getPeer() const { return _peer; } unsigned 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::isSnubbing() const { return _peer->snubbing(); } bool BtLeecherStateChoke::PeerEntry::operator<(const PeerEntry& peerEntry) const { return _downloadSpeed > peerEntry._downloadSpeed; } class PeerFilter { private: bool _amChoking; bool _peerInterested; public: PeerFilter(bool amChoking, bool peerInterested): _amChoking(amChoking), _peerInterested(peerInterested) {} bool operator()(const BtLeecherStateChoke::PeerEntry& peerEntry) const { return peerEntry.getPeer()->amChoking() == _amChoking && peerEntry.getPeer()->peerInterested() == _peerInterested; } }; void BtLeecherStateChoke::plannedOptimisticUnchoke (std::deque& peerEntries) { std::for_each(peerEntries.begin(), peerEntries.end(), std::mem_fun_ref(&PeerEntry::disableOptUnchoking)); std::deque::iterator i = std::partition(peerEntries.begin(), peerEntries.end(), PeerFilter(true, true)); if(i != peerEntries.begin()) { std::random_shuffle(peerEntries.begin(), i, *(SimpleRandomizer::getInstance().get())); (*peerEntries.begin()).enableOptUnchoking(); _logger->info("POU: %s", (*peerEntries.begin()).getPeer()->ipaddr.c_str()); } } void BtLeecherStateChoke::regularUnchoke(std::deque& peerEntries) { std::deque::iterator rest = std::partition(peerEntries.begin(), peerEntries.end(), std::mem_fun_ref(&PeerEntry::isRegularUnchoker)); struct timeval now; gettimeofday(&now, 0); std::sort(peerEntries.begin(), rest); // the number of regular unchokers int count = 3; bool fastOptUnchoker = false; std::deque::iterator peerIter = peerEntries.begin(); for(;peerIter != rest && count; ++peerIter, --count) { (*peerIter).disableChokingRequired(); _logger->info("RU: %s, dlspd=%u", (*peerIter).getPeer()->ipaddr.c_str(), (*peerIter).getDownloadSpeed()); if((*peerIter).getPeer()->optUnchoking()) { fastOptUnchoker = true; (*peerIter).disableOptUnchoking(); } } if(fastOptUnchoker) { std::random_shuffle(peerIter, peerEntries.end(), *(SimpleRandomizer::getInstance().get())); for(std::deque::iterator i = peerIter; i != peerEntries.end(); ++i) { if((*i).getPeer()->peerInterested()) { (*i).enableOptUnchoking(); _logger->info("OU: %s", (*i).getPeer()->ipaddr.c_str()); break; } else { (*i).disableChokingRequired(); _logger->info("OU: %s", (*i).getPeer()->ipaddr.c_str()); } } } } class BtLeecherStateChokeGenPeerEntry { private: struct timeval _now; public: BtLeecherStateChokeGenPeerEntry() { gettimeofday(&_now, 0); } BtLeecherStateChoke::PeerEntry operator() (const SharedHandle& peer) const { return BtLeecherStateChoke::PeerEntry(peer, _now); } }; void BtLeecherStateChoke::executeChoke(const std::deque >& peerSet) { _logger->info("Leecher state, %d choke round started", _round); _lastRound.reset(); std::deque peerEntries; std::transform(peerSet.begin(), peerSet.end(), std::back_inserter(peerEntries), BtLeecherStateChokeGenPeerEntry()); peerEntries.erase(std::remove_if(peerEntries.begin(), peerEntries.end(), std::mem_fun_ref(&PeerEntry::isSnubbing)), peerEntries.end()); std::for_each(peerEntries.begin(), peerEntries.end(), std::mem_fun_ref(&PeerEntry::enableChokingRequired)); // planned optimistic unchoke if(_round == 0) { plannedOptimisticUnchoke(peerEntries); } regularUnchoke(peerEntries); if(++_round == 3) { _round = 0; } } const Time& BtLeecherStateChoke::getLastRound() const { return _lastRound; } } // namespace aria2