source: trunk/libtransmission/peer-mgr.c @ 7231

Last change on this file since 7231 was 7231, checked in by charles, 12 years ago

(libT) re-apply jhujhiti's IPv6 patch. This merges in my tr_port cleanup, so any new bugs are mine :/

  • Property svn:keywords set to Date Rev Author Id
File size: 62.9 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: peer-mgr.c 7231 2008-12-02 03:41:58Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <string.h> /* memcpy, memcmp, strstr */
16#include <stdlib.h> /* qsort */
17#include <limits.h> /* INT_MAX */
18
19#include <event.h>
20
21#include "transmission.h"
22#include "bandwidth.h"
23#include "bencode.h"
24#include "blocklist.h"
25#include "clients.h"
26#include "completion.h"
27#include "crypto.h"
28#include "handshake.h"
29#include "inout.h" /* tr_ioTestPiece */
30#include "net.h"
31#include "peer-io.h"
32#include "peer-mgr.h"
33#include "peer-mgr-private.h"
34#include "peer-msgs.h"
35#include "ptrarray.h"
36#include "stats.h" /* tr_statsAddUploaded, tr_statsAddDownloaded */
37#include "torrent.h"
38#include "trevent.h"
39#include "utils.h"
40#include "webseed.h"
41
42enum
43{
44    /* how frequently to change which peers are choked */
45    RECHOKE_PERIOD_MSEC = ( 10 * 1000 ),
46
47    /* minimum interval for refilling peers' request lists */
48    REFILL_PERIOD_MSEC = 333,
49
50    /* when many peers are available, keep idle ones this long */
51    MIN_UPLOAD_IDLE_SECS = ( 60 * 3 ),
52
53    /* when few peers are available, keep idle ones this long */
54    MAX_UPLOAD_IDLE_SECS = ( 60 * 10 ),
55
56    /* how frequently to decide which peers live and die */
57    RECONNECT_PERIOD_MSEC = ( 2 * 1000 ),
58   
59    /* how frequently to reallocate bandwidth */
60    BANDWIDTH_PERIOD_MSEC = 250,
61
62    /* max # of peers to ask fer per torrent per reconnect pulse */
63    MAX_RECONNECTIONS_PER_PULSE = 4,
64
65    /* max number of peers to ask for per second overall.
66    * this throttle is to avoid overloading the router */
67    MAX_CONNECTIONS_PER_SECOND = 8,
68
69    /* number of unchoked peers per torrent.
70     * FIXME: this probably ought to be configurable */
71    MAX_UNCHOKED_PEERS = 14,
72
73    /* number of bad pieces a peer is allowed to send before we ban them */
74    MAX_BAD_PIECES_PER_PEER = 5,
75
76    /* use for bitwise operations w/peer_atom.myflags */
77    MYFLAG_BANNED = 1,
78
79    /* unreachable for now... but not banned.
80     * if they try to connect to us it's okay */
81    MYFLAG_UNREACHABLE = 2,
82
83    /* the minimum we'll wait before attempting to reconnect to a peer */
84    MINIMUM_RECONNECT_INTERVAL_SECS = 5
85};
86
87
88/**
89***
90**/
91
92/* We keep one of these for every peer we know about, whether
93 * it's connected or not, so the struct must be small.
94 * When our current connections underperform, we dip back
95 * into this list for new ones. */
96struct peer_atom
97{
98    uint8_t     from;
99    uint8_t     flags; /* these match the added_f flags */
100    uint8_t     myflags; /* flags that aren't defined in added_f */
101    tr_port     port;
102    uint16_t    numFails;
103    tr_address  addr;
104    time_t      time; /* when the peer's connection status last changed */
105    time_t      piece_data_time;
106};
107
108typedef struct
109{
110    tr_bool         isRunning;
111
112    uint8_t         hash[SHA_DIGEST_LENGTH];
113    int         *   pendingRequestCount;
114    tr_ptrArray *   outgoingHandshakes; /* tr_handshake */
115    tr_ptrArray *   pool; /* struct peer_atom */
116    tr_ptrArray *   peers; /* tr_peer */
117    tr_ptrArray *   webseeds; /* tr_webseed */
118    tr_timer *      reconnectTimer;
119    tr_timer *      rechokeTimer;
120    tr_timer *      refillTimer;
121    tr_torrent *    tor;
122    tr_peer *       optimistic; /* the optimistic peer, or NULL if none */
123
124    struct tr_peerMgr * manager;
125}
126Torrent;
127
128struct tr_peerMgr
129{
130    tr_session      * session;
131    tr_ptrArray     * torrents; /* Torrent */
132    tr_ptrArray     * incomingHandshakes; /* tr_handshake */
133    tr_timer        * bandwidthTimer;
134};
135
136#define tordbg( t, ... ) \
137    do { \
138        if( tr_deepLoggingIsActive( ) ) \
139            tr_deepLog( __FILE__, __LINE__, t->tor->info.name, __VA_ARGS__ ); \
140    } while( 0 )
141
142#define dbgmsg( ... ) \
143    do { \
144        if( tr_deepLoggingIsActive( ) ) \
145            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
146    } while( 0 )
147
148/**
149***
150**/
151
152static void
153managerLock( const struct tr_peerMgr * manager )
154{
155    tr_globalLock( manager->session );
156}
157
158static void
159managerUnlock( const struct tr_peerMgr * manager )
160{
161    tr_globalUnlock( manager->session );
162}
163
164static void
165torrentLock( Torrent * torrent )
166{
167    managerLock( torrent->manager );
168}
169
170static void
171torrentUnlock( Torrent * torrent )
172{
173    managerUnlock( torrent->manager );
174}
175
176static int
177torrentIsLocked( const Torrent * t )
178{
179    return tr_globalIsLocked( t->manager->session );
180}
181
182/**
183***
184**/
185
186static int
187handshakeCompareToAddr( const void * va,
188                        const void * vb )
189{
190    const tr_handshake * a = va;
191
192    return tr_compareAddresses( tr_handshakeGetAddr( a, NULL ), vb );
193}
194
195static int
196handshakeCompare( const void * a,
197                  const void * b )
198{
199    return handshakeCompareToAddr( a, tr_handshakeGetAddr( b, NULL ) );
200}
201
202static tr_handshake*
203getExistingHandshake( tr_ptrArray      * handshakes,
204                      const tr_address * addr )
205{
206    return tr_ptrArrayFindSorted( handshakes, addr, handshakeCompareToAddr );
207}
208
209static int
210comparePeerAtomToAddress( const void * va, const void * vb )
211{
212    const struct peer_atom * a = va;
213
214    return tr_compareAddresses( &a->addr, vb );
215}
216
217static int
218comparePeerAtoms( const void * va, const void * vb )
219{
220    const struct peer_atom * b = vb;
221
222    return comparePeerAtomToAddress( va, &b->addr );
223}
224
225/**
226***
227**/
228
229static int
230torrentCompare( const void * va,
231                const void * vb )
232{
233    const Torrent * a = va;
234    const Torrent * b = vb;
235
236    return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH );
237}
238
239static int
240torrentCompareToHash( const void * va,
241                      const void * vb )
242{
243    const Torrent * a = va;
244    const uint8_t * b_hash = vb;
245
246    return memcmp( a->hash, b_hash, SHA_DIGEST_LENGTH );
247}
248
249static Torrent*
250getExistingTorrent( tr_peerMgr *    manager,
251                    const uint8_t * hash )
252{
253    return (Torrent*) tr_ptrArrayFindSorted( manager->torrents,
254                                             hash,
255                                             torrentCompareToHash );
256}
257
258static int
259peerCompare( const void * va, const void * vb )
260{
261    const tr_peer * a = va;
262    const tr_peer * b = vb;
263
264    return tr_compareAddresses( &a->addr, &b->addr );
265}
266
267static int
268peerCompareToAddr( const void * va, const void * vb )
269{
270    const tr_peer * a = va;
271
272    return tr_compareAddresses( &a->addr, vb );
273}
274
275static tr_peer*
276getExistingPeer( Torrent          * torrent,
277                 const tr_address * addr )
278{
279    assert( torrentIsLocked( torrent ) );
280    assert( addr );
281
282    return tr_ptrArrayFindSorted( torrent->peers, addr, peerCompareToAddr );
283}
284
285static struct peer_atom*
286getExistingAtom( const Torrent    * t,
287                 const tr_address * addr )
288{
289    assert( torrentIsLocked( t ) );
290    return tr_ptrArrayFindSorted( t->pool, addr, comparePeerAtomToAddress );
291}
292
293static int
294peerIsInUse( const Torrent    * ct,
295             const tr_address * addr )
296{
297    Torrent * t = (Torrent*) ct;
298
299    assert( torrentIsLocked ( t ) );
300
301    return getExistingPeer( t, addr )
302           || getExistingHandshake( t->outgoingHandshakes, addr )
303           || getExistingHandshake( t->manager->incomingHandshakes, addr );
304}
305
306static tr_peer*
307peerConstructor( tr_torrent * tor, const tr_address * addr )
308{
309    tr_peer * p;
310
311    p = tr_new0( tr_peer, 1 );
312    p->addr = *addr;
313    p->bandwidth = tr_bandwidthNew( tor->session, tor->bandwidth );
314    return p;
315}
316
317static tr_peer*
318getPeer( Torrent          * torrent,
319         const tr_address * addr )
320{
321    tr_peer * peer;
322
323    assert( torrentIsLocked( torrent ) );
324
325    peer = getExistingPeer( torrent, addr );
326
327    if( peer == NULL )
328    {
329        peer = peerConstructor( torrent->tor, addr );
330        tr_ptrArrayInsertSorted( torrent->peers, peer, peerCompare );
331    }
332
333    return peer;
334}
335
336static void
337peerDestructor( tr_peer * peer )
338{
339    assert( peer );
340    assert( peer->msgs );
341
342    tr_peerMsgsUnsubscribe( peer->msgs, peer->msgsTag );
343    tr_peerMsgsFree( peer->msgs );
344
345    tr_peerIoFree( peer->io );
346
347    tr_bitfieldFree( peer->have );
348    tr_bitfieldFree( peer->blame );
349    tr_free( peer->client );
350
351    tr_bandwidthFree( peer->bandwidth );
352
353    tr_free( peer );
354}
355
356static void
357removePeer( Torrent * t,
358            tr_peer * peer )
359{
360    tr_peer *          removed;
361    struct peer_atom * atom;
362
363    assert( torrentIsLocked( t ) );
364
365    atom = getExistingAtom( t, &peer->addr );
366    assert( atom );
367    atom->time = time( NULL );
368
369    removed = tr_ptrArrayRemoveSorted( t->peers, peer, peerCompare );
370    assert( removed == peer );
371    peerDestructor( removed );
372}
373
374static void
375removeAllPeers( Torrent * t )
376{
377    while( !tr_ptrArrayEmpty( t->peers ) )
378        removePeer( t, tr_ptrArrayNth( t->peers, 0 ) );
379}
380
381static void
382torrentDestructor( void * vt )
383{
384    Torrent * t = vt;
385    uint8_t   hash[SHA_DIGEST_LENGTH];
386
387    assert( t );
388    assert( !t->isRunning );
389    assert( t->peers );
390    assert( torrentIsLocked( t ) );
391    assert( tr_ptrArrayEmpty( t->outgoingHandshakes ) );
392    assert( tr_ptrArrayEmpty( t->peers ) );
393
394    memcpy( hash, t->hash, SHA_DIGEST_LENGTH );
395
396    tr_timerFree( &t->reconnectTimer );
397    tr_timerFree( &t->rechokeTimer );
398    tr_timerFree( &t->refillTimer );
399
400    tr_ptrArrayFree( t->webseeds, (PtrArrayForeachFunc)tr_webseedFree );
401    tr_ptrArrayFree( t->pool, (PtrArrayForeachFunc)tr_free );
402    tr_ptrArrayFree( t->outgoingHandshakes, NULL );
403    tr_ptrArrayFree( t->peers, NULL );
404
405    tr_free( t->pendingRequestCount );
406    tr_free( t );
407}
408
409static void peerCallbackFunc( void * vpeer,
410                              void * vevent,
411                              void * vt );
412
413static Torrent*
414torrentConstructor( tr_peerMgr * manager,
415                    tr_torrent * tor )
416{
417    int       i;
418    Torrent * t;
419
420    t = tr_new0( Torrent, 1 );
421    t->manager = manager;
422    t->tor = tor;
423    t->pool = tr_ptrArrayNew( );
424    t->peers = tr_ptrArrayNew( );
425    t->webseeds = tr_ptrArrayNew( );
426    t->outgoingHandshakes = tr_ptrArrayNew( );
427    memcpy( t->hash, tor->info.hash, SHA_DIGEST_LENGTH );
428
429    for( i = 0; i < tor->info.webseedCount; ++i )
430    {
431        tr_webseed * w =
432            tr_webseedNew( tor, tor->info.webseeds[i], peerCallbackFunc,
433                           t );
434        tr_ptrArrayAppend( t->webseeds, w );
435    }
436
437    return t;
438}
439
440
441static int bandwidthPulse( void * vmgr );
442
443
444tr_peerMgr*
445tr_peerMgrNew( tr_session * session )
446{
447    tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
448
449    m->session = session;
450    m->torrents = tr_ptrArrayNew( );
451    m->incomingHandshakes = tr_ptrArrayNew( );
452    m->bandwidthTimer = tr_timerNew( session, bandwidthPulse, m, BANDWIDTH_PERIOD_MSEC );
453    return m;
454}
455
456void
457tr_peerMgrFree( tr_peerMgr * manager )
458{
459    managerLock( manager );
460
461    tr_timerFree( &manager->bandwidthTimer );
462
463    /* free the handshakes.  Abort invokes handshakeDoneCB(), which removes
464     * the item from manager->handshakes, so this is a little roundabout... */
465    while( !tr_ptrArrayEmpty( manager->incomingHandshakes ) )
466        tr_handshakeAbort( tr_ptrArrayNth( manager->incomingHandshakes, 0 ) );
467
468    tr_ptrArrayFree( manager->incomingHandshakes, NULL );
469
470    /* free the torrents. */
471    tr_ptrArrayFree( manager->torrents, torrentDestructor );
472
473    managerUnlock( manager );
474    tr_free( manager );
475}
476
477static tr_peer**
478getConnectedPeers( Torrent * t,
479                   int *     setmeCount )
480{
481    int       i, peerCount, connectionCount;
482    tr_peer **peers;
483    tr_peer **ret;
484
485    assert( torrentIsLocked( t ) );
486
487    peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
488    ret = tr_new( tr_peer *, peerCount );
489
490    for( i = connectionCount = 0; i < peerCount; ++i )
491        if( peers[i]->msgs )
492            ret[connectionCount++] = peers[i];
493
494    *setmeCount = connectionCount;
495    return ret;
496}
497
498static int
499clientIsDownloadingFrom( const tr_peer * peer )
500{
501    return peer->clientIsInterested && !peer->clientIsChoked;
502}
503
504static int
505clientIsUploadingTo( const tr_peer * peer )
506{
507    return peer->peerIsInterested && !peer->peerIsChoked;
508}
509
510/***
511****
512***/
513
514int
515tr_peerMgrPeerIsSeed( const tr_peerMgr * mgr,
516                      const uint8_t    * torrentHash,
517                      const tr_address * addr )
518{
519    int                      isSeed = FALSE;
520    const Torrent *          t = NULL;
521    const struct peer_atom * atom = NULL;
522
523    t = getExistingTorrent( (tr_peerMgr*)mgr, torrentHash );
524    if( t )
525        atom = getExistingAtom( t, addr );
526    if( atom )
527        isSeed = ( atom->flags & ADDED_F_SEED_FLAG ) != 0;
528
529    return isSeed;
530}
531
532/****
533*****
534*****  REFILL
535*****
536****/
537
538static void
539assertValidPiece( Torrent * t, tr_piece_index_t piece )
540{
541    assert( t );
542    assert( t->tor );
543    assert( piece < t->tor->info.pieceCount );
544}
545
546static int
547getPieceRequests( Torrent * t, tr_piece_index_t piece )
548{
549    assertValidPiece( t, piece );
550
551    return t->pendingRequestCount ? t->pendingRequestCount[piece] : 0;
552}
553
554static void
555incrementPieceRequests( Torrent * t, tr_piece_index_t piece )
556{
557    assertValidPiece( t, piece );
558
559    if( t->pendingRequestCount == NULL )
560        t->pendingRequestCount = tr_new0( int, t->tor->info.pieceCount );
561    t->pendingRequestCount[piece]++;
562}
563
564static void
565decrementPieceRequests( Torrent * t, tr_piece_index_t piece )
566{
567    assertValidPiece( t, piece );
568
569    if( t->pendingRequestCount )
570        t->pendingRequestCount[piece]--;
571}
572
573struct tr_refill_piece
574{
575    tr_priority_t    priority;
576    uint32_t         piece;
577    uint32_t         peerCount;
578    int              random;
579    int              pendingRequestCount;
580    int              missingBlockCount;
581};
582
583static int
584compareRefillPiece( const void * aIn,
585                    const void * bIn )
586{
587    const struct tr_refill_piece * a = aIn;
588    const struct tr_refill_piece * b = bIn;
589
590    /* if one piece has a higher priority, it goes first */
591    if( a->priority != b->priority )
592        return a->priority > b->priority ? -1 : 1;
593
594    /* have a per-priority endgame */
595    if( a->pendingRequestCount != b->pendingRequestCount )
596        return a->pendingRequestCount < b->pendingRequestCount ? -1 : 1;
597
598    /* fewer missing pieces goes first */
599    if( a->missingBlockCount != b->missingBlockCount )
600        return a->missingBlockCount < b->missingBlockCount ? -1 : 1;
601
602    /* otherwise if one has fewer peers, it goes first */
603    if( a->peerCount != b->peerCount )
604        return a->peerCount < b->peerCount ? -1 : 1;
605
606    /* otherwise go with our random seed */
607    if( a->random != b->random )
608        return a->random < b->random ? -1 : 1;
609
610    return 0;
611}
612
613static tr_piece_index_t *
614getPreferredPieces( Torrent           * t,
615                    tr_piece_index_t  * pieceCount )
616{
617    const tr_torrent  * tor = t->tor;
618    const tr_info     * inf = &tor->info;
619    tr_piece_index_t    i;
620    tr_piece_index_t    poolSize = 0;
621    tr_piece_index_t  * pool = tr_new( tr_piece_index_t , inf->pieceCount );
622    int                 peerCount;
623    tr_peer**           peers;
624
625    assert( torrentIsLocked( t ) );
626
627    peers = getConnectedPeers( t, &peerCount );
628
629    /* make a list of the pieces that we want but don't have */
630    for( i = 0; i < inf->pieceCount; ++i )
631        if( !tor->info.pieces[i].dnd
632                && !tr_cpPieceIsComplete( tor->completion, i ) )
633            pool[poolSize++] = i;
634
635    /* sort the pool by which to request next */
636    if( poolSize > 1 )
637    {
638        tr_piece_index_t j;
639        struct tr_refill_piece * p = tr_new( struct tr_refill_piece, poolSize );
640
641        for( j = 0; j < poolSize; ++j )
642        {
643            int k;
644            const tr_piece_index_t piece = pool[j];
645            struct tr_refill_piece * setme = p + j;
646
647            setme->piece = piece;
648            setme->priority = inf->pieces[piece].priority;
649            setme->peerCount = 0;
650            setme->random = tr_cryptoWeakRandInt( INT_MAX );
651            setme->pendingRequestCount = getPieceRequests( t, piece );
652            setme->missingBlockCount
653                         = tr_cpMissingBlocksInPiece( tor->completion, piece );
654
655            for( k = 0; k < peerCount; ++k )
656            {
657                const tr_peer * peer = peers[k];
658                if( peer->peerIsInterested
659                        && !peer->clientIsChoked
660                        && tr_bitfieldHas( peer->have, piece ) )
661                    ++setme->peerCount;
662            }
663        }
664
665        qsort( p, poolSize, sizeof( struct tr_refill_piece ),
666               compareRefillPiece );
667
668        for( j = 0; j < poolSize; ++j )
669            pool[j] = p[j].piece;
670
671        tr_free( p );
672    }
673
674    tr_free( peers );
675
676    *pieceCount = poolSize;
677    return pool;
678}
679
680struct tr_blockIterator
681{
682    Torrent * t;
683    tr_block_index_t blockIndex, blockCount, *blocks;
684    tr_piece_index_t pieceIndex, pieceCount, *pieces;
685};
686
687static struct tr_blockIterator*
688blockIteratorNew( Torrent * t )
689{
690    struct tr_blockIterator * i = tr_new0( struct tr_blockIterator, 1 );
691    i->t = t;
692    i->pieces = getPreferredPieces( t, &i->pieceCount );
693    i->blocks = tr_new0( tr_block_index_t, t->tor->blockCount );
694    return i;
695}
696
697static int
698blockIteratorNext( struct tr_blockIterator * i, tr_block_index_t * setme )
699{
700    int found;
701    Torrent * t = i->t;
702    tr_torrent * tor = t->tor;
703
704    while( ( i->blockIndex == i->blockCount )
705        && ( i->pieceIndex < i->pieceCount ) )
706    {
707        const tr_piece_index_t index = i->pieces[i->pieceIndex++];
708        const tr_block_index_t b = tr_torPieceFirstBlock( tor, index );
709        const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, index );
710        tr_block_index_t block;
711
712        assert( index < tor->info.pieceCount );
713
714        i->blockCount = 0;
715        i->blockIndex = 0;
716        for( block=b; block!=e; ++block )
717            if( !tr_cpBlockIsComplete( tor->completion, block ) )
718                i->blocks[i->blockCount++] = block;
719    }
720
721    if(( found = ( i->blockIndex < i->blockCount )))
722        *setme = i->blocks[i->blockIndex++];
723
724    return found;
725}
726
727static void
728blockIteratorFree( struct tr_blockIterator * i )
729{
730    tr_free( i->blocks );
731    tr_free( i->pieces );
732    tr_free( i );
733}
734
735static tr_peer**
736getPeersUploadingToClient( Torrent * t,
737                           int *     setmeCount )
738{
739    int j;
740    int peerCount = 0;
741    int retCount = 0;
742    tr_peer ** peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
743    tr_peer ** ret = tr_new( tr_peer *, peerCount );
744
745    j = 0; /* this is a temporary test to make sure we walk through all the peers */
746    if( peerCount )
747    {
748        /* Get a list of peers we're downloading from.
749           Pick a different starting point each time so all peers
750           get a chance at being the first in line */
751        const int fencepost = tr_cryptoWeakRandInt( peerCount );
752        int i = fencepost;
753        do {
754            if( clientIsDownloadingFrom( peers[i] ) )
755                ret[retCount++] = peers[i];
756            i = ( i + 1 ) % peerCount;
757            ++j;
758        } while( i != fencepost );
759    }
760    assert( j == peerCount );
761    *setmeCount = retCount;
762    return ret;
763}
764
765static uint32_t
766getBlockOffsetInPiece( const tr_torrent * tor, uint64_t b )
767{
768    const uint64_t piecePos = tor->info.pieceSize * tr_torBlockPiece( tor, b );
769    const uint64_t blockPos = tor->blockSize * b;
770    assert( blockPos >= piecePos );
771    return (uint32_t)( blockPos - piecePos );
772}
773
774static int
775refillPulse( void * vtorrent )
776{
777    tr_block_index_t block;
778    int peerCount;
779    int webseedCount;
780    tr_peer ** peers;
781    tr_webseed ** webseeds;
782    struct tr_blockIterator * blockIterator;
783    Torrent * t = vtorrent;
784    tr_torrent * tor = t->tor;
785
786    if( !t->isRunning )
787        return TRUE;
788    if( tr_torrentIsSeed( t->tor ) )
789        return TRUE;
790
791    torrentLock( t );
792    tordbg( t, "Refilling Request Buffers..." );
793
794    blockIterator = blockIteratorNew( t );
795    peers = getPeersUploadingToClient( t, &peerCount );
796    webseedCount = tr_ptrArraySize( t->webseeds );
797    webseeds = tr_memdup( tr_ptrArrayBase( t->webseeds ),
798                          webseedCount * sizeof( tr_webseed* ) );
799
800    while( ( webseedCount || peerCount )
801        && blockIteratorNext( blockIterator, &block ) )
802    {
803        int j;
804        int handled = FALSE;
805
806        const tr_piece_index_t index = tr_torBlockPiece( tor, block );
807        const uint32_t offset = getBlockOffsetInPiece( tor, block );
808        const uint32_t length = tr_torBlockCountBytes( tor, block );
809
810        /* find a peer who can ask for this block */
811        for( j=0; !handled && j<peerCount; )
812        {
813            const int val = tr_peerMsgsAddRequest( peers[j]->msgs,
814                                                   index, offset, length );
815            switch( val )
816            {
817                case TR_ADDREQ_FULL:
818                case TR_ADDREQ_CLIENT_CHOKED:
819                    peers[j] = peers[--peerCount];
820                    break;
821
822                case TR_ADDREQ_MISSING:
823                case TR_ADDREQ_DUPLICATE:
824                    ++j;
825                    break;
826
827                case TR_ADDREQ_OK:
828                    incrementPieceRequests( t, index );
829                    handled = TRUE;
830                    break;
831
832                default:
833                    assert( 0 && "unhandled value" );
834                    break;
835            }
836        }
837
838        /* maybe one of the webseeds can do it */
839        for( j=0; !handled && j<webseedCount; )
840        {
841            const tr_addreq_t val = tr_webseedAddRequest( webseeds[j],
842                                                          index, offset, length );
843            switch( val )
844            {
845                case TR_ADDREQ_FULL:
846                    webseeds[j] = webseeds[--webseedCount];
847                    break;
848
849                case TR_ADDREQ_OK:
850                    incrementPieceRequests( t, index );
851                    handled = TRUE;
852                    break;
853
854                default:
855                    assert( 0 && "unhandled value" );
856                    break;
857            }
858        }
859    }
860
861    /* cleanup */
862    blockIteratorFree( blockIterator );
863    tr_free( webseeds );
864    tr_free( peers );
865
866    t->refillTimer = NULL;
867    torrentUnlock( t );
868    return FALSE;
869}
870
871static void
872broadcastGotBlock( Torrent * t, uint32_t index, uint32_t offset, uint32_t length )
873{
874    int i, size;
875    tr_peer ** peers;
876
877    assert( torrentIsLocked( t ) );
878
879    peers = getConnectedPeers( t, &size );
880    for( i=0; i<size; ++i )
881        tr_peerMsgsCancel( peers[i]->msgs, index, offset, length );
882    tr_free( peers );
883}
884
885static void
886addStrike( Torrent * t,
887           tr_peer * peer )
888{
889    tordbg( t, "increasing peer %s strike count to %d",
890            tr_peerIoAddrStr( &peer->addr,
891                              peer->port ), peer->strikes + 1 );
892
893    if( ++peer->strikes >= MAX_BAD_PIECES_PER_PEER )
894    {
895        struct peer_atom * atom = getExistingAtom( t, &peer->addr );
896        atom->myflags |= MYFLAG_BANNED;
897        peer->doPurge = 1;
898        tordbg( t, "banning peer %s",
899               tr_peerIoAddrStr( &atom->addr, atom->port ) );
900    }
901}
902
903static void
904gotBadPiece( Torrent *        t,
905             tr_piece_index_t pieceIndex )
906{
907    tr_torrent *   tor = t->tor;
908    const uint32_t byteCount = tr_torPieceCountBytes( tor, pieceIndex );
909
910    tor->corruptCur += byteCount;
911    tor->downloadedCur -= MIN( tor->downloadedCur, byteCount );
912}
913
914static void
915refillSoon( Torrent * t )
916{
917    if( t->refillTimer == NULL )
918        t->refillTimer = tr_timerNew( t->manager->session,
919                                      refillPulse, t,
920                                      REFILL_PERIOD_MSEC );
921}
922
923static void
924peerCallbackFunc( void * vpeer,
925                  void * vevent,
926                  void * vt )
927{
928    tr_peer *             peer = vpeer; /* may be NULL if peer is a webseed */
929    Torrent *             t = (Torrent *) vt;
930    const tr_peer_event * e = vevent;
931
932    torrentLock( t );
933
934    switch( e->eventType )
935    {
936        case TR_PEER_NEED_REQ:
937            refillSoon( t );
938            break;
939
940        case TR_PEER_CANCEL:
941            decrementPieceRequests( t, e->pieceIndex );
942            break;
943
944        case TR_PEER_PEER_GOT_DATA:
945        {
946            const time_t now = time( NULL );
947            tr_torrent * tor = t->tor;
948
949            tor->activityDate = now;
950
951            if( e->wasPieceData )
952                tor->uploadedCur += e->length;
953
954            /* update the stats */
955            if( e->wasPieceData )
956                tr_statsAddUploaded( tor->session, e->length );
957
958            /* update our atom */
959            if( peer ) {
960                struct peer_atom * a = getExistingAtom( t, &peer->addr );
961                a->piece_data_time = now;
962            }
963
964            break;
965        }
966
967        case TR_PEER_CLIENT_GOT_DATA:
968        {
969            const time_t now = time( NULL );
970            tr_torrent * tor = t->tor;
971
972            tor->activityDate = now;
973
974            /* only add this to downloadedCur if we got it from a peer --
975             * webseeds shouldn't count against our ratio.  As one tracker
976             * admin put it, "Those pieces are downloaded directly from the
977             * content distributor, not the peers, it is the tracker's job
978             * to manage the swarms, not the web server and does not fit
979             * into the jurisdiction of the tracker." */
980            if( peer && e->wasPieceData )
981                tor->downloadedCur += e->length;
982
983            /* update the stats */ 
984            if( e->wasPieceData )
985                tr_statsAddDownloaded( tor->session, e->length );
986
987            /* update our atom */
988            if( peer ) {
989                struct peer_atom * a = getExistingAtom( t, &peer->addr );
990                a->piece_data_time = now;
991            }
992
993            break;
994        }
995
996        case TR_PEER_PEER_PROGRESS:
997        {
998            if( peer )
999            {
1000                struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1001                const int          peerIsSeed = e->progress >= 1.0;
1002                if( peerIsSeed )
1003                {
1004                    tordbg( t, "marking peer %s as a seed",
1005                           tr_peerIoAddrStr( &atom->addr,
1006                                             atom->port ) );
1007                    atom->flags |= ADDED_F_SEED_FLAG;
1008                }
1009                else
1010                {
1011                    tordbg( t, "marking peer %s as a non-seed",
1012                           tr_peerIoAddrStr( &atom->addr,
1013                                             atom->port ) );
1014                    atom->flags &= ~ADDED_F_SEED_FLAG;
1015                }
1016            }
1017            break;
1018        }
1019
1020        case TR_PEER_CLIENT_GOT_BLOCK:
1021        {
1022            tr_torrent *     tor = t->tor;
1023
1024            tr_block_index_t block = _tr_block( tor, e->pieceIndex,
1025                                                e->offset );
1026
1027            tr_cpBlockAdd( tor->completion, block );
1028            decrementPieceRequests( t, e->pieceIndex );
1029
1030            broadcastGotBlock( t, e->pieceIndex, e->offset, e->length );
1031
1032            if( tr_cpPieceIsComplete( tor->completion, e->pieceIndex ) )
1033            {
1034                const tr_piece_index_t p = e->pieceIndex;
1035                const int              ok = tr_ioTestPiece( tor, p );
1036
1037                if( !ok )
1038                {
1039                    tr_torerr( tor,
1040                              _( "Piece %lu, which was just downloaded, failed its checksum test" ),
1041                              (unsigned long)p );
1042                }
1043
1044                tr_torrentSetHasPiece( tor, p, ok );
1045                tr_torrentSetPieceChecked( tor, p, TRUE );
1046                tr_peerMgrSetBlame( tor->session->peerMgr, tor->info.hash, p, ok );
1047
1048                if( !ok )
1049                    gotBadPiece( t, p );
1050                else
1051                {
1052                    int        i, peerCount;
1053                    tr_peer ** peers = getConnectedPeers( t, &peerCount );
1054                    for( i = 0; i < peerCount; ++i )
1055                        tr_peerMsgsHave( peers[i]->msgs, p );
1056                    tr_free( peers );
1057                }
1058
1059                tr_torrentRecheckCompleteness( tor );
1060            }
1061            break;
1062        }
1063
1064        case TR_PEER_ERROR:
1065            if( e->err == EINVAL )
1066            {
1067                addStrike( t, peer );
1068                peer->doPurge = 1;
1069            }
1070            else if( ( e->err == ERANGE )
1071                  || ( e->err == EMSGSIZE )
1072                  || ( e->err == ENOTCONN ) )
1073            {
1074                /* some protocol error from the peer */
1075                peer->doPurge = 1;
1076            }
1077            else /* a local error, such as an IO error */
1078            {
1079                t->tor->error = e->err;
1080                tr_strlcpy( t->tor->errorString,
1081                            tr_strerror( t->tor->error ),
1082                            sizeof( t->tor->errorString ) );
1083                tr_torrentStop( t->tor );
1084            }
1085            break;
1086
1087        default:
1088            assert( 0 );
1089    }
1090
1091    torrentUnlock( t );
1092}
1093
1094static void
1095ensureAtomExists( Torrent          * t,
1096                  const tr_address * addr,
1097                  tr_port            port,
1098                  uint8_t            flags,
1099                  uint8_t            from )
1100{
1101    if( getExistingAtom( t, addr ) == NULL )
1102    {
1103        struct peer_atom * a;
1104        a = tr_new0( struct peer_atom, 1 );
1105        a->addr = *addr;
1106        a->port = port;
1107        a->flags = flags;
1108        a->from = from;
1109        tordbg( t, "got a new atom: %s",
1110               tr_peerIoAddrStr( &a->addr, a->port ) );
1111        tr_ptrArrayInsertSorted( t->pool, a, comparePeerAtoms );
1112    }
1113}
1114
1115static int
1116getMaxPeerCount( const tr_torrent * tor )
1117{
1118    return tor->maxConnectedPeers;
1119}
1120
1121static int
1122getPeerCount( const Torrent * t )
1123{
1124    return tr_ptrArraySize( t->peers ) + tr_ptrArraySize(
1125               t->outgoingHandshakes );
1126}
1127
1128/* FIXME: this is kind of a mess. */
1129static int
1130myHandshakeDoneCB( tr_handshake *  handshake,
1131                   tr_peerIo *     io,
1132                   int             isConnected,
1133                   const uint8_t * peer_id,
1134                   void *          vmanager )
1135{
1136    int                ok = isConnected;
1137    int                success = FALSE;
1138    tr_port            port;
1139    const tr_address * addr;
1140    tr_peerMgr       * manager = vmanager;
1141    Torrent          * t;
1142    tr_handshake     * ours;
1143
1144    assert( io );
1145    assert( isConnected == 0 || isConnected == 1 );
1146
1147    t = tr_peerIoHasTorrentHash( io )
1148        ? getExistingTorrent( manager, tr_peerIoGetTorrentHash( io ) )
1149        : NULL;
1150
1151    if( tr_peerIoIsIncoming ( io ) )
1152        ours = tr_ptrArrayRemoveSorted( manager->incomingHandshakes,
1153                                        handshake, handshakeCompare );
1154    else if( t )
1155        ours = tr_ptrArrayRemoveSorted( t->outgoingHandshakes,
1156                                        handshake, handshakeCompare );
1157    else
1158        ours = handshake;
1159
1160    assert( ours );
1161    assert( ours == handshake );
1162
1163    if( t )
1164        torrentLock( t );
1165
1166    addr = tr_peerIoGetAddress( io, &port );
1167
1168    if( !ok || !t || !t->isRunning )
1169    {
1170        if( t )
1171        {
1172            struct peer_atom * atom = getExistingAtom( t, addr );
1173            if( atom )
1174                ++atom->numFails;
1175        }
1176
1177        tr_peerIoFree( io );
1178    }
1179    else /* looking good */
1180    {
1181        struct peer_atom * atom;
1182        ensureAtomExists( t, addr, port, 0, TR_PEER_FROM_INCOMING );
1183        atom = getExistingAtom( t, addr );
1184        atom->time = time( NULL );
1185
1186        if( atom->myflags & MYFLAG_BANNED )
1187        {
1188            tordbg( t, "banned peer %s tried to reconnect",
1189                   tr_peerIoAddrStr( &atom->addr,
1190                                     atom->port ) );
1191            tr_peerIoFree( io );
1192        }
1193        else if( tr_peerIoIsIncoming( io )
1194               && ( getPeerCount( t ) >= getMaxPeerCount( t->tor ) ) )
1195
1196        {
1197            tr_peerIoFree( io );
1198        }
1199        else
1200        {
1201            tr_peer * peer = getExistingPeer( t, addr );
1202
1203            if( peer ) /* we already have this peer */
1204            {
1205                tr_peerIoFree( io );
1206            }
1207            else
1208            {
1209                peer = getPeer( t, addr );
1210                tr_free( peer->client );
1211
1212                if( !peer_id )
1213                    peer->client = NULL;
1214                else {
1215                    char client[128];
1216                    tr_clientForId( client, sizeof( client ), peer_id );
1217                    peer->client = tr_strdup( client );
1218                }
1219
1220                peer->port = port;
1221                peer->io = io;
1222                peer->msgs = tr_peerMsgsNew( t->tor, peer, peerCallbackFunc, t, &peer->msgsTag );
1223                tr_peerIoSetBandwidth( io, peer->bandwidth );
1224
1225                success = TRUE;
1226            }
1227        }
1228    }
1229
1230    if( t )
1231        torrentUnlock( t );
1232
1233    return success;
1234}
1235
1236void
1237tr_peerMgrAddIncoming( tr_peerMgr * manager,
1238                       tr_address * addr,
1239                       tr_port      port,
1240                       int          socket )
1241{
1242    managerLock( manager );
1243
1244    if( tr_sessionIsAddressBlocked( manager->session, addr ) )
1245    {
1246        tr_dbg( "Banned IP address \"%s\" tried to connect to us",
1247               tr_ntop_non_ts( addr ) );
1248        tr_netClose( socket );
1249    }
1250    else if( getExistingHandshake( manager->incomingHandshakes, addr ) )
1251    {
1252        tr_netClose( socket );
1253    }
1254    else /* we don't have a connetion to them yet... */
1255    {
1256        tr_peerIo *    io;
1257        tr_handshake * handshake;
1258
1259        io = tr_peerIoNewIncoming( manager->session, addr, port, socket );
1260
1261        handshake = tr_handshakeNew( io,
1262                                     manager->session->encryptionMode,
1263                                     myHandshakeDoneCB,
1264                                     manager );
1265
1266        tr_ptrArrayInsertSorted( manager->incomingHandshakes, handshake,
1267                                 handshakeCompare );
1268    }
1269
1270    managerUnlock( manager );
1271}
1272
1273void
1274tr_peerMgrAddPex( tr_peerMgr *    manager,
1275                  const uint8_t * torrentHash,
1276                  uint8_t         from,
1277                  const tr_pex *  pex )
1278{
1279    Torrent * t;
1280
1281    managerLock( manager );
1282
1283    t = getExistingTorrent( manager, torrentHash );
1284    if( !tr_sessionIsAddressBlocked( t->manager->session, &pex->addr ) )
1285        ensureAtomExists( t, &pex->addr, pex->port, pex->flags, from );
1286
1287    managerUnlock( manager );
1288}
1289
1290tr_pex *
1291tr_peerMgrCompactToPex( const void *    compact,
1292                        size_t          compactLen,
1293                        const uint8_t * added_f,
1294                        size_t          added_f_len,
1295                        size_t *        pexCount )
1296{
1297    size_t          i;
1298    size_t          n = compactLen / 6;
1299    const uint8_t * walk = compact;
1300    tr_pex *        pex = tr_new0( tr_pex, n );
1301
1302    for( i = 0; i < n; ++i )
1303    {
1304        pex[i].addr.type = TR_AF_INET;
1305        memcpy( &pex[i].addr.addr, walk, 4 ); walk += 4;
1306        memcpy( &pex[i].port, walk, 2 ); walk += 2;
1307        if( added_f && ( n == added_f_len ) )
1308            pex[i].flags = added_f[i];
1309    }
1310
1311    *pexCount = n;
1312    return pex;
1313}
1314
1315/**
1316***
1317**/
1318
1319void
1320tr_peerMgrSetBlame( tr_peerMgr *     manager,
1321                    const uint8_t *  torrentHash,
1322                    tr_piece_index_t pieceIndex,
1323                    int              success )
1324{
1325    if( !success )
1326    {
1327        int        peerCount, i;
1328        Torrent *  t = getExistingTorrent( manager, torrentHash );
1329        tr_peer ** peers;
1330
1331        assert( torrentIsLocked( t ) );
1332
1333        peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
1334        for( i = 0; i < peerCount; ++i )
1335        {
1336            tr_peer * peer = peers[i];
1337            if( tr_bitfieldHas( peer->blame, pieceIndex ) )
1338            {
1339                tordbg(
1340                    t,
1341                    "peer %s contributed to corrupt piece (%d); now has %d strikes",
1342                    tr_peerIoAddrStr( &peer->addr, peer->port ),
1343                    pieceIndex, (int)peer->strikes + 1 );
1344                addStrike( t, peer );
1345            }
1346        }
1347    }
1348}
1349
1350int
1351tr_pexCompare( const void * va,
1352               const void * vb )
1353{
1354    const tr_pex * a = va;
1355    const tr_pex * b = vb;
1356    int            i = tr_compareAddresses( &a->addr, &b->addr );
1357
1358    if( i ) return i;
1359    if( a->port < b->port ) return -1;
1360    if( a->port > b->port ) return 1;
1361    return 0;
1362}
1363
1364int tr_pexCompare( const void * a,
1365                   const void * b );
1366
1367static int
1368peerPrefersCrypto( const tr_peer * peer )
1369{
1370    if( peer->encryption_preference == ENCRYPTION_PREFERENCE_YES )
1371        return TRUE;
1372
1373    if( peer->encryption_preference == ENCRYPTION_PREFERENCE_NO )
1374        return FALSE;
1375
1376    return tr_peerIoIsEncrypted( peer->io );
1377}
1378
1379int
1380tr_peerMgrGetPeers( tr_peerMgr *    manager,
1381                    const uint8_t * torrentHash,
1382                    tr_pex **       setme_pex )
1383{
1384    int peerCount = 0;
1385    const Torrent *  t;
1386
1387    managerLock( manager );
1388
1389    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1390    if( !t )
1391    {
1392        *setme_pex = NULL;
1393    }
1394    else
1395    {
1396        int i;
1397        const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
1398        tr_pex * pex = tr_new( tr_pex, peerCount );
1399        tr_pex * walk = pex;
1400
1401        for( i = 0; i < peerCount; ++i, ++walk )
1402        {
1403            const tr_peer * peer = peers[i];
1404            walk->addr = peer->addr;
1405            walk->port = peer->port;
1406            walk->flags = 0;
1407            if( peerPrefersCrypto( peer ) ) walk->flags |= ADDED_F_ENCRYPTION_FLAG;
1408            if( peer->progress >= 1.0 ) walk->flags |= ADDED_F_SEED_FLAG;
1409        }
1410
1411        assert( ( walk - pex ) == peerCount );
1412        qsort( pex, peerCount, sizeof( tr_pex ), tr_pexCompare );
1413        *setme_pex = pex;
1414    }
1415
1416    managerUnlock( manager );
1417    return peerCount;
1418}
1419
1420static int reconnectPulse( void * vtorrent );
1421
1422static int rechokePulse( void * vtorrent );
1423
1424void
1425tr_peerMgrStartTorrent( tr_peerMgr *    manager,
1426                        const uint8_t * torrentHash )
1427{
1428    Torrent * t;
1429
1430    managerLock( manager );
1431
1432    t = getExistingTorrent( manager, torrentHash );
1433
1434    assert( t );
1435    assert( ( t->isRunning != 0 ) == ( t->reconnectTimer != NULL ) );
1436    assert( ( t->isRunning != 0 ) == ( t->rechokeTimer != NULL ) );
1437
1438    if( !t->isRunning )
1439    {
1440        t->isRunning = 1;
1441
1442        t->reconnectTimer = tr_timerNew( t->manager->session,
1443                                         reconnectPulse, t,
1444                                         RECONNECT_PERIOD_MSEC );
1445
1446        t->rechokeTimer = tr_timerNew( t->manager->session,
1447                                       rechokePulse, t,
1448                                       RECHOKE_PERIOD_MSEC );
1449
1450        reconnectPulse( t );
1451
1452        rechokePulse( t );
1453
1454        if( !tr_ptrArrayEmpty( t->webseeds ) )
1455            refillSoon( t );
1456    }
1457
1458    managerUnlock( manager );
1459}
1460
1461static void
1462stopTorrent( Torrent * t )
1463{
1464    assert( torrentIsLocked( t ) );
1465
1466    t->isRunning = 0;
1467    tr_timerFree( &t->rechokeTimer );
1468    tr_timerFree( &t->reconnectTimer );
1469
1470    /* disconnect the peers. */
1471    tr_ptrArrayForeach( t->peers, (PtrArrayForeachFunc)peerDestructor );
1472    tr_ptrArrayClear( t->peers );
1473
1474    /* disconnect the handshakes.  handshakeAbort calls handshakeDoneCB(),
1475     * which removes the handshake from t->outgoingHandshakes... */
1476    while( !tr_ptrArrayEmpty( t->outgoingHandshakes ) )
1477        tr_handshakeAbort( tr_ptrArrayNth( t->outgoingHandshakes, 0 ) );
1478}
1479
1480void
1481tr_peerMgrStopTorrent( tr_peerMgr *    manager,
1482                       const uint8_t * torrentHash )
1483{
1484    managerLock( manager );
1485
1486    stopTorrent( getExistingTorrent( manager, torrentHash ) );
1487
1488    managerUnlock( manager );
1489}
1490
1491void
1492tr_peerMgrAddTorrent( tr_peerMgr * manager,
1493                      tr_torrent * tor )
1494{
1495    Torrent * t;
1496
1497    managerLock( manager );
1498
1499    assert( tor );
1500    assert( getExistingTorrent( manager, tor->info.hash ) == NULL );
1501
1502    t = torrentConstructor( manager, tor );
1503    tr_ptrArrayInsertSorted( manager->torrents, t, torrentCompare );
1504
1505    managerUnlock( manager );
1506}
1507
1508void
1509tr_peerMgrRemoveTorrent( tr_peerMgr *    manager,
1510                         const uint8_t * torrentHash )
1511{
1512    Torrent * t;
1513
1514    managerLock( manager );
1515
1516    t = getExistingTorrent( manager, torrentHash );
1517    assert( t );
1518    stopTorrent( t );
1519    tr_ptrArrayRemoveSorted( manager->torrents, t, torrentCompare );
1520    torrentDestructor( t );
1521
1522    managerUnlock( manager );
1523}
1524
1525void
1526tr_peerMgrTorrentAvailability( const tr_peerMgr * manager,
1527                               const uint8_t *    torrentHash,
1528                               int8_t *           tab,
1529                               unsigned int       tabCount )
1530{
1531    tr_piece_index_t   i;
1532    const Torrent *    t;
1533    const tr_torrent * tor;
1534    float              interval;
1535    int                isSeed;
1536    int                peerCount;
1537    const tr_peer **   peers;
1538
1539    managerLock( manager );
1540
1541    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1542    tor = t->tor;
1543    interval = tor->info.pieceCount / (float)tabCount;
1544    isSeed = tor && ( tr_cpGetStatus ( tor->completion ) == TR_SEED );
1545    peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
1546
1547    memset( tab, 0, tabCount );
1548
1549    for( i = 0; tor && i < tabCount; ++i )
1550    {
1551        const int piece = i * interval;
1552
1553        if( isSeed || tr_cpPieceIsComplete( tor->completion, piece ) )
1554            tab[i] = -1;
1555        else if( peerCount )
1556        {
1557            int j;
1558            for( j = 0; j < peerCount; ++j )
1559                if( tr_bitfieldHas( peers[j]->have, i ) )
1560                    ++tab[i];
1561        }
1562    }
1563
1564    managerUnlock( manager );
1565}
1566
1567/* Returns the pieces that are available from peers */
1568tr_bitfield*
1569tr_peerMgrGetAvailable( const tr_peerMgr * manager,
1570                        const uint8_t *    torrentHash )
1571{
1572    int           i, size;
1573    Torrent *     t;
1574    tr_peer **    peers;
1575    tr_bitfield * pieces;
1576
1577    managerLock( manager );
1578
1579    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1580    pieces = tr_bitfieldNew( t->tor->info.pieceCount );
1581    peers = getConnectedPeers( t, &size );
1582    for( i = 0; i < size; ++i )
1583        tr_bitfieldOr( pieces, peers[i]->have );
1584
1585    managerUnlock( manager );
1586    tr_free( peers );
1587    return pieces;
1588}
1589
1590int
1591tr_peerMgrHasConnections( const tr_peerMgr * manager,
1592                          const uint8_t *    torrentHash )
1593{
1594    int             ret;
1595    const Torrent * t;
1596
1597    managerLock( manager );
1598
1599    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1600    ret = t && ( !tr_ptrArrayEmpty( t->peers )
1601               || !tr_ptrArrayEmpty( t->webseeds ) );
1602
1603    managerUnlock( manager );
1604    return ret;
1605}
1606
1607void
1608tr_peerMgrTorrentStats( const tr_peerMgr * manager,
1609                        const uint8_t *    torrentHash,
1610                        int *              setmePeersKnown,
1611                        int *              setmePeersConnected,
1612                        int *              setmeSeedsConnected,
1613                        int *              setmeWebseedsSendingToUs,
1614                        int *              setmePeersSendingToUs,
1615                        int *              setmePeersGettingFromUs,
1616                        int *              setmePeersFrom )
1617{
1618    int                 i, size;
1619    const Torrent *     t;
1620    const tr_peer **    peers;
1621    const tr_webseed ** webseeds;
1622
1623    managerLock( manager );
1624
1625    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1626    peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
1627
1628    *setmePeersKnown           = tr_ptrArraySize( t->pool );
1629    *setmePeersConnected       = 0;
1630    *setmeSeedsConnected       = 0;
1631    *setmePeersGettingFromUs   = 0;
1632    *setmePeersSendingToUs     = 0;
1633    *setmeWebseedsSendingToUs  = 0;
1634
1635    for( i = 0; i < TR_PEER_FROM__MAX; ++i )
1636        setmePeersFrom[i] = 0;
1637
1638    for( i = 0; i < size; ++i )
1639    {
1640        const tr_peer *          peer = peers[i];
1641        const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1642
1643        if( peer->io == NULL ) /* not connected */
1644            continue;
1645
1646        ++ * setmePeersConnected;
1647
1648        ++setmePeersFrom[atom->from];
1649
1650        if( clientIsDownloadingFrom( peer ) )
1651            ++ * setmePeersSendingToUs;
1652
1653        if( clientIsUploadingTo( peer ) )
1654            ++ * setmePeersGettingFromUs;
1655
1656        if( atom->flags & ADDED_F_SEED_FLAG )
1657            ++ * setmeSeedsConnected;
1658    }
1659
1660    webseeds = (const tr_webseed **) tr_ptrArrayPeek( t->webseeds, &size );
1661    for( i = 0; i < size; ++i )
1662    {
1663        if( tr_webseedIsActive( webseeds[i] ) )
1664            ++ * setmeWebseedsSendingToUs;
1665    }
1666
1667    managerUnlock( manager );
1668}
1669
1670float*
1671tr_peerMgrWebSpeeds( const tr_peerMgr * manager,
1672                     const uint8_t *    torrentHash )
1673{
1674    const Torrent *     t;
1675    const tr_webseed ** webseeds;
1676    int                 i;
1677    int                 webseedCount;
1678    float *             ret;
1679
1680    assert( manager );
1681    managerLock( manager );
1682
1683    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1684    webseeds = (const tr_webseed**) tr_ptrArrayPeek( t->webseeds,
1685                                                     &webseedCount );
1686    assert( webseedCount == t->tor->info.webseedCount );
1687    ret = tr_new0( float, webseedCount );
1688
1689    for( i = 0; i < webseedCount; ++i )
1690        if( !tr_webseedGetSpeed( webseeds[i], &ret[i] ) )
1691            ret[i] = -1.0;
1692
1693    managerUnlock( manager );
1694    return ret;
1695}
1696
1697double
1698tr_peerGetPieceSpeed( const tr_peer    * peer,
1699                      tr_direction       direction )
1700{
1701    assert( peer );
1702    assert( direction==TR_CLIENT_TO_PEER || direction==TR_PEER_TO_CLIENT );
1703
1704    return tr_bandwidthGetPieceSpeed( peer->bandwidth, direction );
1705}
1706
1707
1708struct tr_peer_stat *
1709tr_peerMgrPeerStats( const   tr_peerMgr  * manager,
1710                     const   uint8_t     * torrentHash,
1711                     int                 * setmeCount UNUSED )
1712{
1713    int             i, size;
1714    const Torrent * t;
1715    tr_peer **      peers;
1716    tr_peer_stat *  ret;
1717
1718    assert( manager );
1719    managerLock( manager );
1720
1721    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1722    peers = getConnectedPeers( (Torrent*)t, &size );
1723    ret = tr_new0( tr_peer_stat, size );
1724
1725    for( i = 0; i < size; ++i )
1726    {
1727        char *                   pch;
1728        const tr_peer *          peer = peers[i];
1729        const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1730        tr_peer_stat *           stat = ret + i;
1731
1732        tr_ntop( &peer->addr, stat->addr, sizeof( stat->addr ) );
1733        tr_strlcpy( stat->client, ( peer->client ? peer->client : "" ),
1734                   sizeof( stat->client ) );
1735        stat->port               = ntohs( peer->port );
1736        stat->from               = atom->from;
1737        stat->progress           = peer->progress;
1738        stat->isEncrypted        = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
1739        stat->rateToPeer         = tr_peerGetPieceSpeed( peer, TR_CLIENT_TO_PEER );
1740        stat->rateToClient       = tr_peerGetPieceSpeed( peer, TR_PEER_TO_CLIENT );
1741        stat->peerIsChoked       = peer->peerIsChoked;
1742        stat->peerIsInterested   = peer->peerIsInterested;
1743        stat->clientIsChoked     = peer->clientIsChoked;
1744        stat->clientIsInterested = peer->clientIsInterested;
1745        stat->isIncoming         = tr_peerIoIsIncoming( peer->io );
1746        stat->isDownloadingFrom  = clientIsDownloadingFrom( peer );
1747        stat->isUploadingTo      = clientIsUploadingTo( peer );
1748
1749        pch = stat->flagStr;
1750        if( t->optimistic == peer ) *pch++ = 'O';
1751        if( stat->isDownloadingFrom ) *pch++ = 'D';
1752        else if( stat->clientIsInterested ) *pch++ = 'd';
1753        if( stat->isUploadingTo ) *pch++ = 'U';
1754        else if( stat->peerIsInterested ) *pch++ = 'u';
1755        if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ =
1756                'K';
1757        if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
1758        if( stat->isEncrypted ) *pch++ = 'E';
1759        if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
1760        if( stat->isIncoming ) *pch++ = 'I';
1761        *pch = '\0';
1762    }
1763
1764    *setmeCount = size;
1765    tr_free( peers );
1766
1767    managerUnlock( manager );
1768    return ret;
1769}
1770
1771/**
1772***
1773**/
1774
1775struct ChokeData
1776{
1777    tr_bool         doUnchoke;
1778    tr_bool         isInterested;
1779    tr_bool         isChoked;
1780    int             rate;
1781    tr_peer *       peer;
1782};
1783
1784static int
1785compareChoke( const void * va,
1786              const void * vb )
1787{
1788    const struct ChokeData * a = va;
1789    const struct ChokeData * b = vb;
1790
1791    if( a->rate != b->rate ) /* prefer higher overall speeds */
1792        return a->rate > b->rate ? -1 : 1;
1793
1794    if( a->isChoked != b->isChoked ) /* prefer unchoked */
1795        return a->isChoked ? 1 : -1;
1796
1797    return 0;
1798}
1799
1800static int
1801isNew( const tr_peer * peer )
1802{
1803    return peer && peer->io && tr_peerIoGetAge( peer->io ) < 45;
1804}
1805
1806static int
1807isSame( const tr_peer * peer )
1808{
1809    return peer && peer->client && strstr( peer->client, "Transmission" );
1810}
1811
1812/**
1813***
1814**/
1815
1816static void
1817rechoke( Torrent * t )
1818{
1819    int                i, peerCount, size, unchokedInterested;
1820    tr_peer **         peers = getConnectedPeers( t, &peerCount );
1821    struct ChokeData * choke = tr_new0( struct ChokeData, peerCount );
1822    const int          chokeAll = !tr_torrentIsPieceTransferAllowed( t->tor, TR_CLIENT_TO_PEER );
1823
1824    assert( torrentIsLocked( t ) );
1825
1826    /* sort the peers by preference and rate */
1827    for( i = 0, size = 0; i < peerCount; ++i )
1828    {
1829        tr_peer * peer = peers[i];
1830        if( peer->progress >= 1.0 ) /* choke all seeds */
1831            tr_peerMsgsSetChoke( peer->msgs, TRUE );
1832        else if( chokeAll )
1833            tr_peerMsgsSetChoke( peer->msgs, TRUE );
1834        else {
1835            struct ChokeData * n = &choke[size++];
1836            n->peer         = peer;
1837            n->isInterested = peer->peerIsInterested;
1838            n->isChoked     = peer->peerIsChoked;
1839            n->rate         = tr_peerGetPieceSpeed( peer, TR_CLIENT_TO_PEER ) * 1024;
1840        }
1841    }
1842
1843    qsort( choke, size, sizeof( struct ChokeData ), compareChoke );
1844
1845    /**
1846     * Reciprocation and number of uploads capping is managed by unchoking
1847     * the N peers which have the best upload rate and are interested.
1848     * This maximizes the client's download rate. These N peers are
1849     * referred to as downloaders, because they are interested in downloading
1850     * from the client.
1851     *
1852     * Peers which have a better upload rate (as compared to the downloaders)
1853     * but aren't interested get unchoked. If they become interested, the
1854     * downloader with the worst upload rate gets choked. If a client has
1855     * a complete file, it uses its upload rate rather than its download
1856     * rate to decide which peers to unchoke.
1857     */
1858    unchokedInterested = 0;
1859    for( i = 0; i < size && unchokedInterested < MAX_UNCHOKED_PEERS; ++i )
1860    {
1861        choke[i].doUnchoke = 1;
1862        if( choke[i].isInterested )
1863            ++unchokedInterested;
1864    }
1865
1866    /* optimistic unchoke */
1867    if( i < size )
1868    {
1869        int                n;
1870        struct ChokeData * c;
1871        tr_ptrArray *      randPool = tr_ptrArrayNew( );
1872
1873        for( ; i < size; ++i )
1874        {
1875            if( choke[i].isInterested )
1876            {
1877                const tr_peer * peer = choke[i].peer;
1878                int             x = 1, y;
1879                if( isNew( peer ) ) x *= 3;
1880                if( isSame( peer ) ) x *= 3;
1881                for( y = 0; y < x; ++y )
1882                    tr_ptrArrayAppend( randPool, &choke[i] );
1883            }
1884        }
1885
1886        if( ( n = tr_ptrArraySize( randPool ) ) )
1887        {
1888            c = tr_ptrArrayNth( randPool, tr_cryptoWeakRandInt( n ) );
1889            c->doUnchoke = 1;
1890            t->optimistic = c->peer;
1891        }
1892
1893        tr_ptrArrayFree( randPool, NULL );
1894    }
1895
1896    for( i = 0; i < size; ++i )
1897        tr_peerMsgsSetChoke( choke[i].peer->msgs, !choke[i].doUnchoke );
1898
1899    /* cleanup */
1900    tr_free( choke );
1901    tr_free( peers );
1902}
1903
1904static int
1905rechokePulse( void * vtorrent )
1906{
1907    Torrent * t = vtorrent;
1908
1909    torrentLock( t );
1910    rechoke( t );
1911    torrentUnlock( t );
1912    return TRUE;
1913}
1914
1915/***
1916****
1917****  Life and Death
1918****
1919***/
1920
1921static int
1922shouldPeerBeClosed( const Torrent * t,
1923                    const tr_peer * peer,
1924                    int             peerCount )
1925{
1926    const tr_torrent *       tor = t->tor;
1927    const time_t             now = time( NULL );
1928    const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1929
1930    /* if it's marked for purging, close it */
1931    if( peer->doPurge )
1932    {
1933        tordbg( t, "purging peer %s because its doPurge flag is set",
1934               tr_peerIoAddrStr( &atom->addr,
1935                                 atom->port ) );
1936        return TRUE;
1937    }
1938
1939    /* if we're seeding and the peer has everything we have,
1940     * and enough time has passed for a pex exchange, then disconnect */
1941    if( tr_torrentIsSeed( tor ) )
1942    {
1943        int peerHasEverything;
1944        if( atom->flags & ADDED_F_SEED_FLAG )
1945            peerHasEverything = TRUE;
1946        else if( peer->progress < tr_cpPercentDone( tor->completion ) )
1947            peerHasEverything = FALSE;
1948        else
1949        {
1950            tr_bitfield * tmp =
1951                tr_bitfieldDup( tr_cpPieceBitfield( tor->completion ) );
1952            tr_bitfieldDifference( tmp, peer->have );
1953            peerHasEverything = tr_bitfieldCountTrueBits( tmp ) == 0;
1954            tr_bitfieldFree( tmp );
1955        }
1956        if( peerHasEverything
1957          && ( !tr_torrentAllowsPex( tor ) || ( now - atom->time >= 30 ) ) )
1958        {
1959            tordbg( t, "purging peer %s because we're both seeds",
1960                   tr_peerIoAddrStr( &atom->addr,
1961                                     atom->port ) );
1962            return TRUE;
1963        }
1964    }
1965
1966    /* disconnect if it's been too long since piece data has been transferred.
1967     * this is on a sliding scale based on number of available peers... */
1968    {
1969        const int    relaxStrictnessIfFewerThanN =
1970            (int)( ( getMaxPeerCount( tor ) * 0.9 ) + 0.5 );
1971        /* if we have >= relaxIfFewerThan, strictness is 100%.
1972         * if we have zero connections, strictness is 0% */
1973        const float  strictness = peerCount >= relaxStrictnessIfFewerThanN
1974                                  ? 1.0
1975                                  : peerCount /
1976                                  (float)relaxStrictnessIfFewerThanN;
1977        const int    lo = MIN_UPLOAD_IDLE_SECS;
1978        const int    hi = MAX_UPLOAD_IDLE_SECS;
1979        const int    limit = lo + ( ( hi - lo ) * strictness );
1980        const time_t then = peer->pieceDataActivityDate;
1981        const int    idleTime = then ? ( now - then ) : 0;
1982        if( idleTime > limit )
1983        {
1984            tordbg(
1985                t,
1986                "purging peer %s because it's been %d secs since we shared anything",
1987                tr_peerIoAddrStr( &atom->addr, atom->port ), idleTime );
1988            return TRUE;
1989        }
1990    }
1991
1992    return FALSE;
1993}
1994
1995static tr_peer **
1996getPeersToClose( Torrent * t,
1997                 int *     setmeSize )
1998{
1999    int               i, peerCount, outsize;
2000    tr_peer **        peers = (tr_peer**) tr_ptrArrayPeek( t->peers,
2001                                                           &peerCount );
2002    struct tr_peer ** ret = tr_new( tr_peer *, peerCount );
2003
2004    assert( torrentIsLocked( t ) );
2005
2006    for( i = outsize = 0; i < peerCount; ++i )
2007        if( shouldPeerBeClosed( t, peers[i], peerCount ) )
2008            ret[outsize++] = peers[i];
2009
2010    *setmeSize = outsize;
2011    return ret;
2012}
2013
2014static int
2015compareCandidates( const void * va,
2016                   const void * vb )
2017{
2018    const struct peer_atom * a = *(const struct peer_atom**) va;
2019    const struct peer_atom * b = *(const struct peer_atom**) vb;
2020
2021    /* <Charles> Here we would probably want to try reconnecting to
2022     * peers that had most recently given us data. Lots of users have
2023     * trouble with resets due to their routers and/or ISPs. This way we
2024     * can quickly recover from an unwanted reset. So we sort
2025     * piece_data_time in descending order.
2026     */
2027
2028    if( a->piece_data_time != b->piece_data_time )
2029        return a->piece_data_time < b->piece_data_time ? 1 : -1;
2030
2031    if( a->numFails != b->numFails )
2032        return a->numFails < b->numFails ? -1 : 1;
2033
2034    if( a->time != b->time )
2035        return a->time < b->time ? -1 : 1;
2036
2037    return 0;
2038}
2039
2040static int
2041getReconnectIntervalSecs( const struct peer_atom * atom )
2042{
2043    int          sec;
2044    const time_t now = time( NULL );
2045
2046    /* if we were recently connected to this peer and transferring piece
2047     * data, try to reconnect to them sooner rather that later -- we don't
2048     * want network troubles to get in the way of a good peer. */
2049    if( ( now - atom->piece_data_time ) <=
2050       ( MINIMUM_RECONNECT_INTERVAL_SECS * 2 ) )
2051        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2052
2053    /* don't allow reconnects more often than our minimum */
2054    else if( ( now - atom->time ) < MINIMUM_RECONNECT_INTERVAL_SECS )
2055        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2056
2057    /* otherwise, the interval depends on how many times we've tried
2058     * and failed to connect to the peer */
2059    else switch( atom->numFails )
2060        {
2061            case 0:
2062                sec = 0; break;
2063
2064            case 1:
2065                sec = 5; break;
2066
2067            case 2:
2068                sec = 2 * 60; break;
2069
2070            case 3:
2071                sec = 15 * 60; break;
2072
2073            case 4:
2074                sec = 30 * 60; break;
2075
2076            case 5:
2077                sec = 60 * 60; break;
2078
2079            default:
2080                sec = 120 * 60; break;
2081        }
2082
2083    return sec;
2084}
2085
2086static struct peer_atom **
2087getPeerCandidates(                               Torrent * t,
2088                                           int * setmeSize )
2089{
2090    int                 i, atomCount, retCount;
2091    struct peer_atom ** atoms;
2092    struct peer_atom ** ret;
2093    const time_t        now = time( NULL );
2094    const int           seed = tr_torrentIsSeed( t->tor );
2095
2096    assert( torrentIsLocked( t ) );
2097
2098    atoms = (struct peer_atom**) tr_ptrArrayPeek( t->pool, &atomCount );
2099    ret = tr_new( struct peer_atom*, atomCount );
2100    for( i = retCount = 0; i < atomCount; ++i )
2101    {
2102        int                interval;
2103        struct peer_atom * atom = atoms[i];
2104
2105        /* peer fed us too much bad data ... we only keep it around
2106         * now to weed it out in case someone sends it to us via pex */
2107        if( atom->myflags & MYFLAG_BANNED )
2108            continue;
2109
2110        /* peer was unconnectable before, so we're not going to keep trying.
2111         * this is needs a separate flag from `banned', since if they try
2112         * to connect to us later, we'll let them in */
2113        if( atom->myflags & MYFLAG_UNREACHABLE )
2114            continue;
2115
2116        /* we don't need two connections to the same peer... */
2117        if( peerIsInUse( t, &atom->addr ) )
2118            continue;
2119
2120        /* no need to connect if we're both seeds... */
2121        if( seed && ( atom->flags & ADDED_F_SEED_FLAG ) )
2122            continue;
2123
2124        /* don't reconnect too often */
2125        interval = getReconnectIntervalSecs( atom );
2126        if( ( now - atom->time ) < interval )
2127        {
2128            tordbg(
2129                t,
2130                "RECONNECT peer %d (%s) is in its grace period of %d seconds..",
2131                i, tr_peerIoAddrStr( &atom->addr,
2132                                     atom->port ), interval );
2133            continue;
2134        }
2135
2136        /* Don't connect to peers in our blocklist */
2137        if( tr_sessionIsAddressBlocked( t->manager->session, &atom->addr ) )
2138            continue;
2139
2140        ret[retCount++] = atom;
2141    }
2142
2143    qsort( ret, retCount, sizeof( struct peer_atom* ), compareCandidates );
2144    *setmeSize = retCount;
2145    return ret;
2146}
2147
2148static int
2149reconnectPulse( void * vtorrent )
2150{
2151    Torrent *     t = vtorrent;
2152    static time_t prevTime = 0;
2153    static int    newConnectionsThisSecond = 0;
2154    time_t        now;
2155
2156    torrentLock( t );
2157
2158    now = time( NULL );
2159    if( prevTime != now )
2160    {
2161        prevTime = now;
2162        newConnectionsThisSecond = 0;
2163    }
2164
2165    if( !t->isRunning )
2166    {
2167        removeAllPeers( t );
2168    }
2169    else
2170    {
2171        int                 i, nCandidates, nBad;
2172        struct peer_atom ** candidates = getPeerCandidates( t, &nCandidates );
2173        struct tr_peer **   connections = getPeersToClose( t, &nBad );
2174
2175        if( nBad || nCandidates )
2176            tordbg(
2177                t, "reconnect pulse for [%s]: %d bad connections, "
2178                   "%d connection candidates, %d atoms, max per pulse is %d",
2179                t->tor->info.name, nBad, nCandidates,
2180                tr_ptrArraySize( t->pool ),
2181                (int)MAX_RECONNECTIONS_PER_PULSE );
2182
2183        /* disconnect some peers.
2184           if we transferred piece data, then they might be good peers,
2185           so reset their `numFails' weight to zero.  otherwise we connected
2186           to them fruitlessly, so mark it as another fail */
2187        for( i = 0; i < nBad; ++i )
2188        {
2189            tr_peer *          peer = connections[i];
2190            struct peer_atom * atom = getExistingAtom( t, &peer->addr );
2191            if( peer->pieceDataActivityDate )
2192                atom->numFails = 0;
2193            else
2194                ++atom->numFails;
2195            tordbg( t, "removing bad peer %s",
2196                   tr_peerIoGetAddrStr( peer->io ) );
2197            removePeer( t, peer );
2198        }
2199
2200        /* add some new ones */
2201        for( i = 0;    ( i < nCandidates )
2202           && ( i < MAX_RECONNECTIONS_PER_PULSE )
2203           && ( getPeerCount( t ) < getMaxPeerCount( t->tor ) )
2204           && ( newConnectionsThisSecond < MAX_CONNECTIONS_PER_SECOND );
2205             ++i )
2206        {
2207            tr_peerMgr *       mgr = t->manager;
2208            struct peer_atom * atom = candidates[i];
2209            tr_peerIo *        io;
2210
2211            tordbg( t, "Starting an OUTGOING connection with %s",
2212                   tr_peerIoAddrStr( &atom->addr, atom->port ) );
2213
2214            io =
2215                tr_peerIoNewOutgoing( mgr->session, &atom->addr, atom->port,
2216                                      t->hash );
2217            if( io == NULL )
2218            {
2219                atom->myflags |= MYFLAG_UNREACHABLE;
2220            }
2221            else
2222            {
2223                tr_handshake * handshake = tr_handshakeNew(
2224                    io,
2225                    mgr->session->
2226                    encryptionMode,
2227                    myHandshakeDoneCB,
2228                    mgr );
2229
2230                assert( tr_peerIoGetTorrentHash( io ) );
2231
2232                ++newConnectionsThisSecond;
2233
2234                tr_ptrArrayInsertSorted( t->outgoingHandshakes, handshake,
2235                                         handshakeCompare );
2236            }
2237
2238            atom->time = time( NULL );
2239        }
2240
2241        /* cleanup */
2242        tr_free( connections );
2243        tr_free( candidates );
2244    }
2245
2246    torrentUnlock( t );
2247    return TRUE;
2248}
2249
2250/****
2251*****
2252*****  BANDWIDTH ALLOCATION
2253*****
2254****/
2255
2256static void
2257pumpAllPeers( tr_peerMgr * mgr )
2258{
2259    const int torrentCount = tr_ptrArraySize( mgr->torrents );
2260    int       i, j;
2261
2262    for( i=0; i<torrentCount; ++i )
2263    {
2264        Torrent * t = tr_ptrArrayNth( mgr->torrents, i );
2265        for( j=0; j<tr_ptrArraySize( t->peers ); ++j )
2266        {
2267            tr_peer * peer = tr_ptrArrayNth( t->peers, j );
2268            tr_peerMsgsPulse( peer->msgs );
2269        }
2270    }
2271}
2272
2273static int
2274bandwidthPulse( void * vmgr )
2275{
2276    tr_peerMgr * mgr = vmgr;
2277    managerLock( mgr );
2278
2279    pumpAllPeers( mgr );
2280    tr_bandwidthAllocate( mgr->session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC );
2281    tr_bandwidthAllocate( mgr->session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC );
2282    pumpAllPeers( mgr );
2283
2284    managerUnlock( mgr );
2285    return TRUE;
2286}
Note: See TracBrowser for help on using the repository browser.