Sfoglia il codice sorgente

2008-04-13 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	Rewritten choking algorithm.
	* src/PeerChokeCommand.{cc, h}
	* src/BtInterestedMessage.cc
	* src/PeerSessionResource.{cc, h}
	* src/DefaultPeerStorage.{cc, h}
	* src/BtNotInterestedMessage.cc
	* src/DefaultBtMessageDispatcher.{cc, h}
	* src/BtMessageDispatcher.h
	* src/Peer.{cc, h}
	* src/BtLeecherStateChoke.{cc, h}
	* src/BtSetup.cc
	* src/BtSeederStateChoke.{cc, h}
	* src/PeerStorage.h
	* test/MockPeerStorage.h
	* test/MockBtMessageDispatcher.h
Tatsuhiro Tsujikawa 17 anni fa
parent
commit
d13c416a94

+ 18 - 0
ChangeLog

@@ -1,3 +1,21 @@
+2008-04-13  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	Rewritten choking algorithm.
+	* src/PeerChokeCommand.{cc, h}
+	* src/BtInterestedMessage.cc
+	* src/PeerSessionResource.{cc, h}
+	* src/DefaultPeerStorage.{cc, h}
+	* src/BtNotInterestedMessage.cc
+	* src/DefaultBtMessageDispatcher.{cc, h}
+	* src/BtMessageDispatcher.h
+	* src/Peer.{cc, h}
+	* src/BtLeecherStateChoke.{cc, h}
+	* src/BtSetup.cc
+	* src/BtSeederStateChoke.{cc, h}
+	* src/PeerStorage.h
+	* test/MockPeerStorage.h
+	* test/MockBtMessageDispatcher.h
+
 2008-04-09  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
 	Fixed compilation error on x84-64 platform.

+ 6 - 0
src/BtInterestedMessage.cc

@@ -37,6 +37,9 @@
 #include "DlAbortEx.h"
 #include "message.h"
 #include "Peer.h"
+#include "BtRegistry.h"
+#include "BtContext.h"
+#include "PeerStorage.h"
 
 namespace aria2 {
 
@@ -54,6 +57,9 @@ BtInterestedMessageHandle BtInterestedMessage::create(const unsigned char* data,
 
 void BtInterestedMessage::doReceivedAction() {
   peer->peerInterested(true);
+  if(!peer->amChoking()) {
+    PEER_STORAGE(btContext)->executeChoke();
+  }
 }
 
 bool BtInterestedMessage::sendPredicate() const {

+ 179 - 0
src/BtLeecherStateChoke.cc

@@ -0,0 +1,179 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "BtLeecherStateChoke.h"
+#include "Peer.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "a2time.h"
+#include <algorithm>
+
+namespace aria2 {
+
+BtLeecherStateChoke::BtLeecherStateChoke():
+  _round(0),
+  _lastRound(0),
+  _logger(LogFactory::getInstance()) {}
+
+BtLeecherStateChoke::~BtLeecherStateChoke() {}
+
+class PeerFilter {
+private:
+  bool _amChoking;
+  bool _peerInterested;
+public:
+  PeerFilter(bool amChoking, bool peerInterested):
+    _amChoking(amChoking),
+    _peerInterested(peerInterested) {}
+
+  bool operator()(const Peer* peer) const
+  {
+    return peer->amChoking() == _amChoking &&
+      peer->peerInterested() == _peerInterested;
+  }
+};
+
+class RegularUnchoker {
+public:
+  bool operator()(const Peer* peer) const
+  {
+    // peer must be interested to us and sent block in the last 30 seconds
+    return peer->peerInterested() && !peer->getLastDownloadUpdate().elapsed(30);
+  }
+};
+
+class DownloadFaster {
+private:
+  const struct timeval _now;
+public:
+
+  DownloadFaster(const struct timeval& now):_now(now) {}
+
+  bool operator() (Peer* left, Peer* right) const
+  {
+    return left->calculateDownloadSpeed(_now) > right->calculateDownloadSpeed(_now);
+  }
+};
+
+class SnubbedPeer {
+public:
+  bool operator() (const Peer* peer) const
+  {
+    return peer->snubbing();
+  }
+};
+
+void BtLeecherStateChoke::plannedOptimisticUnchoke(std::deque<Peer*>& peers)
+{
+  std::for_each(peers.begin(), peers.end(),
+		std::bind2nd(std::mem_fun((void (Peer::*)(bool))&Peer::optUnchoking), false));
+  
+  std::deque<Peer*>::iterator i = std::partition(peers.begin(), peers.end(), PeerFilter(true, true));
+  if(i != peers.begin()) {
+    std::random_shuffle(peers.begin(), i);
+    (*peers.begin())->optUnchoking(true);
+    _logger->info("POU: %s", (*peers.begin())->ipaddr.c_str());
+  }
+}
+
+void BtLeecherStateChoke::regularUnchoke(std::deque<Peer*>& peers)
+{
+  std::deque<Peer*>::iterator rest = std::partition(peers.begin(), peers.end(), RegularUnchoker());
+  
+  struct timeval now;
+  gettimeofday(&now, 0);
+
+  std::sort(peers.begin(), rest, DownloadFaster(now));
+
+  // the number of regular unchokers
+  int count = 3;
+
+  bool fastOptUnchoker = false;
+  std::deque<Peer*>::iterator peerIter = peers.begin();
+  for(;peerIter != rest && count; ++peerIter, --count) {
+    (*peerIter)->chokingRequired(false);
+    _logger->info("RU: %s, dlspd=%u", (*peerIter)->ipaddr.c_str(), (*peerIter)->calculateDownloadSpeed(now));
+    if((*peerIter)->optUnchoking()) {
+      fastOptUnchoker = true;
+      (*peerIter)->optUnchoking(false);
+    }
+  }
+  if(fastOptUnchoker) {
+    std::random_shuffle(peerIter, peers.end());
+    for(std::deque<Peer*>::iterator i = peerIter; i != peers.end(); ++i) {
+      if((*i)->peerInterested()) {
+	(*i)->optUnchoking(true);
+	_logger->info("OU: %s", (*i)->ipaddr.c_str());
+	break;
+      } else {
+	(*i)->chokingRequired(false);
+	_logger->info("OU: %s", (*i)->ipaddr.c_str());
+      }
+    }
+  }
+}
+
+void
+BtLeecherStateChoke::executeChoke(const std::deque<SharedHandle<Peer> >& peerSet)
+{
+  _logger->info("Leecher state, %d choke round started", _round);
+  _lastRound.reset();
+
+  std::deque<Peer*> peers;
+  std::transform(peerSet.begin(), peerSet.end(), std::back_inserter(peers),
+		 std::mem_fun_ref(&SharedHandle<Peer>::get));
+
+  peers.erase(std::remove_if(peers.begin(), peers.end(), SnubbedPeer()),
+	      peers.end());
+	      
+  std::for_each(peers.begin(), peers.end(),
+		std::bind2nd(std::mem_fun((void (Peer::*)(bool))&Peer::chokingRequired), true));
+
+  // planned optimistic unchoke
+  if(_round == 0) {
+    plannedOptimisticUnchoke(peers);
+  }
+  regularUnchoke(peers);
+
+  if(++_round == 3) {
+    _round = 0;
+  }
+}
+
+const Time& BtLeecherStateChoke::getLastRound() const
+{
+  return _lastRound;
+}
+
+} // namespace aria2

+ 72 - 0
src/BtLeecherStateChoke.h

@@ -0,0 +1,72 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_BT_LEECHER_STATE_CHOKE_H_
+#define _D_BT_LEECHER_STATE_CHOKE_H_
+
+#include "common.h"
+#include "SharedHandle.h"
+#include "TimeA2.h"
+#include <deque>
+
+namespace aria2 {
+
+class Peer;
+class Logger;
+
+class BtLeecherStateChoke {
+private:
+  int _round;
+
+  Time _lastRound;
+
+  const Logger* _logger;
+
+  void plannedOptimisticUnchoke(std::deque<Peer*>& peers);
+
+  void regularUnchoke(std::deque<Peer*>& peers);
+
+public:
+  BtLeecherStateChoke();
+
+  ~BtLeecherStateChoke();
+
+  void executeChoke(const std::deque<SharedHandle<Peer> >& peerSet);
+
+  const Time& getLastRound() const;
+};
+
+} // namespace aria2
+
+#endif // _D_BT_LEECHER_STATE_CHOKE_H_

+ 2 - 0
src/BtMessageDispatcher.h

@@ -81,6 +81,8 @@ public:
   virtual void removeOutstandingRequest(const RequestSlot& slot) = 0;
 
   virtual void addOutstandingRequest(const RequestSlot& slot) = 0;
+
+  virtual size_t countOutstandingUpload() = 0;
 };
 
 typedef SharedHandle<BtMessageDispatcher> BtMessageDispatcherHandle;

+ 6 - 0
src/BtNotInterestedMessage.cc

@@ -37,6 +37,9 @@
 #include "DlAbortEx.h"
 #include "message.h"
 #include "Peer.h"
+#include "BtRegistry.h"
+#include "BtContext.h"
+#include "PeerStorage.h"
 
 namespace aria2 {
 
@@ -54,6 +57,9 @@ BtNotInterestedMessageHandle BtNotInterestedMessage::create(const unsigned char*
 
 void BtNotInterestedMessage::doReceivedAction() {
   peer->peerInterested(false);
+  if(!peer->amChoking()) {
+    PEER_STORAGE(btContext)->executeChoke();
+  }
 }
 
 bool BtNotInterestedMessage::sendPredicate() const {

+ 151 - 0
src/BtSeederStateChoke.cc

@@ -0,0 +1,151 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "BtSeederStateChoke.h"
+#include "BtContext.h"
+#include "Peer.h"
+#include "BtRegistry.h"
+#include "PeerObject.h"
+#include "BtMessageDispatcher.h"
+#include "BtMessageFactory.h"
+#include "BtRequestFactory.h"
+#include "BtMessageReceiver.h"
+#include "PeerConnection.h"
+#include "ExtensionMessageFactory.h"
+#include "Logger.h"
+#include "LogFactory.h"
+#include "a2time.h"
+#include <algorithm>
+
+namespace aria2 {
+
+BtSeederStateChoke::BtSeederStateChoke(const SharedHandle<BtContext>& btContext):
+  _btContext(btContext),
+  _round(0),
+  _lastRound(0),
+  _logger(LogFactory::getInstance()) {}
+
+BtSeederStateChoke::~BtSeederStateChoke() {}
+
+class RecentUnchoke {
+private:
+  SharedHandle<BtContext> _btContext;
+
+  const struct timeval _now;
+public:
+  RecentUnchoke(const SharedHandle<BtContext>& btContext,
+		const struct timeval& now):
+    _btContext(btContext), _now(now) {}
+
+  bool operator()(Peer* left, Peer* right) const
+  {
+    size_t leftUpload = BT_MESSAGE_DISPATCHER(_btContext, left)->countOutstandingRequest();
+    size_t rightUpload = BT_MESSAGE_DISPATCHER(_btContext, right)->countOutstandingRequest();
+    if(leftUpload && !rightUpload) {
+      return true;
+    } else if(!leftUpload && rightUpload) {
+      return false;
+    }
+    const int TIME_FRAME = 20;
+    if(!left->getLastAmUnchoking().elapsed(TIME_FRAME) &&
+       left->getLastAmUnchoking().isNewer(right->getLastAmUnchoking())) {
+      return true;
+    } else if(!right->getLastAmUnchoking().elapsed(TIME_FRAME) &&
+	      right->getLastAmUnchoking().isNewer(left->getLastAmUnchoking())) {
+      return false;
+    } else {
+      return left->calculateUploadSpeed(_now) > right->calculateUploadSpeed(_now);
+    }
+  }
+};
+
+class NotInterestedPeer {
+public:
+  bool operator()(const Peer* peer) const
+  {
+    return !peer->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());
+    (*r)->optUnchoking(true);
+    _logger->info("POU: %s", (*r)->ipaddr.c_str());
+  }
+}
+
+void
+BtSeederStateChoke::executeChoke(const std::deque<SharedHandle<Peer> >& peerSet)
+{
+  _logger->info("Seeder state, %d choke round started", _round);
+  _lastRound.reset();
+
+  std::deque<Peer*> peers;
+  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::bind2nd(std::mem_fun((void (Peer::*)(bool))&Peer::chokingRequired), true));
+
+  peers.erase(std::remove_if(peers.begin(), peers.end(), NotInterestedPeer()),
+	      peers.end());
+
+  unchoke(peers);
+  
+  if(++_round == 3) {
+    _round = 0;
+  }
+}
+
+const Time& BtSeederStateChoke::getLastRound() const
+{
+  return _lastRound;
+}
+
+} // namespace aria2

+ 72 - 0
src/BtSeederStateChoke.h

@@ -0,0 +1,72 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL.  If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so.  If you
+ * do not wish to do so, delete this exception statement from your
+ * version.  If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#ifndef _D_BT_SEEDER_STATE_CHOKE_H_
+#define _D_BT_SEEDER_STATE_CHOKE_H_
+
+#include "common.h"
+#include "SharedHandle.h"
+#include "TimeA2.h"
+#include <deque>
+
+namespace aria2 {
+
+class BtContext;
+class Peer;
+class Logger;
+
+class BtSeederStateChoke {
+private:
+  SharedHandle<BtContext> _btContext;
+
+  int _round;
+
+  Time _lastRound;
+
+  const Logger* _logger;
+
+  void unchoke(std::deque<Peer*>& peers);
+public:
+  BtSeederStateChoke(const SharedHandle<BtContext>& btContext);
+
+  ~BtSeederStateChoke();
+
+  void executeChoke(const std::deque<SharedHandle<Peer> >& peerSet);
+
+  const Time& getLastRound() const;
+};
+
+} // namespace aria2
+
+#endif // _D_BT_SEEDER_STATE_CHOKE_H_

+ 1 - 3
src/BtSetup.cc

@@ -76,10 +76,8 @@ Commands BtSetup::setup(RequestGroup* requestGroup,
 					       e,
 					       btContext));
   commands.push_back(new PeerChokeCommand(CUIDCounterSingletonHolder::instance()->newID(),
-					  requestGroup,
 					  e,
-					  btContext,
-					  10));
+					  btContext));
 
   commands.push_back(new ActivePeerConnectionCommand(CUIDCounterSingletonHolder::instance()->newID(),
 						     requestGroup, e, btContext, 10));

+ 7 - 0
src/DefaultBtMessageDispatcher.cc

@@ -49,6 +49,7 @@
 #include "Piece.h"
 #include "LogFactory.h"
 #include "Logger.h"
+#include "a2functional.h"
 #include <algorithm>
 
 namespace aria2 {
@@ -266,6 +267,12 @@ void DefaultBtMessageDispatcher::addOutstandingRequest(const RequestSlot& reques
   }
 }
 
+size_t DefaultBtMessageDispatcher::countOutstandingUpload()
+{
+  return std::count_if(messageQueue.begin(), messageQueue.end(),
+		       mem_fun_sh(&BtMessage::isUploading));
+}
+
 std::deque<SharedHandle<BtMessage> >&
 DefaultBtMessageDispatcher::getMessageQueue()
 {

+ 2 - 0
src/DefaultBtMessageDispatcher.h

@@ -101,6 +101,8 @@ public:
 
   virtual void addOutstandingRequest(const RequestSlot& requestSlot);
 
+  virtual size_t countOutstandingUpload();
+
   std::deque<SharedHandle<BtMessage> >& getMessageQueue();
 
   RequestSlots& getRequestSlots();

+ 30 - 2
src/DefaultPeerStorage.cc

@@ -41,6 +41,9 @@
 #include "Peer.h"
 #include "BtContext.h"
 #include "BtRuntime.h"
+#include "BtSeederStateChoke.h"
+#include "BtLeecherStateChoke.h"
+#include "PieceStorage.h"
 #include <algorithm>
 
 namespace aria2 {
@@ -52,12 +55,18 @@ DefaultPeerStorage::DefaultPeerStorage(const BtContextHandle& btContext,
   maxPeerListSize(MAX_PEER_LIST_SIZE),
   btRuntime(BT_RUNTIME(btContext)),
   removedPeerSessionDownloadLength(0),
-  removedPeerSessionUploadLength(0)
+  removedPeerSessionUploadLength(0),
+  _seederStateChoke(new BtSeederStateChoke(btContext)),
+  _leecherStateChoke(new BtLeecherStateChoke())
 {
   logger = LogFactory::getInstance();
 }
 
-DefaultPeerStorage::~DefaultPeerStorage() {}
+DefaultPeerStorage::~DefaultPeerStorage()
+{
+  delete _seederStateChoke;
+  delete _leecherStateChoke;
+}
 
 class FindIdenticalPeer {
 private:
@@ -246,4 +255,23 @@ void DefaultPeerStorage::returnPeer(const PeerHandle& peer)
   }
 }
 
+bool DefaultPeerStorage::chokeRoundIntervalElapsed()
+{
+  const time_t CHOKE_ROUND_INTERVAL = 10;
+  if(PIECE_STORAGE(btContext)->downloadFinished()) {
+    return _seederStateChoke->getLastRound().elapsed(CHOKE_ROUND_INTERVAL);
+  } else {
+    return _leecherStateChoke->getLastRound().elapsed(CHOKE_ROUND_INTERVAL);
+  }
+}
+
+void DefaultPeerStorage::executeChoke()
+{
+  if(PIECE_STORAGE(btContext)->downloadFinished()) {
+    return _seederStateChoke->executeChoke(getActivePeers());
+  } else {
+    return _leecherStateChoke->executeChoke(getActivePeers());
+  }
+}
+
 } // namespace aria2

+ 9 - 0
src/DefaultPeerStorage.h

@@ -46,6 +46,8 @@ class BtContext;
 class Option;
 class Logger;
 class BtRuntime;
+class BtSeederStateChoke;
+class BtLeecherStateChoke;
 
 class DefaultPeerStorage : public PeerStorage {
 private:
@@ -58,6 +60,9 @@ private:
   uint64_t removedPeerSessionDownloadLength;
   uint64_t removedPeerSessionUploadLength;
 
+  BtSeederStateChoke* _seederStateChoke;
+  BtLeecherStateChoke* _leecherStateChoke;
+
   bool isPeerAlreadyAdded(const SharedHandle<Peer>& peer);
 public:
   DefaultPeerStorage(const SharedHandle<BtContext>& btContext,
@@ -91,6 +96,10 @@ public:
 
   virtual void returnPeer(const SharedHandle<Peer>& peer);
 
+  virtual bool chokeRoundIntervalElapsed();
+
+  virtual void executeChoke();
+
   void setMaxPeerListSize(size_t size) { this->maxPeerListSize = size; }
  
   size_t getMaxPeerListSize() const { return maxPeerListSize; }

+ 3 - 1
src/Makefile.am

@@ -367,7 +367,9 @@ SRCS += MetaEntry.h\
 	LibsslARC4Decryptor.h\
 	LibsslARC4Encryptor.h\
 	LibsslDHKeyExchange.h\
-	BtConstants.h
+	BtConstants.h\
+	BtLeecherStateChoke.cc BtLeecherStateChoke.h\
+	BtSeederStateChoke.cc BtSeederStateChoke.h
 endif # ENABLE_BITTORRENT
 
 if ENABLE_METALINK

+ 11 - 3
src/Makefile.in

@@ -221,7 +221,9 @@ bin_PROGRAMS = aria2c$(EXEEXT)
 @ENABLE_BITTORRENT_TRUE@	LibsslARC4Decryptor.h\
 @ENABLE_BITTORRENT_TRUE@	LibsslARC4Encryptor.h\
 @ENABLE_BITTORRENT_TRUE@	LibsslDHKeyExchange.h\
-@ENABLE_BITTORRENT_TRUE@	BtConstants.h
+@ENABLE_BITTORRENT_TRUE@	BtConstants.h\
+@ENABLE_BITTORRENT_TRUE@	BtLeecherStateChoke.cc BtLeecherStateChoke.h\
+@ENABLE_BITTORRENT_TRUE@	BtSeederStateChoke.cc BtSeederStateChoke.h
 
 @ENABLE_METALINK_TRUE@am__append_3 = Metalinker.cc Metalinker.h\
 @ENABLE_METALINK_TRUE@	MetalinkEntry.cc MetalinkEntry.h\
@@ -522,7 +524,9 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 	LibgcryptARC4Decryptor.h LibgcryptARC4Encryptor.h \
 	LibgcryptDHKeyExchange.h LibsslARC4Context.h \
 	LibsslARC4Decryptor.h LibsslARC4Encryptor.h \
-	LibsslDHKeyExchange.h BtConstants.h Metalinker.cc Metalinker.h \
+	LibsslDHKeyExchange.h BtConstants.h BtLeecherStateChoke.cc \
+	BtLeecherStateChoke.h BtSeederStateChoke.cc \
+	BtSeederStateChoke.h Metalinker.cc Metalinker.h \
 	MetalinkEntry.cc MetalinkEntry.h MetalinkResource.cc \
 	MetalinkResource.h MetalinkProcessor.h \
 	MetalinkProcessorFactory.cc MetalinkProcessorFactory.h \
@@ -667,7 +671,9 @@ am__libaria2c_a_SOURCES_DIST = Socket.h SocketCore.cc SocketCore.h \
 @ENABLE_BITTORRENT_TRUE@	DHTRegistry.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	InitiatorMSEHandshakeCommand.$(OBJEXT) \
 @ENABLE_BITTORRENT_TRUE@	ReceiverMSEHandshakeCommand.$(OBJEXT) \
-@ENABLE_BITTORRENT_TRUE@	MSEHandshake.$(OBJEXT)
+@ENABLE_BITTORRENT_TRUE@	MSEHandshake.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	BtLeecherStateChoke.$(OBJEXT) \
+@ENABLE_BITTORRENT_TRUE@	BtSeederStateChoke.$(OBJEXT)
 @ENABLE_METALINK_TRUE@am__objects_3 = Metalinker.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkEntry.$(OBJEXT) \
 @ENABLE_METALINK_TRUE@	MetalinkResource.$(OBJEXT) \
@@ -1234,6 +1240,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtHaveNoneMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtInterestedMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtKeepAliveMessage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtLeecherStateChoke.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtNotInterestedMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtPieceMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtPortMessage.Po@am__quote@
@@ -1241,6 +1248,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtRegistry.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtRejectMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtRequestMessage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtSeederStateChoke.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtSetup.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtSuggestPieceMessage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BtUnchokeMessage.Po@am__quote@

+ 12 - 0
src/Peer.cc

@@ -442,4 +442,16 @@ const Time& Peer::getBadConditionStartTime() const
   return _badConditionStartTime;
 }
 
+const Time& Peer::getLastDownloadUpdate() const
+{
+  assert(_res);
+  return _res->getLastDownloadUpdate();
+}
+
+const Time& Peer::getLastAmUnchoking() const
+{
+  assert(_res);
+  return _res->getLastAmUnchoking();
+}
+
 } // namespace aria2

+ 4 - 0
src/Peer.h

@@ -231,6 +231,10 @@ public:
   std::string getExtensionName(uint8_t id) const;
 
   void setExtension(const std::string& name, uint8_t id);
+
+  const Time& getLastDownloadUpdate() const;
+
+  const Time& getLastAmUnchoking() const;
 };
 
 typedef SharedHandle<Peer> PeerHandle;

+ 4 - 116
src/PeerChokeCommand.cc

@@ -33,141 +33,29 @@
  */
 /* copyright --> */
 #include "PeerChokeCommand.h"
-#include "Util.h"
-#include "Peer.h"
 #include "DownloadEngine.h"
 #include "BtContext.h"
 #include "BtRuntime.h"
-#include "PieceStorage.h"
 #include "PeerStorage.h"
-#include "Logger.h"
-#include <algorithm>
 
 namespace aria2 {
 
 PeerChokeCommand::PeerChokeCommand(int32_t cuid,
-				   RequestGroup* requestGroup,
 				   DownloadEngine* e,
-				   const BtContextHandle& btContext,
-				   time_t interval):
+				   const BtContextHandle& btContext):
   Command(cuid),
   BtContextAwareCommand(btContext),
-  RequestGroupAware(requestGroup),
-  interval(interval),
-  e(e),
-  rotate(0)
+  e(e)
 {}
 
 PeerChokeCommand::~PeerChokeCommand() {}
 
-class ChokePeer {
-public:
-  ChokePeer() {}
-  void operator()(PeerHandle& peer) {
-    peer->chokingRequired(true);
-  }
-};
-
-void PeerChokeCommand::optUnchokingPeer(Peers& peers) const {
-  if(peers.empty()) {
-    return;
-  }
-  std::random_shuffle(peers.begin(), peers.end());
-  unsigned int optUnchokCount = 1;
-  for(Peers::iterator itr = peers.begin(); itr != peers.end(); itr++) {
-    Peers::value_type peer = *itr;
-    if(optUnchokCount > 0 && !peer->snubbing()) {
-      optUnchokCount--;
-      peer->optUnchoking(true);
-      logger->debug("opt, unchoking %s, download speed=%d",
-		    peer->ipaddr.c_str(), peer->calculateDownloadSpeed());
-    } else {
-      peer->optUnchoking(false);
-    }
-  }
-}
-
-class UploadFaster {
-public:
-  bool operator() (const PeerHandle& left, const PeerHandle& right) const {
-    return left->calculateUploadSpeed() > right->calculateUploadSpeed();
-  }
-};
-
-void PeerChokeCommand::orderByUploadRate(Peers& peers) const {
-  std::sort(peers.begin(), peers.end(), UploadFaster());
-}
-
-class DownloadFaster {
-public:
-  bool operator() (const PeerHandle& left, const PeerHandle& right) const {
-    return left->calculateDownloadSpeed() > right->calculateDownloadSpeed();
-  }
-};
-
-void PeerChokeCommand::orderByDownloadRate(Peers& peers) const {
-  std::sort(peers.begin(), peers.end(), DownloadFaster());
-}
-
 bool PeerChokeCommand::execute() {
   if(btRuntime->isHalt()) {
     return true;
   }
-  if(checkPoint.elapsed(interval)) {
-    checkPoint.reset();
-    Peers peers = peerStorage->getActivePeers();
-    std::for_each(peers.begin(), peers.end(), ChokePeer());
-    if(pieceStorage->downloadFinished()) {
-      orderByUploadRate(peers);
-    } else {
-      orderByDownloadRate(peers);
-    }
-    unsigned int unchokingCount = 4;//peers.size() >= 4 ? 4 : peers.size();
-    for(Peers::iterator itr = peers.begin(); itr != peers.end() && unchokingCount > 0; ) {
-      PeerHandle peer = *itr;
-      if(peer->peerInterested() && !peer->snubbing()) {
-	unchokingCount--;
-	peer->chokingRequired(false);
-	peer->optUnchoking(false);
-	itr = peers.erase(itr);
-	if(pieceStorage->downloadFinished()) {
-	  logger->debug("cat01, unchoking %s, upload speed=%d",
-			peer->ipaddr.c_str(),
-			peer->calculateUploadSpeed());
-	} else {
-	  logger->debug("cat01, unchoking %s, download speed=%d",
-			peer->ipaddr.c_str(),
-			peer->calculateDownloadSpeed());
-	}
-      } else {
-	itr++;
-      }
-    }
-    for(Peers::iterator itr = peers.begin(); itr != peers.end(); ) {
-      PeerHandle peer = *itr;
-      if(!peer->peerInterested() && !peer->snubbing()) {
-	peer->chokingRequired(false);
-	peer->optUnchoking(false);
-	itr = peers.erase(itr);
-	if(pieceStorage->downloadFinished()) {
-	  logger->debug("cat02, unchoking %s, upload speed=%d",
-			peer->ipaddr.c_str(),
-			peer->calculateUploadSpeed());
-	} else {
-	  logger->debug("cat02, unchoking %s, download speed=%d",
-			peer->ipaddr.c_str(),
-			peer->calculateDownloadSpeed());
-	}
-	break;
-      } else {
-	itr++;
-      }
-    }
-    if(rotate%3 == 0) {
-      optUnchokingPeer(peers);
-      rotate = 0;
-    }
-    rotate++;
+  if(peerStorage->chokeRoundIntervalElapsed()) {
+    peerStorage->executeChoke();
   }
   e->commands.push_back(this);
   return false;

+ 2 - 15
src/PeerChokeCommand.h

@@ -37,34 +37,21 @@
 
 #include "Command.h"
 #include "BtContextAwareCommand.h"
-#include "RequestGroupAware.h"
-#include "TimeA2.h"
 
 namespace aria2 {
 
 class DownloadEngine;
-class Peer;
 
 class PeerChokeCommand : public Command,
-			 public BtContextAwareCommand,
-			 public RequestGroupAware
+			 public BtContextAwareCommand
 {
 private:
-  time_t interval;
   DownloadEngine* e;
-  unsigned int rotate;
-  Time checkPoint;
-
-  void orderByUploadRate(std::deque<SharedHandle<Peer> >& peers) const;
-  void orderByDownloadRate(std::deque<SharedHandle<Peer> >& peers) const;
-  void optUnchokingPeer(std::deque<SharedHandle<Peer> >& peers) const;
 
 public:
   PeerChokeCommand(int32_t cuid,
-		   RequestGroup* requestGroup,
 		   DownloadEngine* e,
-		   const SharedHandle<BtContext>& btContext,
-		   time_t interval);
+		   const SharedHandle<BtContext>& btContext);
 
   virtual ~PeerChokeCommand();
 

+ 22 - 1
src/PeerSessionResource.cc

@@ -53,7 +53,9 @@ PeerSessionResource::PeerSessionResource(size_t pieceLength, uint64_t totalLengt
   _dhtEnabled(false),
   _latency(DEFAULT_LATENCY),
   _uploadLength(0),
-  _downloadLength(0)
+  _downloadLength(0),
+  _lastDownloadUpdate(0),
+  _lastAmUnchoking(0)
 {}
 
 PeerSessionResource::~PeerSessionResource()
@@ -69,6 +71,9 @@ bool PeerSessionResource::amChoking() const
 void PeerSessionResource::amChoking(bool b)
 {
   _amChoking = b;
+  if(!b) {
+    _lastAmUnchoking.reset();
+  }
 }
 
 bool PeerSessionResource::amInterested() const
@@ -137,6 +142,10 @@ bool PeerSessionResource::snubbing() const
 void PeerSessionResource::snubbing(bool b)
 {
   _snubbing = b;
+  if(_snubbing) {
+    chokingRequired(true);
+    optUnchoking(false);
+  }
 }
 
 bool PeerSessionResource::hasAllPieces() const
@@ -311,6 +320,18 @@ void PeerSessionResource::updateDownloadLength(size_t bytes)
 {
   _peerStat.updateDownloadLength(bytes);
   _downloadLength += bytes;
+
+  _lastDownloadUpdate.reset();
+}
+
+const Time& PeerSessionResource::getLastDownloadUpdate() const
+{
+  return _lastDownloadUpdate;
+}
+
+const Time& PeerSessionResource::getLastAmUnchoking() const
+{
+  return _lastAmUnchoking;
 }
 
 } // namespace aria2

+ 9 - 0
src/PeerSessionResource.h

@@ -38,6 +38,7 @@
 #include "common.h"
 #include "BtConstants.h"
 #include "PeerStat.h"
+#include "TimeA2.h"
 #include <string>
 #include <deque>
 
@@ -76,6 +77,10 @@ private:
   uint64_t _uploadLength;
   uint64_t _downloadLength;
 
+  Time _lastDownloadUpdate;
+
+  Time _lastAmUnchoking;
+
   template<typename T>
   bool indexIncluded(const std::deque<T>& c, T index) const;
 public:
@@ -179,6 +184,10 @@ public:
   uint64_t downloadLength() const;
 
   void updateDownloadLength(size_t bytes);
+
+  const Time& getLastDownloadUpdate() const;
+
+  const Time& getLastAmUnchoking() const;
 };
 
 } // namespace aria2

+ 4 - 0
src/PeerStorage.h

@@ -89,6 +89,10 @@ public:
    * Tells PeerStorage object that peer is no longer used in the session.
    */
   virtual void returnPeer(const SharedHandle<Peer>& peer) = 0;
+
+  virtual bool chokeRoundIntervalElapsed() = 0;
+
+  virtual void executeChoke() = 0;
 };
 
 typedef SharedHandle<PeerStorage> PeerStorageHandle;

+ 5 - 0
test/MockBtMessageDispatcher.h

@@ -57,6 +57,11 @@ public:
   virtual void removeOutstandingRequest(const RequestSlot& slot) {}
   
   virtual void addOutstandingRequest(const RequestSlot& slot) {}
+
+  virtual size_t countOutstandingUpload()
+  {
+    return 0;
+  }
 };
 
 } // namespace aria2

+ 7 - 0
test/MockPeerStorage.h

@@ -56,6 +56,13 @@ public:
   virtual void returnPeer(const SharedHandle<Peer>& peer)
   {
   }
+
+  virtual bool chokeRoundIntervalElapsed()
+  {
+    return false;
+  }
+
+  virtual void executeChoke() {}
 };
 
 #endif // _D_MOCK_PEER_STORAGE_H_