diff --git a/libtransmission/peer-common.h b/libtransmission/peer-common.h index 55e177613..c4ad31414 100644 --- a/libtransmission/peer-common.h +++ b/libtransmission/peer-common.h @@ -28,6 +28,8 @@ #include "transmission.h" +struct tr_bitfield; + enum { /** when we're making requests from another peer, @@ -64,6 +66,10 @@ typedef enum TR_PEER_CLIENT_GOT_SUGGEST, TR_PEER_CLIENT_GOT_PORT, TR_PEER_CLIENT_GOT_REJ, + TR_PEER_CLIENT_GOT_BITFIELD, + TR_PEER_CLIENT_GOT_HAVE, + TR_PEER_CLIENT_GOT_HAVE_ALL, + TR_PEER_CLIENT_GOT_HAVE_NONE, TR_PEER_PEER_GOT_DATA, TR_PEER_PEER_PROGRESS, TR_PEER_ERROR @@ -72,17 +78,21 @@ PeerEventType; typedef struct { - PeerEventType eventType; - uint32_t pieceIndex; /* for GOT_BLOCK, CANCEL, ALLOWED, SUGGEST */ - uint32_t offset; /* for GOT_BLOCK */ - uint32_t length; /* for GOT_BLOCK + GOT_DATA */ - float progress; /* for PEER_PROGRESS */ - int err; /* errno for GOT_ERROR */ - tr_bool wasPieceData; /* for GOT_DATA */ - tr_port port; /* for GOT_PORT */ + PeerEventType eventType; + + uint32_t pieceIndex; /* for GOT_BLOCK, GOT_HAVE, CANCEL, ALLOWED, SUGGEST */ + struct tr_bitfield * bitfield; /* for GOT_BITFIELD */ + uint32_t offset; /* for GOT_BLOCK */ + uint32_t length; /* for GOT_BLOCK + GOT_DATA */ + float progress; /* for PEER_PROGRESS */ + int err; /* errno for GOT_ERROR */ + tr_bool wasPieceData; /* for GOT_DATA */ + tr_port port; /* for GOT_PORT */ } tr_peer_event; +extern const tr_peer_event TR_PEER_EVENT_INIT; + struct tr_peer; typedef void tr_peer_callback( struct tr_peer * peer, diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index bf9ec429e..3f951acee 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -98,6 +98,7 @@ enum CANCEL_HISTORY_SEC = 60 }; +const tr_peer_event TR_PEER_EVENT_INIT = { 0, 0, NULL, 0, 0, 0.0f, 0, FALSE, 0 }; /** *** @@ -177,6 +178,13 @@ struct weighted_piece int16_t requestCount; }; +enum piece_sort_state +{ + PIECES_UNSORTED, + PIECES_SORTED_BY_INDEX, + PIECES_SORTED_BY_WEIGHT +}; + /** @brief Opaque, per-torrent data structure for peer connection information */ typedef struct tr_torrent_peers { @@ -200,6 +208,14 @@ typedef struct tr_torrent_peers struct weighted_piece * pieces; int pieceCount; + enum piece_sort_state pieceSortState; + + /* An array of pieceCount items stating how many peers have each piece. + This is used to help us for downloading pieces "rarest first." + This may be NULL if we don't have metainfo yet, or if we're not + downloading and don't care about rarity */ + uint16_t * pieceReplication; + size_t pieceReplicationSize; int interestedCount; int maxPeers; @@ -424,27 +440,44 @@ peerDestructor( Torrent * t, tr_peer * peer ) tr_free( peer ); } -static void -removePeer( Torrent * t, tr_peer * peer ) +static tr_bool +replicationExists( const Torrent * t ) { - tr_peer * removed; - struct peer_atom * atom = peer->atom; - - assert( torrentIsLocked( t ) ); - assert( atom ); - - atom->time = tr_time( ); - - removed = tr_ptrArrayRemoveSorted( &t->peers, peer, peerCompare ); - assert( removed == peer ); - peerDestructor( t, removed ); + return t->pieceReplication != NULL; } static void -removeAllPeers( Torrent * t ) +replicationFree( Torrent * t ) { - while( !tr_ptrArrayEmpty( &t->peers ) ) - removePeer( t, tr_ptrArrayNth( &t->peers, 0 ) ); + tr_free( t->pieceReplication ); + t->pieceReplication = NULL; + t->pieceReplicationSize = 0; +} + +static void +replicationNew( Torrent * t ) +{ + tr_piece_index_t piece_i; + const tr_piece_index_t piece_count = t->tor->info.pieceCount; + tr_peer ** peers = (tr_peer**) tr_ptrArrayBase( &t->peers ); + const int peer_count = tr_ptrArraySize( &t->peers ); + + assert( !replicationExists( t ) ); + + t->pieceReplicationSize = piece_count; + t->pieceReplication = tr_new0( uint16_t, piece_count ); + + for( piece_i=0; piece_ihave, piece_i ) ) + ++r; + + t->pieceReplication[piece_i] = r; + } } static void @@ -463,6 +496,8 @@ torrentDestructor( void * vt ) tr_ptrArrayDestruct( &t->outgoingHandshakes, NULL ); tr_ptrArrayDestruct( &t->peers, NULL ); + replicationFree( t ); + tr_free( t->requests ); tr_free( t->pieces ); tr_free( t ); @@ -784,77 +819,6 @@ requestListRemove( Torrent * t, tr_block_index_t block, const tr_peer * peer ) } } -/** -*** struct weighted_piece -**/ - -enum -{ - PIECES_UNSORTED, - PIECES_SORTED_BY_INDEX, - PIECES_SORTED_BY_WEIGHT -}; - -const tr_torrent * weightTorrent; - -/* we try to create a "weight" s.t. high-priority pieces come before others, - * and that partially-complete pieces come before empty ones. */ -static int -comparePieceByWeight( const void * va, const void * vb ) -{ - const struct weighted_piece * a = va; - const struct weighted_piece * b = vb; - int ia, ib, missing, pending; - const tr_torrent * tor = weightTorrent; - - /* primary key: weight */ - missing = tr_cpMissingBlocksInPiece( &tor->completion, a->index ); - pending = a->requestCount; - ia = missing > pending ? missing - pending : (tor->blockCountInPiece + pending); - missing = tr_cpMissingBlocksInPiece( &tor->completion, b->index ); - pending = b->requestCount; - ib = missing > pending ? missing - pending : (tor->blockCountInPiece + pending); - if( ia < ib ) return -1; - if( ia > ib ) return 1; - - /* secondary key: higher priorities go first */ - ia = tor->info.pieces[a->index].priority; - ib = tor->info.pieces[b->index].priority; - if( ia > ib ) return -1; - if( ia < ib ) return 1; - - /* tertiary key: random */ - if( a->salt < b->salt ) return -1; - if( a->salt > b->salt ) return 1; - - /* okay, they're equal */ - return 0; -} - -static int -comparePieceByIndex( const void * va, const void * vb ) -{ - const struct weighted_piece * a = va; - const struct weighted_piece * b = vb; - if( a->index < b->index ) return -1; - if( a->index > b->index ) return 1; - return 0; -} - -static void -pieceListSort( Torrent * t, int mode ) -{ - assert( mode==PIECES_SORTED_BY_INDEX - || mode==PIECES_SORTED_BY_WEIGHT ); - - weightTorrent = t->tor; - - if( mode == PIECES_SORTED_BY_WEIGHT ) - qsort( t->pieces, t->pieceCount, sizeof( struct weighted_piece ), comparePieceByWeight ); - else - qsort( t->pieces, t->pieceCount, sizeof( struct weighted_piece ), comparePieceByIndex ); -} - static int countActiveWebseeds( const Torrent * t ) { @@ -901,25 +865,149 @@ updateEndgame( Torrent * t ) } } + +/**** +***** +***** Piece List Manipulation / Accessors +***** +****/ + +static inline void +invalidatePieceSorting( Torrent * t ) +{ + t->pieceSortState = PIECES_UNSORTED; +} + +const tr_torrent * weightTorrent; + +const uint16_t * weightReplication; + +static void +setComparePieceByWeightTorrent( Torrent * t ) +{ + if( !replicationExists( t ) ) + replicationNew( t ); + + weightTorrent = t->tor; + weightReplication = t->pieceReplication; +} + +/* we try to create a "weight" s.t. high-priority pieces come before others, + * and that partially-complete pieces come before empty ones. */ +static int +comparePieceByWeight( const void * va, const void * vb ) +{ + const struct weighted_piece * a = va; + const struct weighted_piece * b = vb; + int ia, ib, missing, pending; + const tr_torrent * tor = weightTorrent; + const uint16_t * rep = weightReplication; + + /* primary key: weight */ + missing = tr_cpMissingBlocksInPiece( &tor->completion, a->index ); + pending = a->requestCount; + ia = missing > pending ? missing - pending : (tor->blockCountInPiece + pending); + missing = tr_cpMissingBlocksInPiece( &tor->completion, b->index ); + pending = b->requestCount; + ib = missing > pending ? missing - pending : (tor->blockCountInPiece + pending); + if( ia < ib ) return -1; + if( ia > ib ) return 1; + + /* secondary key: higher priorities go first */ + ia = tor->info.pieces[a->index].priority; + ib = tor->info.pieces[b->index].priority; + if( ia > ib ) return -1; + if( ia < ib ) return 1; + + /* tertiary key: rarest first. */ + ia = rep[a->index]; + ib = rep[b->index]; + if( ia < ib ) return -1; + if( ia > ib ) return 1; + + /* quaternary key: random */ + if( a->salt < b->salt ) return -1; + if( a->salt > b->salt ) return 1; + + /* okay, they're equal */ + return 0; +} + +static int +comparePieceByIndex( const void * va, const void * vb ) +{ + const struct weighted_piece * a = va; + const struct weighted_piece * b = vb; + if( a->index < b->index ) return -1; + if( a->index > b->index ) return 1; + return 0; +} + +static void +pieceListSort( Torrent * t, enum piece_sort_state state ) +{ + assert( state==PIECES_SORTED_BY_INDEX + || state==PIECES_SORTED_BY_WEIGHT ); + + + if( state == PIECES_SORTED_BY_WEIGHT ) + { + setComparePieceByWeightTorrent( t ); + qsort( t->pieces, t->pieceCount, sizeof( struct weighted_piece ), comparePieceByWeight ); + } + else + qsort( t->pieces, t->pieceCount, sizeof( struct weighted_piece ), comparePieceByIndex ); + + t->pieceSortState = state; +} + /** - * This function is useful for sanity checking, - * but is too expensive even for nightly builds... + * These functions are useful for testing, but too expensive for nightly builds. * let's leave it disabled but add an easy hook to compile it back in */ #if 0 +#define assertWeightedPiecesAreSorted(t) +#define assertReplicationCountIsExact(t) +#else static void assertWeightedPiecesAreSorted( Torrent * t ) { if( !t->endgame ) { int i; - weightTorrent = t->tor; + setComparePieceByWeightTorrent( t ); for( i=0; ipieceCount-1; ++i ) assert( comparePieceByWeight( &t->pieces[i], &t->pieces[i+1] ) <= 0 ); } } -#else -#define assertWeightedPiecesAreSorted(t) +static void +assertReplicationCountIsExact( Torrent * t ) +{ + /* This assert might fail due to errors of implementations in other + * clients. It happens when receiving duplicate bitfields/HaveAll/HaveNone + * from a client. If a such a behavior is noticed, + * a bug report should be filled to the faulty client. */ + + size_t piece_i; + const uint16_t * rep = t->pieceReplication; + const size_t piece_count = t->pieceReplicationSize; + const tr_peer ** peers = (const tr_peer**) tr_ptrArrayBase( &t->peers ); + const int peer_count = tr_ptrArraySize( &t->peers ); + + assert( piece_count == t->tor->info.pieceCount ); + + for( piece_i=0; piece_ihave, piece_i ) ) + ++r; + + assert( rep[piece_i] == r ); + } +} #endif static struct weighted_piece * @@ -937,7 +1025,6 @@ pieceListLookup( Torrent * t, tr_piece_index_t index ) static void pieceListRebuild( Torrent * t ) { - assertWeightedPiecesAreSorted( t ); if( !tr_torrentIsSeed( t->tor ) ) { @@ -1002,8 +1089,6 @@ pieceListRemovePiece( Torrent * t, tr_piece_index_t piece ) { struct weighted_piece * p; - assertWeightedPiecesAreSorted( t ); - if(( p = pieceListLookup( t, piece ))) { const int pos = p - t->pieces; @@ -1019,8 +1104,6 @@ pieceListRemovePiece( Torrent * t, tr_piece_index_t piece ) t->pieces = NULL; } } - - assertWeightedPiecesAreSorted( t ); } static void @@ -1034,12 +1117,18 @@ pieceListResortPiece( Torrent * t, struct weighted_piece * p ) /* is the torrent already sorted? */ pos = p - t->pieces; - weightTorrent = t->tor; + setComparePieceByWeightTorrent( t ); if( isSorted && ( pos > 0 ) && ( comparePieceByWeight( p-1, p ) > 0 ) ) isSorted = FALSE; if( isSorted && ( pos < t->pieceCount - 1 ) && ( comparePieceByWeight( p, p+1 ) > 0 ) ) isSorted = FALSE; + if( t->pieceSortState != PIECES_SORTED_BY_WEIGHT ) + { + pieceListSort( t, PIECES_SORTED_BY_WEIGHT); + isSorted = TRUE; + } + /* if it's not sorted, move it around */ if( !isSorted ) { @@ -1071,15 +1160,106 @@ pieceListRemoveRequest( Torrent * t, tr_block_index_t block ) struct weighted_piece * p; const tr_piece_index_t index = tr_torBlockPiece( t->tor, block ); - assertWeightedPiecesAreSorted( t ); - if( ((p = pieceListLookup( t, index ))) && ( p->requestCount > 0 ) ) { --p->requestCount; pieceListResortPiece( t, p ); } +} - assertWeightedPiecesAreSorted( t ); + +/**** +***** +***** Replication count ( for rarest first policy ) +***** +****/ + +/** + * Increase the replication count of this piece and sort it if the + * piece list is already sorted + */ +static void +tr_incrReplicationOfPiece( Torrent * t, const size_t index ) +{ + assert( replicationExists( t ) ); + assert( t->pieceReplicationSize == t->tor->info.pieceCount ); + + /* One more replication of this piece is present in the swarm */ + ++t->pieceReplication[index]; + + /* we only resort the piece if the list is already sorted */ + if( t->pieceSortState == PIECES_SORTED_BY_WEIGHT ) + pieceListResortPiece( t, pieceListLookup( t, index ) ); +} + +/** + * Increases the replication count of pieces present in the bitfield + */ +static void +tr_incrReplicationFromBitfield( Torrent * t, const tr_bitfield * b ) +{ + size_t i; + uint16_t * rep = t->pieceReplication; + const size_t n = t->tor->info.pieceCount; + + assert( replicationExists( t ) ); + assert( n == t->pieceReplicationSize ); + assert( tr_bitfieldTestFast( b, n-1 ) ); + + if( tr_bitfieldTestFast( b, n-1 ) ) + for( i=0; ipieceSortState == PIECES_SORTED_BY_WEIGHT ) + invalidatePieceSorting( t ); +} + +/** + * Increase the replication count of every piece + */ +static void +tr_incrReplication( Torrent * t ) +{ + int i; + const int n = t->pieceReplicationSize; + + assert( replicationExists( t ) ); + assert( t->pieceReplicationSize == t->tor->info.pieceCount ); + + for( i=0; ipieceReplication[i]; +} + +/** + * Decrease the replication count of pieces present in the bitset. + */ +static void +tr_decrReplicationFromBitset( Torrent * t, const tr_bitset * bitset ) +{ + int i; + const int n = t->pieceReplicationSize; + + assert( replicationExists( t ) ); + assert( t->pieceReplicationSize == t->tor->info.pieceCount ); + + if( bitset->haveAll ) + { + for( i=0; ipieceReplication[i]; + } + else if ( !bitset->haveNone ) + { + const tr_bitfield * const b = &bitset->bitfield; + + if( tr_bitfieldTestFast( b, n-1 ) ) + for( i=0; ipieceReplication[i]; + + if( t->pieceSortState == PIECES_SORTED_BY_WEIGHT ) + invalidatePieceSorting( t ); + } } /** @@ -1116,12 +1296,17 @@ tr_peerMgrGetNextRequests( tr_torrent * tor, /* walk through the pieces and find blocks that should be requested */ got = 0; t = tor->torrentPeers; - assertWeightedPiecesAreSorted( t ); /* prep the pieces list */ if( t->pieces == NULL ) pieceListRebuild( t ); + if( t->pieceSortState != PIECES_SORTED_BY_WEIGHT ) + pieceListSort( t, PIECES_SORTED_BY_WEIGHT ); + + assertReplicationCountIsExact( t ); + assertWeightedPiecesAreSorted( t ); + updateEndgame( t ); pieces = t->pieces; for( i=0; ipieceCount && gotpieceCount ) --i; - weightTorrent = t->tor; + setComparePieceByWeightTorrent( t ); while( --i >= 0 ) { tr_bool exact; @@ -1419,6 +1604,32 @@ peerCallbackFunc( tr_peer * peer, const tr_peer_event * e, void * vt ) break; } + case TR_PEER_CLIENT_GOT_HAVE: + if( replicationExists( t ) ) { + tr_incrReplicationOfPiece( t, e->pieceIndex ); + assertReplicationCountIsExact( t ); + } + break; + + case TR_PEER_CLIENT_GOT_HAVE_ALL: + if( replicationExists( t ) ) { + tr_incrReplication( t ); + assertReplicationCountIsExact( t ); + } + break; + + case TR_PEER_CLIENT_GOT_HAVE_NONE: + /* noop */ + break; + + case TR_PEER_CLIENT_GOT_BITFIELD: + assert( e->bitfield != NULL ); + if( replicationExists( t ) ) { + tr_incrReplicationFromBitfield( t, e->bitfield ); + assertReplicationCountIsExact( t ); + } + break; + case TR_PEER_CLIENT_GOT_REJ: removeRequestFromTables( t, _tr_block( t->tor, e->pieceIndex, e->offset ), peer ); break; @@ -2141,6 +2352,7 @@ tr_peerMgrStartTorrent( tr_torrent * tor ) t->isRunning = TRUE; t->maxPeers = t->tor->maxConnectedPeers; + t->pieceSortState = PIECES_UNSORTED; rechokePulse( 0, 0, t->manager ); } @@ -2152,6 +2364,9 @@ stopTorrent( Torrent * t ) t->isRunning = FALSE; + replicationFree( t ); + invalidatePieceSorting( t ); + /* disconnect the peers. */ for( i=0, n=tr_ptrArraySize( &t->peers ); ipeers, i ) ); @@ -2971,6 +3186,26 @@ getReconnectIntervalSecs( const struct peer_atom * atom, const time_t now ) return sec; } +static void +removePeer( Torrent * t, tr_peer * peer ) +{ + tr_peer * removed; + struct peer_atom * atom = peer->atom; + + assert( torrentIsLocked( t ) ); + assert( atom ); + + atom->time = tr_time( ); + + removed = tr_ptrArrayRemoveSorted( &t->peers, peer, peerCompare ); + + if( replicationExists( t ) ) + tr_decrReplicationFromBitset( t, &peer->have ); + + assert( removed == peer ); + peerDestructor( t, removed ); +} + static void closePeer( Torrent * t, tr_peer * peer ) { @@ -2996,6 +3231,13 @@ closePeer( Torrent * t, tr_peer * peer ) removePeer( t, peer ); } +static void +removeAllPeers( Torrent * t ) +{ + while( !tr_ptrArrayEmpty( &t->peers ) ) + removePeer( t, tr_ptrArrayNth( &t->peers, 0 ) ); +} + static void closeBadPeers( Torrent * t, const uint64_t now_msec, const time_t now_sec ) { diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index 70d4c5a04..d1e741ced 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -171,6 +171,7 @@ struct tr_incoming */ struct tr_peermsgs { + tr_bool got_a_bitfield_or_have_all_or_have_none; tr_bool peerSupportsPex; tr_bool peerSupportsMetadataXfer; tr_bool clientSentLtepHandshake; @@ -451,8 +452,6 @@ protocolSendHaveNone( tr_peermsgs * msgs ) *** EVENTS **/ -static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0, 0, 0 }; - static void publish( tr_peermsgs * msgs, tr_peer_event * e ) { @@ -466,7 +465,7 @@ publish( tr_peermsgs * msgs, tr_peer_event * e ) static void fireError( tr_peermsgs * msgs, int err ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_ERROR; e.err = err; publish( msgs, &e ); @@ -475,7 +474,7 @@ fireError( tr_peermsgs * msgs, int err ) static void firePeerProgress( tr_peermsgs * msgs ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_PEER_PROGRESS; e.progress = msgs->peer->progress; publish( msgs, &e ); @@ -484,7 +483,7 @@ firePeerProgress( tr_peermsgs * msgs ) static void fireGotBlock( tr_peermsgs * msgs, const struct peer_request * req ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_BLOCK; e.pieceIndex = req->index; e.offset = req->offset; @@ -495,7 +494,7 @@ fireGotBlock( tr_peermsgs * msgs, const struct peer_request * req ) static void fireGotRej( tr_peermsgs * msgs, const struct peer_request * req ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_REJ; e.pieceIndex = req->index; e.offset = req->offset; @@ -506,17 +505,33 @@ fireGotRej( tr_peermsgs * msgs, const struct peer_request * req ) static void fireGotChoke( tr_peermsgs * msgs ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_CHOKE; publish( msgs, &e ); } +static void +fireClientGotHaveAll( tr_peermsgs * msgs ) +{ + tr_peer_event e = TR_PEER_EVENT_INIT; + e.eventType = TR_PEER_CLIENT_GOT_HAVE_ALL; + publish( msgs, &e ); +} + +static void +fireClientGotHaveNone( tr_peermsgs * msgs ) +{ + tr_peer_event e = TR_PEER_EVENT_INIT; + e.eventType = TR_PEER_CLIENT_GOT_HAVE_NONE; + publish( msgs, &e ); +} + static void fireClientGotData( tr_peermsgs * msgs, uint32_t length, int wasPieceData ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.length = length; e.eventType = TR_PEER_CLIENT_GOT_DATA; @@ -527,7 +542,7 @@ fireClientGotData( tr_peermsgs * msgs, static void fireClientGotSuggest( tr_peermsgs * msgs, uint32_t pieceIndex ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_SUGGEST; e.pieceIndex = pieceIndex; publish( msgs, &e ); @@ -536,7 +551,7 @@ fireClientGotSuggest( tr_peermsgs * msgs, uint32_t pieceIndex ) static void fireClientGotPort( tr_peermsgs * msgs, tr_port port ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_PORT; e.port = port; publish( msgs, &e ); @@ -545,18 +560,36 @@ fireClientGotPort( tr_peermsgs * msgs, tr_port port ) static void fireClientGotAllowedFast( tr_peermsgs * msgs, uint32_t pieceIndex ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_ALLOWED_FAST; e.pieceIndex = pieceIndex; publish( msgs, &e ); } +static void +fireClientGotBitfield( tr_peermsgs * msgs, tr_bitfield * bitfield ) +{ + tr_peer_event e = TR_PEER_EVENT_INIT; + e.eventType = TR_PEER_CLIENT_GOT_BITFIELD; + e.bitfield = bitfield; + publish( msgs, &e ); +} + +static void +fireClientGotHave( tr_peermsgs * msgs, tr_piece_index_t index ) +{ + tr_peer_event e = TR_PEER_EVENT_INIT; + e.eventType = TR_PEER_CLIENT_GOT_HAVE; + e.pieceIndex = index; + publish( msgs, &e ); +} + static void firePeerGotData( tr_peermsgs * msgs, uint32_t length, int wasPieceData ) { - tr_peer_event e = blankEvent; + tr_peer_event e = TR_PEER_EVENT_INIT; e.length = length; e.eventType = TR_PEER_PEER_GOT_DATA; @@ -1410,11 +1443,11 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) fireError( msgs, ERANGE ); return READ_ERR; } - if( tr_bitsetAdd( &msgs->peer->have, ui32 ) ) - { - fireError( msgs, ERANGE ); - return READ_ERR; - } + + /* a peer can send the same HAVE message twice... */ + if( !tr_bitsetHas( &msgs->peer->have, ui32 ) ) + if( !tr_bitsetAdd( &msgs->peer->have, ui32 ) ) + fireClientGotHave( msgs, ui32 ); updatePeerProgress( msgs ); break; @@ -1422,10 +1455,13 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) const size_t bitCount = tr_torrentHasMetadata( msgs->torrent ) ? msgs->torrent->info.pieceCount : msglen * 8; +assert( !msgs->got_a_bitfield_or_have_all_or_have_none ); +msgs->got_a_bitfield_or_have_all_or_have_none = TRUE; dbgmsg( msgs, "got a bitfield" ); tr_bitsetReserve( &msgs->peer->have, bitCount ); tr_peerIoReadBytes( msgs->peer->io, inbuf, msgs->peer->have.bitfield.bits, msglen ); + fireClientGotBitfield( msgs, &msgs->peer->have.bitfield ); updatePeerProgress( msgs ); break; } @@ -1501,7 +1537,10 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) case BT_FEXT_HAVE_ALL: dbgmsg( msgs, "Got a BT_FEXT_HAVE_ALL" ); if( fext ) { +assert( !msgs->got_a_bitfield_or_have_all_or_have_none ); +msgs->got_a_bitfield_or_have_all_or_have_none = TRUE; tr_bitsetSetHaveAll( &msgs->peer->have ); + fireClientGotHaveAll( msgs ); updatePeerProgress( msgs ); } else { fireError( msgs, EMSGSIZE ); @@ -1512,7 +1551,10 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) case BT_FEXT_HAVE_NONE: dbgmsg( msgs, "Got a BT_FEXT_HAVE_NONE" ); if( fext ) { +assert( !msgs->got_a_bitfield_or_have_all_or_have_none ); +msgs->got_a_bitfield_or_have_all_or_have_none = TRUE; tr_bitsetSetHaveNone( &msgs->peer->have ); + fireClientGotHaveNone( msgs ); updatePeerProgress( msgs ); } else { fireError( msgs, EMSGSIZE ); diff --git a/libtransmission/webseed.c b/libtransmission/webseed.c index 8a3c5164d..2396aebbe 100644 --- a/libtransmission/webseed.c +++ b/libtransmission/webseed.c @@ -77,8 +77,6 @@ webseed_free( struct tr_webseed * w ) **** ***/ -static const tr_peer_event blank_event = { 0, 0, 0, 0, 0.0f, 0, 0, 0 }; - static void publish( tr_webseed * w, tr_peer_event * e ) { @@ -89,7 +87,7 @@ publish( tr_webseed * w, tr_peer_event * e ) static void fire_client_got_rej( tr_torrent * tor, tr_webseed * w, tr_block_index_t block ) { - tr_peer_event e = blank_event; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_REJ; e.pieceIndex = tr_torBlockPiece( tor, block ); e.offset = tor->blockSize * block - tor->info.pieceSize * e.pieceIndex; @@ -100,7 +98,7 @@ fire_client_got_rej( tr_torrent * tor, tr_webseed * w, tr_block_index_t block ) static void fire_client_got_block( tr_torrent * tor, tr_webseed * w, tr_block_index_t block ) { - tr_peer_event e = blank_event; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_BLOCK; e.pieceIndex = tr_torBlockPiece( tor, block ); e.offset = tor->blockSize * block - tor->info.pieceSize * e.pieceIndex; @@ -111,7 +109,7 @@ fire_client_got_block( tr_torrent * tor, tr_webseed * w, tr_block_index_t block static void fire_client_got_data( tr_webseed * w, uint32_t length ) { - tr_peer_event e = blank_event; + tr_peer_event e = TR_PEER_EVENT_INIT; e.eventType = TR_PEER_CLIENT_GOT_DATA; e.length = length; e.wasPieceData = TRUE;