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

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

(libT) patch from jhujhiti to add ipv6 support.

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