memset( cp->completeBlocks, 0, sizeof( uint16_t ) * cp->tor->info.pieceCount );
cp->sizeNow = 0;
cp->sizeWhenDoneIsDirty = 1;
+ cp->blocksWantedIsDirty = 1;
cp->haveValidIsDirty = 1;
}
tr_cpInvalidateDND( tr_completion * cp )
{
cp->sizeWhenDoneIsDirty = 1;
+ cp->blocksWantedIsDirty = 1;
+}
+
+tr_block_index_t
+tr_cpBlocksMissing( const tr_completion * ccp )
+{
+ if( ccp->blocksWantedIsDirty )
+ {
+ tr_completion * cp = (tr_completion *) ccp; /* mutable */
+ const tr_torrent * tor = cp->tor;
+ const tr_info * info = &tor->info;
+ tr_piece_index_t i;
+ tr_block_index_t wanted = 0;
+ tr_block_index_t complete = 0;
+
+ for( i = 0; i < info->pieceCount; ++i )
+ {
+ if( info->pieces[i].dnd )
+ continue;
+
+ wanted += tr_torPieceCountBlocks( tor, i );
+ complete += cp->completeBlocks[i];
+ }
+
+ cp->blocksWantedLazy = wanted;
+ cp->blocksWantedCompleteLazy = complete;
+ cp->blocksWantedIsDirty = FALSE;
+ }
+
+ return ccp->blocksWantedLazy - ccp->blocksWantedCompleteLazy;
}
uint64_t
if( tr_cpBlockIsCompleteFast( cp, block ) )
cp->sizeNow -= tr_torBlockCountBytes( tor, block );
+ if( !tor->info.pieces[piece].dnd )
+ cp->blocksWantedCompleteLazy -= cp->completeBlocks[piece];
+
cp->sizeWhenDoneIsDirty = 1;
cp->haveValidIsDirty = 1;
cp->completeBlocks[piece] = 0;
tr_bitfieldAdd( &cp->blockBitfield, block );
cp->sizeNow += blockSize;
+ if( !tor->info.pieces[piece].dnd )
+ cp->blocksWantedCompleteLazy++;
cp->haveValidIsDirty = 1;
cp->sizeWhenDoneIsDirty = 1;
for( i=0; i<tor->info.pieceCount; ++i )
cp->completeBlocks[i] = tr_torPieceCountBlocks( tor, i );
cp->sizeWhenDoneIsDirty = 1;
+ cp->blocksWantedIsDirty = 1;
cp->haveValidIsDirty = 1;
}
/* invalidate the fields that are lazy-evaluated */
cp->sizeWhenDoneIsDirty = TRUE;
+ cp->blocksWantedIsDirty = TRUE;
cp->haveValidIsDirty = TRUE;
/* to set the remaining fields, we walk through every block... */
int interestedCount;
int maxPeers;
time_t lastCancel;
+
+ /* Before the endgame this should be 0. In endgame, is contains the average
+ * number of pending requests per peer. Only peers which have more pending
+ * requests are considered 'fast' are allowed to request a block that's
+ * already been requested from another (slower?) peer. */
+ int endgame;
}
Torrent;
compareReqByBlock );
}
-/* how many peers are we currently requesting this block from... */
-static int
-countBlockRequests( Torrent * t, tr_block_index_t block )
+/**
+ * Find the peers are we currently requesting the block
+ * with index @a block from and append them to @a peerArr.
+ */
+static void
+getBlockRequestPeers( Torrent * t, tr_block_index_t block,
+ tr_ptrArray * peerArr )
{
tr_bool exact;
- int i, n, pos;
+ int i, pos;
struct block_request key;
key.block = block;
assert( !exact ); /* shouldn't have a request with .peer == NULL */
- n = 0;
- for( i=pos; i<t->requestCount; ++i ) {
- if( t->requests[i].block == block )
- ++n;
- else
+ for( i = pos; i < t->requestCount; ++i )
+ {
+ if( t->requests[i].block != block )
break;
+ tr_ptrArrayAppend( peerArr, t->requests[i].peer );
}
-
- return n;
}
static void
qsort( t->pieces, t->pieceCount, sizeof( struct weighted_piece ), comparePieceByIndex );
}
-static tr_bool
-isInEndgame( Torrent * t )
+static int
+countActiveWebseeds( const Torrent * t )
{
- tr_bool endgame = FALSE;
+ int activeCount = 0;
+ const tr_webseed ** w = (const tr_webseed **) tr_ptrArrayBase( &t->webseeds );
+ const tr_webseed ** const wend = w + tr_ptrArraySize( &t->webseeds );
+
+ for( ; w!=wend; ++w )
+ if( tr_webseedIsActive( *w ) )
+ ++activeCount;
+
+ return activeCount;
+}
+
+static void
+updateEndgame( Torrent * t )
+{
+ const tr_torrent * tor = t->tor;
+ const tr_block_index_t missing = tr_cpBlocksMissing( &tor->completion );
+
+ assert( t->requestCount >= 0 );
- if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
+ if( (tr_block_index_t) t->requestCount < missing )
{
- const struct weighted_piece * p = t->pieces;
- const int pending = p->requestCount;
- const int missing = tr_cpMissingBlocksInPiece( &t->tor->completion, p->index );
- endgame = pending >= missing;
+ /* not in endgame */
+ t->endgame = 0;
}
+ else if( !t->endgame ) /* only recalculate when endgame first begins */
+ {
+ int numDownloading = 0;
+ const tr_peer ** p = (const tr_peer **) tr_ptrArrayBase( &t->peers );
+ const tr_peer ** const pend = p + tr_ptrArraySize( &t->peers );
+
+ /* add the active bittorrent peers... */
+ for( ; p!=pend; ++p )
+ if( (*p)->pendingReqsToPeer > 0 )
+ ++numDownloading;
+
+ /* add the active webseeds... */
+ numDownloading += countActiveWebseeds( t );
- /*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
- return endgame;
+ /* average number of pending requests per downloading peer */
+ t->endgame = t->requestCount / MAX( numDownloading, 1 );
+ }
}
/**
static void
assertWeightedPiecesAreSorted( Torrent * t )
{
- if( !isInEndgame( t ) )
+ if( !t->endgame )
{
int i;
weightTorrent = t->tor;
int i;
int got;
Torrent * t;
- tr_bool endgame;
struct weighted_piece * pieces;
const tr_bitset * have = &peer->have;
if( t->pieces == NULL )
pieceListRebuild( t );
- endgame = isInEndgame( t );
-
+ updateEndgame( t );
pieces = t->pieces;
for( i=0; i<t->pieceCount && got<numwant; ++i )
{
struct weighted_piece * p = pieces + i;
- const int missing = tr_cpMissingBlocksInPiece( &tor->completion, p->index );
- const int maxDuplicatesPerBlock = endgame ? 3 : 1;
-
- if( p->requestCount > ( missing * maxDuplicatesPerBlock ) )
- continue;
/* if the peer has this piece that we want... */
if( tr_bitsetHasFast( have, p->index ) )
{
tr_block_index_t b = tr_torPieceFirstBlock( tor, p->index );
const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, p->index );
+ tr_ptrArray peerArr = TR_PTR_ARRAY_INIT;
for( ; b!=e && got<numwant; ++b )
{
+ int peerCount;
+ tr_peer ** peers;
+
/* don't request blocks we've already got */
if( tr_cpBlockIsCompleteFast( &tor->completion, b ) )
continue;
- /* don't send the same request to the same peer twice */
- if( tr_peerMgrDidPeerRequest( tor, peer, b ) )
- continue;
-
- /* don't send the same request to any peer too many times */
- if( countBlockRequests( t, b ) >= maxDuplicatesPerBlock )
- continue;
+ /* always add peer if this block has no peers yet */
+ tr_ptrArrayClear( &peerArr );
+ getBlockRequestPeers( t, b, &peerArr );
+ peers = (tr_peer **) tr_ptrArrayPeek( &peerArr, &peerCount );
+ if( peerCount != 0 )
+ {
+ /* don't make a second block request until the endgame */
+ if( !t->endgame )
+ continue;
+
+ /* don't have more than two peers requesting this block */
+ if( peerCount > 1 )
+ continue;
+
+ /* don't send the same request to the same peer twice */
+ if( peer == peers[0] )
+ continue;
+
+ /* in the endgame allow an additional peer to download a
+ block but only if the peer seems to be handling requests
+ relatively fast */
+ if( peer->pendingReqsToPeer + numwant - got < t->endgame )
+ continue;
+ }
/* update the caller's table */
setme[got++] = b;
requestListAdd( t, b, peer );
++p->requestCount;
}
+
+ tr_ptrArrayDestruct( &peerArr, NULL );
}
}
{
tr_torrent * tor = t->tor;
tr_block_index_t block = _tr_block( tor, e->pieceIndex, e->offset );
+ int i, peerCount;
+ tr_peer ** peers;
+ tr_ptrArray peerArr = TR_PTR_ARRAY_INIT;
+
+ removeRequestFromTables( t, block, peer );
+ getBlockRequestPeers( t, block, &peerArr );
+ peers = (tr_peer **) tr_ptrArrayPeek( &peerArr, &peerCount );
+
+ /* remove additional block requests and send cancel to peers */
+ for( i=0; i<peerCount; i++ ) {
+ tr_peer * p = peers[i];
+ assert( p != peer );
+ if( p->msgs ) {
+ tr_historyAdd( p->cancelsSentToPeer, tr_time( ), 1 );
+ tr_peerMsgsCancel( p->msgs, block );
+ }
+ removeRequestFromTables( t, block, p );
+ }
- requestListRemove( t, block, peer );
- pieceListRemoveRequest( t, block );
+ tr_ptrArrayDestruct( &peerArr, FALSE );
if( peer && peer->blocksSentToClient )
tr_historyAdd( peer->blocksSentToClient, tr_time( ), 1 );
int i, size;
const Torrent * t = tor->torrentPeers;
const tr_peer ** peers;
- const tr_webseed ** webseeds;
assert( tr_torrentIsLocked( tor ) );
++*setmeSeedsConnected;
}
- webseeds = (const tr_webseed**) tr_ptrArrayBase( &t->webseeds );
- size = tr_ptrArraySize( &t->webseeds );
- for( i=0; i<size; ++i )
- if( tr_webseedIsActive( webseeds[i] ) )
- ++*setmeWebseedsSendingToUs;
+ *setmeWebseedsSendingToUs = countActiveWebseeds( t );
}
int