* improve the unchoking algorithm.

* when deciding which peers to connect to, take peer's previous speeds into account
* longer delay interval before reconnecting to peers that didn't give us data
* added uTorrent-inspired "flags" column in tr_peer_stat and gtk's torrent details' "peer" tab
This commit is contained in:
Charles Kerr 2008-01-09 17:33:43 +00:00
parent c4cba5ad2a
commit 591f7f8b10
6 changed files with 91 additions and 168 deletions

2
NEWS
View File

@ -12,7 +12,7 @@ NEWS file for Transmission <http://transmission.m0k.org/>
- OS X:
+ Leopard: Better behavior with Time Machine
- GTK+:
+ New Chinese, Dutch, and Turkish translations
+ New Brazilian Portuguese, Chinese, Dutch, and Turkish translations
+ Fix 1.00 desktop internationalization error
1.00 (2008/01/04)

View File

@ -290,7 +290,7 @@ peer_row_set (GtkTreeStore * store,
PEER_COL_DOWNLOAD_RATE, peer->downloadFromRate,
PEER_COL_IS_UPLOADING, peer->isUploading,
PEER_COL_UPLOAD_RATE, peer->uploadToRate,
PEER_COL_STATUS, peer->status,
PEER_COL_STATUS, peer->flagStr,
-1);
}
@ -311,16 +311,16 @@ static GtkTreeModel*
peer_model_new (tr_torrent * tor)
{
GtkTreeStore * m = gtk_tree_store_new (N_PEER_COLS,
G_TYPE_STRING, /* addr */
G_TYPE_INT, /* port */
G_TYPE_STRING, /* client */
G_TYPE_INT, /* progress [0..100] */
G_TYPE_BOOLEAN, /* isEncrypted */
G_TYPE_BOOLEAN, /* isDownloading */
G_TYPE_FLOAT, /* downloadFromRate */
G_TYPE_BOOLEAN, /* isUploading */
G_TYPE_FLOAT, /* uploadToRate */
G_TYPE_INT ); /* tr_peer_status */
G_TYPE_STRING, /* addr */
G_TYPE_INT, /* port */
G_TYPE_STRING, /* client */
G_TYPE_INT, /* progress [0..100] */
G_TYPE_BOOLEAN, /* isEncrypted */
G_TYPE_BOOLEAN, /* isDownloading */
G_TYPE_FLOAT, /* downloadFromRate */
G_TYPE_BOOLEAN, /* isUploading */
G_TYPE_FLOAT, /* uploadToRate */
G_TYPE_STRING ); /* flagString */
int n_peers = 0;
tr_peer_stat * peers = tr_torrentPeers (tor, &n_peers);
@ -346,46 +346,6 @@ render_encrypted (GtkTreeViewColumn * column UNUSED,
NULL);
}
static void
render_status( GtkTreeViewColumn * column UNUSED,
GtkCellRenderer * renderer,
GtkTreeModel * tree_model,
GtkTreeIter * iter,
gpointer data UNUSED )
{
GString * gstr = g_string_new( NULL );
int status;
gtk_tree_model_get( tree_model, iter, PEER_COL_STATUS, &status, -1 );
if( status & TR_PEER_STATUS_HANDSHAKE )
{
g_string_append( gstr, _("Handshaking") );
}
else
{
if( status & TR_PEER_STATUS_CLIENT_IS_SENDING )
g_string_append( gstr, _("Uploading to peer") );
else if( status & TR_PEER_STATUS_PEER_IS_INTERESTED )
g_string_append( gstr, _("Peer wants our data") );
else if( status & TR_PEER_STATUS_PEER_IS_CHOKED )
g_string_append( gstr, _("Refusing to send data to peer") );
g_string_append( gstr, " - " );
if( status & TR_PEER_STATUS_PEER_IS_SENDING )
g_string_append( gstr, _("Downloading from peer") );
else if( status & TR_PEER_STATUS_CLIENT_SENT_REQUEST )
g_string_append( gstr, _("Requesting data from peer") );
else if( status & TR_PEER_STATUS_CLIENT_IS_INTERESTED )
g_string_append( gstr, _("Waiting to request data from peer") );
else if( status & TR_PEER_STATUS_CLIENT_IS_CHOKED )
g_string_append( gstr, _("Peer will not send us data") );
}
g_object_set( renderer, "text", gstr->str, NULL );
g_string_free( gstr, TRUE );
}
static void
render_ul_rate (GtkTreeViewColumn * column UNUSED,
GtkCellRenderer * renderer,
@ -539,11 +499,8 @@ static GtkWidget* peer_page_new ( TrTorrent * gtor )
PEER_COL_PROGRESS,
PEER_COL_IS_ENCRYPTED,
PEER_COL_UPLOAD_RATE,
PEER_COL_DOWNLOAD_RATE
#if 0
, PEER_COL_STATUS
#endif
};
PEER_COL_DOWNLOAD_RATE,
PEER_COL_STATUS };
m = peer_model_new (tor);
v = gtk_tree_view_new_with_model (m);
@ -620,9 +577,7 @@ static GtkWidget* peer_page_new ( TrTorrent * gtor )
case PEER_COL_STATUS:
r = gtk_cell_renderer_text_new( );
c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
gtk_tree_view_column_set_cell_data_func (c, r, render_status, NULL, NULL);
break;
default:
abort ();

View File

@ -57,8 +57,6 @@ typedef struct tr_peer
unsigned int clientIsInterested : 1;
unsigned int doPurge : 1;
tr_peer_status status;
/* number of bad pieces they've contributed to */
uint8_t strikes;

View File

@ -72,7 +72,7 @@ static const int SWIFT_PERIOD_MSEC = 5000;
enum
{
/* how frequently to change which peers are choked */
RECHOKE_PERIOD_MSEC = (1000),
RECHOKE_PERIOD_MSEC = (10 * 1000),
/* how frequently to decide which peers live and die */
RECONNECT_PERIOD_MSEC = (2 * 1000),
@ -80,9 +80,6 @@ enum
/* how frequently to refill peers' request lists */
REFILL_PERIOD_MSEC = 666,
/* don't change a peer's choke status more often than this */
MIN_CHOKE_PERIOD_SEC = 10,
/* following the BT spec, we consider ourselves `snubbed' if
* we're we don't get piece data from a peer in this long */
SNUBBED_SEC = 60,
@ -1462,21 +1459,38 @@ tr_peerMgrPeerStats( const tr_peerMgr * manager,
for( i=0; i<size; ++i )
{
char * pch;
const tr_peer * peer = peers[i];
const struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
tr_peer_stat * stat = ret + i;
tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) );
strlcpy( stat->client, (peer->client ? peer->client : ""), sizeof(stat->client) );
stat->port = peer->port;
stat->from = atom->from;
stat->progress = peer->progress;
stat->isEncrypted = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
stat->uploadToRate = peer->rateToPeer;
stat->downloadFromRate = peer->rateToClient;
stat->isDownloading = stat->uploadToRate > 0.01;
stat->isUploading = stat->downloadFromRate > 0.01;
stat->status = peer->status;
stat->port = peer->port;
stat->from = atom->from;
stat->progress = peer->progress;
stat->isEncrypted = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
stat->uploadToRate = peer->rateToPeer;
stat->downloadFromRate = peer->rateToClient;
stat->peerIsChoked = peer->peerIsChoked;
stat->peerIsInterested = peer->peerIsInterested;
stat->clientIsChoked = peer->clientIsChoked;
stat->clientIsInterested = peer->clientIsInterested;
stat->isIncoming = tr_peerIoIsIncoming( peer->io );
stat->isDownloading = stat->clientIsInterested && !stat->clientIsChoked;
stat->isUploading = stat->peerIsInterested && !stat->peerIsChoked;
pch = stat->flagStr;
if( stat->isDownloading ) *pch++ = 'D';
else if( stat->clientIsInterested ) *pch++ = 'd';
if( stat->isUploading ) *pch++ = 'U';
else if( stat->peerIsInterested ) *pch++ = 'u';
if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ = 'K';
if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
if( stat->isEncrypted ) *pch++ = 'E';
if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
if( stat->isIncoming ) *pch++ = 'I';
*pch = '\0';
}
*setmeCount = size;
@ -1492,11 +1506,10 @@ tr_peerMgrPeerStats( const tr_peerMgr * manager,
struct ChokeData
{
tr_peer * peer;
int rate;
uint8_t preferred;
uint8_t preferred_t;
uint8_t doUnchoke;
uint8_t isInterested;
uint32_t rate;
tr_peer * peer;
};
static int
@ -1504,20 +1517,7 @@ compareChoke( const void * va, const void * vb )
{
const struct ChokeData * a = va;
const struct ChokeData * b = vb;
if( a->rate > b->rate ) return -1;
if( a->rate < b->rate ) return 1;
if( a->preferred_t != b->preferred_t ) return a->preferred_t ? -1 : 1;
if( a->preferred != b->preferred ) return a->preferred ? -1 : 1;
return 0;
}
static int
clientIsSnubbedBy( const tr_peer * peer, int clientIsSeed )
{
assert( peer != NULL );
return !clientIsSeed
&& ( peer->peerSentPieceDataAt < (time(NULL) - SNUBBED_SEC ) );
return -tr_compareUint32( a->rate, b->rate );
}
/**
@ -1525,7 +1525,7 @@ clientIsSnubbedBy( const tr_peer * peer, int clientIsSeed )
**/
static double
getWeightedThroughput( const tr_peer * peer, int clientIsSeed )
getWeightedRate( const tr_peer * peer, int clientIsSeed )
{
return (int)( 10.0 * ( clientIsSeed ? peer->rateToPeer
: peer->rateToClient ) );
@ -1534,8 +1534,7 @@ getWeightedThroughput( const tr_peer * peer, int clientIsSeed )
static void
rechoke( Torrent * t )
{
int i, peerCount, size=0, unchoked=0;
const time_t fibrillationTime = time(NULL) - MIN_CHOKE_PERIOD_SEC;
int i, n, peerCount, size, unchokedInterested;
tr_peer ** peers = getConnectedPeers( t, &peerCount );
struct ChokeData * choke = tr_new0( struct ChokeData, peerCount );
const int clientIsSeed = tr_torrentIsSeed( t->tor );
@ -1543,35 +1542,48 @@ rechoke( Torrent * t )
assert( torrentIsLocked( t ) );
/* sort the peers by preference and rate */
for( i=0; i<peerCount; ++i )
for( i=0, size=0; i<peerCount; ++i )
{
tr_peer * peer = peers[i];
struct ChokeData * node;
if( peer->chokeChangedAt > fibrillationTime ) {
if( !peer->peerIsChoked )
++unchoked;
continue;
if( peer->progress >= 1.0 ) /* choke all seeds */
tr_peerMsgsSetChoke( peer->msgs, TRUE );
else {
struct ChokeData * node = &choke[size++];
node->peer = peer;
node->isInterested = peer->peerIsInterested;
node->rate = getWeightedRate( peer, clientIsSeed );
}
node = &choke[size++];
node->peer = peer;
node->preferred = peer->peerIsInterested && !clientIsSnubbedBy( peer, clientIsSeed );
node->preferred_t = node->preferred && peer->client && strstr( peer->client, "Transmission" );
node->rate = getWeightedThroughput( peer, clientIsSeed );
}
qsort( choke, size, sizeof(struct ChokeData), compareChoke );
for( i=0; i<size && unchoked<t->tor->maxUnchokedPeers; ++i ) {
/**
* Reciprocation and number of uploads capping is managed by unchoking
* the N peers which have the best upload rate and are interested.
* This maximizes the client's download rate. These N peers are
* referred to as downloaders, because they are interested in downloading
* from the client.
*
* Peers which have a better upload rate (as compared to the downloaders)
* but aren't interested get unchoked. If they become interested, the
* downloader with the worst upload rate gets choked. If a client has
* a complete file, it uses its upload rate rather than its download
* rate to decide which peers to unchoke.
*/
unchokedInterested = 0;
for( i=0; i<size && unchokedInterested<t->tor->maxUnchokedPeers; ++i ) {
choke[i].doUnchoke = 1;
++unchoked;
if( choke[i].isInterested )
++unchokedInterested;
}
n = i;
while( i<size )
choke[i++].doUnchoke = 0;
for( ; i<size; ++i ) {
++unchoked;
/* optimistic unchoke */
if( i != size ) {
i = n + tr_rand( size - n );
choke[i].doUnchoke = 1;
if( choke[i].peer->peerIsInterested )
break;
}
for( i=0; i<size; ++i )
@ -1718,6 +1730,9 @@ compareCandidates( const void * va, const void * vb )
const struct peer_atom * b = * (const struct peer_atom**) vb;
int i;
if( a->piece_data_time > b->piece_data_time ) return -1;
if( a->piece_data_time < b->piece_data_time ) return 1;
if(( i = tr_compareUint16( a->numFails, b->numFails )))
return i;
@ -1764,16 +1779,16 @@ getPeerCandidates( Torrent * t, int * setmeSize )
continue;
/* we're wasting our time trying to connect to this bozo. */
if( atom->numFails > 10 )
if( atom->numFails > 5 )
continue;
/* If we were connected to this peer recently and transferring
* piece data, try to reconnect -- network troubles may have
* disconnected us. but if we weren't sharing piece data,
* hold off on this peer to give another one a try instead */
if( ( now - atom->piece_data_time ) > 15 )
if( ( now - atom->piece_data_time ) > 30 )
{
int minWait = 60;
int minWait = (60 * 2); /* two minutes */
int maxWait = (60 * 20); /* twenty minutes */
int wait = atom->numFails * 30; /* add 30 secs to the wait interval for each consecutive failure*/
if( wait < minWait ) wait = minWait;

View File

@ -1580,40 +1580,6 @@ popNextRequest( tr_peermsgs * msgs )
return ret;
}
static void
updatePeerStatus( tr_peermsgs * msgs )
{
const time_t now = time( NULL );
tr_peer * peer = msgs->info;
tr_peer_status status = 0;
if( !msgs->peerSentBitfield )
status |= TR_PEER_STATUS_HANDSHAKE;
if( msgs->info->peerIsChoked )
status |= TR_PEER_STATUS_PEER_IS_CHOKED;
if( msgs->info->peerIsInterested )
status |= TR_PEER_STATUS_PEER_IS_INTERESTED;
if( msgs->info->clientIsChoked )
status |= TR_PEER_STATUS_CLIENT_IS_CHOKED;
if( msgs->info->clientIsInterested )
status |= TR_PEER_STATUS_CLIENT_IS_INTERESTED;
if( ( now - msgs->clientSentPieceDataAt ) < 3 )
status |= TR_PEER_STATUS_CLIENT_IS_SENDING;
if( ( now - msgs->peerSentPieceDataAt ) < 3 )
status |= TR_PEER_STATUS_PEER_IS_SENDING;
if( msgs->clientAskedFor != NULL )
status |= TR_PEER_STATUS_CLIENT_SENT_REQUEST;
peer->status = status;
}
static int
pulse( void * vmsgs )
{
@ -1624,7 +1590,6 @@ pulse( void * vmsgs )
tr_peerIoTryRead( msgs->io );
pumpRequestQueue( msgs );
expireOldRequests( msgs );
updatePeerStatus( msgs );
if( msgs->sendingBlock )
{
@ -1884,7 +1849,6 @@ tr_peerMsgsNew( struct tr_torrent * torrent,
m->handle = torrent->handle;
m->torrent = torrent;
m->io = info->io;
m->info->status = TR_PEER_STATUS_HANDSHAKE;
m->info->clientIsChoked = 1;
m->info->peerIsChoked = 1;
m->info->clientIsInterested = 0;

View File

@ -777,21 +777,6 @@ struct tr_file_stat
float progress;
};
typedef enum
{
TR_PEER_STATUS_HANDSHAKE = (1<<0), /* we're handshaking with peer */
TR_PEER_STATUS_PEER_IS_SENDING = (1<<1), /* peer is sending data to us */
TR_PEER_STATUS_PEER_IS_INTERESTED = (1<<2), /* we have data the peer wants */
TR_PEER_STATUS_PEER_IS_CHOKED = (1<<3), /* we refuse to send data to the peer */
TR_PEER_STATUS_CLIENT_IS_SENDING = (1<<4), /* we're sending data to the peer */
TR_PEER_STATUS_CLIENT_SENT_REQUEST = (1<<5), /* we've sent the peer a request */
TR_PEER_STATUS_CLIENT_IS_INTERESTED = (1<<6), /* peer has data that we want */
TR_PEER_STATUS_CLIENT_IS_CHOKED = (1<<7), /* peer refuses to send data to us */
}
tr_peer_status;
struct tr_peer_stat
{
char addr[INET_ADDRSTRLEN];
@ -801,7 +786,13 @@ struct tr_peer_stat
unsigned int isDownloading : 1;
unsigned int isUploading : 1;
tr_peer_status status;
unsigned int peerIsChoked : 1;
unsigned int peerIsInterested : 1;
unsigned int clientIsChoked : 1;
unsigned int clientIsInterested : 1;
unsigned int isIncoming : 1;
char flagStr[32];
uint8_t from;
uint16_t port;