2008-11-17 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>

Fixed the bug that causes segmentation fault/bus error during
	executing choking algorithm while seeding. This is caused by
	improper implementation of compare function which returns
	inconsistent results depending on the timing of last unchoke.
	* src/BtSeederStateChoke.cc
	* src/BtSeederStateChoke.h
	* src/DefaultPeerStorage.cc
pull/1/head
Tatsuhiro Tsujikawa 2008-11-17 11:07:04 +00:00
parent 87b442d8c3
commit 644f707519
4 changed files with 126 additions and 77 deletions

View File

@ -1,3 +1,13 @@
2008-11-17 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Fixed the bug that causes segmentation fault/bus error during executing
choking algorithm while seeding. This is caused by improper
implementation of compare function which returns inconsistent results
depending on the timing of last unchoke.
* src/BtSeederStateChoke.cc
* src/BtSeederStateChoke.h
* src/DefaultPeerStorage.cc
2008-11-16 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net> 2008-11-16 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Removed TODO Removed TODO

View File

@ -36,109 +36,127 @@
#include <algorithm> #include <algorithm>
#include "BtContext.h"
#include "Peer.h" #include "Peer.h"
#include "BtMessageDispatcher.h"
#include "BtMessageFactory.h"
#include "BtRequestFactory.h"
#include "BtMessageReceiver.h"
#include "PeerConnection.h"
#include "ExtensionMessageFactory.h"
#include "Logger.h" #include "Logger.h"
#include "LogFactory.h" #include "LogFactory.h"
#include "a2time.h"
#include "SimpleRandomizer.h" #include "SimpleRandomizer.h"
namespace aria2 { namespace aria2 {
BtSeederStateChoke::BtSeederStateChoke(const SharedHandle<BtContext>& btContext): BtSeederStateChoke::BtSeederStateChoke():
_btContext(btContext),
_round(0), _round(0),
_lastRound(0), _lastRound(0),
_logger(LogFactory::getInstance()) {} _logger(LogFactory::getInstance()) {}
BtSeederStateChoke::~BtSeederStateChoke() {} BtSeederStateChoke::~BtSeederStateChoke() {}
class RecentUnchoke { BtSeederStateChoke::PeerEntry::PeerEntry
private: (const SharedHandle<Peer>& peer, const struct timeval& now):
SharedHandle<BtContext> _btContext; _peer(peer),
_outstandingUpload(peer->countOutstandingUpload()),
_lastAmUnchoking(peer->getLastAmUnchoking()),
_recentUnchoking(!_lastAmUnchoking.elapsed(TIME_FRAME)),
_uploadSpeed(peer->calculateUploadSpeed(now))
{}
const struct timeval _now; bool
public: BtSeederStateChoke::PeerEntry::operator<(const PeerEntry& rhs) const
RecentUnchoke(const SharedHandle<BtContext>& btContext, {
const struct timeval& now): if(this->_outstandingUpload && !rhs._outstandingUpload) {
_btContext(btContext), _now(now) {}
bool operator()(Peer* left, Peer* right) const
{
size_t leftUpload = left->countOutstandingUpload();
size_t rightUpload = right->countOutstandingUpload();
if(leftUpload && !rightUpload) {
return true; return true;
} else if(!leftUpload && rightUpload) { } else if(!this->_outstandingUpload && rhs._outstandingUpload) {
return false; return false;
} }
const int TIME_FRAME = 20; if(this->_recentUnchoking &&
if(!left->getLastAmUnchoking().elapsed(TIME_FRAME) && this->_lastAmUnchoking.isNewer(rhs._lastAmUnchoking)) {
left->getLastAmUnchoking().isNewer(right->getLastAmUnchoking())) {
return true; return true;
} else if(!right->getLastAmUnchoking().elapsed(TIME_FRAME)) { } else if(rhs._recentUnchoking) {
return false; return false;
} else { } else {
return left->calculateUploadSpeed(_now) > right->calculateUploadSpeed(_now); return this->_uploadSpeed > rhs._uploadSpeed;
} }
}
SharedHandle<Peer> BtSeederStateChoke::PeerEntry::getPeer() const
{
return _peer;
}
unsigned int BtSeederStateChoke::PeerEntry::getUploadSpeed() const
{
return _uploadSpeed;
}
void BtSeederStateChoke::unchoke
(std::deque<BtSeederStateChoke::PeerEntry>& peers)
{
int count = (_round == 2) ? 4 : 3;
std::sort(peers.begin(), peers.end());
std::deque<PeerEntry>::iterator r = peers.begin();
for(; r != peers.end() && count; ++r, --count) {
(*r).getPeer()->chokingRequired(false);
_logger->info("RU: %s, ulspd=%u", (*r).getPeer()->ipaddr.c_str(),
(*r).getUploadSpeed());
}
if(_round == 2 && r != peers.end()) {
std::random_shuffle(r, peers.end(),
*(SimpleRandomizer::getInstance().get()));
(*r).getPeer()->optUnchoking(true);
_logger->info("POU: %s", (*r).getPeer()->ipaddr.c_str());
}
}
class ChokingRequired {
public:
void operator()(const SharedHandle<Peer>& peer) const
{
peer->chokingRequired(true);
}
};
class GenPeerEntry {
private:
struct timeval _now;
public:
GenPeerEntry()
{
gettimeofday(&_now, 0);
}
BtSeederStateChoke::PeerEntry operator()(const SharedHandle<Peer>& peer) const
{
return BtSeederStateChoke::PeerEntry(peer, _now);
} }
}; };
class NotInterestedPeer { class NotInterestedPeer {
public: public:
bool operator()(const Peer* peer) const bool operator()(const BtSeederStateChoke::PeerEntry& peerEntry) const
{ {
return !peer->peerInterested(); return !peerEntry.getPeer()->peerInterested();
} }
}; };
void BtSeederStateChoke::unchoke(std::deque<Peer*>& peers)
{
int count = (_round == 2) ? 4 : 3;
struct timeval now;
gettimeofday(&now, 0);
std::sort(peers.begin(), peers.end(), RecentUnchoke(_btContext, now));
std::deque<Peer*>::iterator r = peers.begin();
for(; r != peers.end() && count; ++r, --count) {
(*r)->chokingRequired(false);
_logger->info("RU: %s, ulspd=%u", (*r)->ipaddr.c_str(),
(*r)->calculateUploadSpeed(now));
}
if(_round == 2 && r != peers.end()) {
std::random_shuffle(r, peers.end(),
*(SimpleRandomizer::getInstance().get()));
// TODO Is r invalidated here?
(*r)->optUnchoking(true);
_logger->info("POU: %s", (*r)->ipaddr.c_str());
}
}
void void
BtSeederStateChoke::executeChoke(const std::deque<SharedHandle<Peer> >& peerSet) BtSeederStateChoke::executeChoke(const std::deque<SharedHandle<Peer> >& peerSet)
{ {
_logger->info("Seeder state, %d choke round started", _round); _logger->info("Seeder state, %d choke round started", _round);
_lastRound.reset(); _lastRound.reset();
std::deque<Peer*> peers; std::deque<PeerEntry> peerEntries;
std::transform(peerSet.begin(), peerSet.end(), std::back_inserter(peers),
std::mem_fun_ref(&SharedHandle<Peer>::get));
std::for_each(peers.begin(), peers.end(), std::for_each(peerSet.begin(), peerSet.end(), ChokingRequired());
std::bind2nd(std::mem_fun((void (Peer::*)(bool))&Peer::chokingRequired), true));
peers.erase(std::remove_if(peers.begin(), peers.end(), NotInterestedPeer()), std::transform(peerSet.begin(), peerSet.end(),
peers.end()); std::back_inserter(peerEntries), GenPeerEntry());
unchoke(peers); peerEntries.erase(std::remove_if(peerEntries.begin(), peerEntries.end(),
NotInterestedPeer()),
peerEntries.end());
unchoke(peerEntries);
if(++_round == 3) { if(++_round == 3) {
_round = 0; _round = 0;

View File

@ -36,29 +36,50 @@
#define _D_BT_SEEDER_STATE_CHOKE_H_ #define _D_BT_SEEDER_STATE_CHOKE_H_
#include "common.h" #include "common.h"
#include <deque>
#include "SharedHandle.h" #include "SharedHandle.h"
#include "TimeA2.h" #include "TimeA2.h"
#include <deque>
namespace aria2 { namespace aria2 {
class BtContext;
class Peer; class Peer;
class Logger; class Logger;
class BtSeederStateChoke { class BtSeederStateChoke {
private: private:
SharedHandle<BtContext> _btContext;
int _round; int _round;
Time _lastRound; Time _lastRound;
Logger* _logger; Logger* _logger;
void unchoke(std::deque<Peer*>& peers); class PeerEntry {
private:
SharedHandle<Peer> _peer;
size_t _outstandingUpload;
Time _lastAmUnchoking;
bool _recentUnchoking;
unsigned int _uploadSpeed;
const static time_t TIME_FRAME = 20;
public:
PeerEntry(const SharedHandle<Peer>& peer, const struct timeval& now);
bool operator<(const PeerEntry& rhs) const;
SharedHandle<Peer> getPeer() const;
unsigned int getUploadSpeed() const;
};
void unchoke(std::deque<PeerEntry>& peers);
friend class GenPeerEntry;
friend class NotInterestedPeer;
public: public:
BtSeederStateChoke(const SharedHandle<BtContext>& btContext); BtSeederStateChoke();
~BtSeederStateChoke(); ~BtSeederStateChoke();

View File

@ -57,7 +57,7 @@ DefaultPeerStorage::DefaultPeerStorage(const BtContextHandle& btContext,
maxPeerListSize(BtRuntime::MAX_PEERS+(BtRuntime::MAX_PEERS >> 2)), maxPeerListSize(BtRuntime::MAX_PEERS+(BtRuntime::MAX_PEERS >> 2)),
removedPeerSessionDownloadLength(0), removedPeerSessionDownloadLength(0),
removedPeerSessionUploadLength(0), removedPeerSessionUploadLength(0),
_seederStateChoke(new BtSeederStateChoke(btContext)), _seederStateChoke(new BtSeederStateChoke()),
_leecherStateChoke(new BtLeecherStateChoke()) _leecherStateChoke(new BtLeecherStateChoke())
{} {}