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

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

#1379 lay some very early groundwork for "partial seeds" -- change tr_completion's enum to use the partial seed terminology

  • 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 7199 2008-11-30 01:52:45Z 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                isSeed;
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    isSeed = tor && ( tr_cpGetStatus ( tor->completion ) == TR_SEED );
1553    peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
1554
1555    memset( tab, 0, tabCount );
1556
1557    for( i = 0; tor && i < tabCount; ++i )
1558    {
1559        const int piece = i * interval;
1560
1561        if( isSeed || tr_cpPieceIsComplete( tor->completion, piece ) )
1562            tab[i] = -1;
1563        else if( peerCount )
1564        {
1565            int j;
1566            for( j = 0; j < peerCount; ++j )
1567                if( tr_bitfieldHas( peers[j]->have, i ) )
1568                    ++tab[i];
1569        }
1570    }
1571
1572    managerUnlock( manager );
1573}
1574
1575/* Returns the pieces that are available from peers */
1576tr_bitfield*
1577tr_peerMgrGetAvailable( const tr_peerMgr * manager,
1578                        const uint8_t *    torrentHash )
1579{
1580    int           i, size;
1581    Torrent *     t;
1582    tr_peer **    peers;
1583    tr_bitfield * pieces;
1584
1585    managerLock( manager );
1586
1587    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1588    pieces = tr_bitfieldNew( t->tor->info.pieceCount );
1589    peers = getConnectedPeers( t, &size );
1590    for( i = 0; i < size; ++i )
1591        tr_bitfieldOr( pieces, peers[i]->have );
1592
1593    managerUnlock( manager );
1594    tr_free( peers );
1595    return pieces;
1596}
1597
1598int
1599tr_peerMgrHasConnections( const tr_peerMgr * manager,
1600                          const uint8_t *    torrentHash )
1601{
1602    int             ret;
1603    const Torrent * t;
1604
1605    managerLock( manager );
1606
1607    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1608    ret = t && ( !tr_ptrArrayEmpty( t->peers )
1609               || !tr_ptrArrayEmpty( t->webseeds ) );
1610
1611    managerUnlock( manager );
1612    return ret;
1613}
1614
1615void
1616tr_peerMgrTorrentStats( const tr_peerMgr * manager,
1617                        const uint8_t *    torrentHash,
1618                        int *              setmePeersKnown,
1619                        int *              setmePeersConnected,
1620                        int *              setmeSeedsConnected,
1621                        int *              setmeWebseedsSendingToUs,
1622                        int *              setmePeersSendingToUs,
1623                        int *              setmePeersGettingFromUs,
1624                        int *              setmePeersFrom )
1625{
1626    int                 i, size;
1627    const Torrent *     t;
1628    const tr_peer **    peers;
1629    const tr_webseed ** webseeds;
1630
1631    managerLock( manager );
1632
1633    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1634    peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
1635
1636    *setmePeersKnown           = tr_ptrArraySize( t->pool );
1637    *setmePeersConnected       = 0;
1638    *setmeSeedsConnected       = 0;
1639    *setmePeersGettingFromUs   = 0;
1640    *setmePeersSendingToUs     = 0;
1641    *setmeWebseedsSendingToUs  = 0;
1642
1643    for( i = 0; i < TR_PEER_FROM__MAX; ++i )
1644        setmePeersFrom[i] = 0;
1645
1646    for( i = 0; i < size; ++i )
1647    {
1648        const tr_peer *          peer = peers[i];
1649        const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1650
1651        if( peer->io == NULL ) /* not connected */
1652            continue;
1653
1654        ++ * setmePeersConnected;
1655
1656        ++setmePeersFrom[atom->from];
1657
1658        if( clientIsDownloadingFrom( peer ) )
1659            ++ * setmePeersSendingToUs;
1660
1661        if( clientIsUploadingTo( peer ) )
1662            ++ * setmePeersGettingFromUs;
1663
1664        if( atom->flags & ADDED_F_SEED_FLAG )
1665            ++ * setmeSeedsConnected;
1666    }
1667
1668    webseeds = (const tr_webseed **) tr_ptrArrayPeek( t->webseeds, &size );
1669    for( i = 0; i < size; ++i )
1670    {
1671        if( tr_webseedIsActive( webseeds[i] ) )
1672            ++ * setmeWebseedsSendingToUs;
1673    }
1674
1675    managerUnlock( manager );
1676}
1677
1678float*
1679tr_peerMgrWebSpeeds( const tr_peerMgr * manager,
1680                     const uint8_t *    torrentHash )
1681{
1682    const Torrent *     t;
1683    const tr_webseed ** webseeds;
1684    int                 i;
1685    int                 webseedCount;
1686    float *             ret;
1687
1688    assert( manager );
1689    managerLock( manager );
1690
1691    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1692    webseeds = (const tr_webseed**) tr_ptrArrayPeek( t->webseeds,
1693                                                     &webseedCount );
1694    assert( webseedCount == t->tor->info.webseedCount );
1695    ret = tr_new0( float, webseedCount );
1696
1697    for( i = 0; i < webseedCount; ++i )
1698        if( !tr_webseedGetSpeed( webseeds[i], &ret[i] ) )
1699            ret[i] = -1.0;
1700
1701    managerUnlock( manager );
1702    return ret;
1703}
1704
1705double
1706tr_peerGetPieceSpeed( const tr_peer    * peer,
1707                      tr_direction       direction )
1708{
1709    assert( peer );
1710    assert( direction==TR_CLIENT_TO_PEER || direction==TR_PEER_TO_CLIENT );
1711
1712    return tr_bandwidthGetPieceSpeed( peer->bandwidth, direction );
1713}
1714
1715
1716struct tr_peer_stat *
1717tr_peerMgrPeerStats( const   tr_peerMgr  * manager,
1718                     const   uint8_t     * torrentHash,
1719                     int                 * setmeCount UNUSED )
1720{
1721    int             i, size;
1722    const Torrent * t;
1723    tr_peer **      peers;
1724    tr_peer_stat *  ret;
1725
1726    assert( manager );
1727    managerLock( manager );
1728
1729    t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
1730    peers = getConnectedPeers( (Torrent*)t, &size );
1731    ret = tr_new0( tr_peer_stat, size );
1732
1733    for( i = 0; i < size; ++i )
1734    {
1735        char *                   pch;
1736        const tr_peer *          peer = peers[i];
1737        const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1738        tr_peer_stat *           stat = ret + i;
1739
1740        tr_ntop( &peer->addr, stat->addr, sizeof( stat->addr ) );
1741        tr_strlcpy( stat->client, ( peer->client ? peer->client : "" ),
1742                   sizeof( stat->client ) );
1743        stat->port               = ntohs( peer->port );
1744        stat->from               = atom->from;
1745        stat->progress           = peer->progress;
1746        stat->isEncrypted        = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
1747        stat->rateToPeer         = tr_peerGetPieceSpeed( peer, TR_CLIENT_TO_PEER );
1748        stat->rateToClient       = tr_peerGetPieceSpeed( peer, TR_PEER_TO_CLIENT );
1749        stat->peerIsChoked       = peer->peerIsChoked;
1750        stat->peerIsInterested   = peer->peerIsInterested;
1751        stat->clientIsChoked     = peer->clientIsChoked;
1752        stat->clientIsInterested = peer->clientIsInterested;
1753        stat->isIncoming         = tr_peerIoIsIncoming( peer->io );
1754        stat->isDownloadingFrom  = clientIsDownloadingFrom( peer );
1755        stat->isUploadingTo      = clientIsUploadingTo( peer );
1756
1757        pch = stat->flagStr;
1758        if( t->optimistic == peer ) *pch++ = 'O';
1759        if( stat->isDownloadingFrom ) *pch++ = 'D';
1760        else if( stat->clientIsInterested ) *pch++ = 'd';
1761        if( stat->isUploadingTo ) *pch++ = 'U';
1762        else if( stat->peerIsInterested ) *pch++ = 'u';
1763        if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ =
1764                'K';
1765        if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
1766        if( stat->isEncrypted ) *pch++ = 'E';
1767        if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
1768        if( stat->isIncoming ) *pch++ = 'I';
1769        *pch = '\0';
1770    }
1771
1772    *setmeCount = size;
1773    tr_free( peers );
1774
1775    managerUnlock( manager );
1776    return ret;
1777}
1778
1779/**
1780***
1781**/
1782
1783struct ChokeData
1784{
1785    tr_bool         doUnchoke;
1786    tr_bool         isInterested;
1787    tr_bool         isChoked;
1788    int             rate;
1789    tr_peer *       peer;
1790};
1791
1792static int
1793compareChoke( const void * va,
1794              const void * vb )
1795{
1796    const struct ChokeData * a = va;
1797    const struct ChokeData * b = vb;
1798
1799    if( a->rate != b->rate ) /* prefer higher overall speeds */
1800        return a->rate > b->rate ? -1 : 1;
1801
1802    if( a->isChoked != b->isChoked ) /* prefer unchoked */
1803        return a->isChoked ? 1 : -1;
1804
1805    return 0;
1806}
1807
1808static int
1809isNew( const tr_peer * peer )
1810{
1811    return peer && peer->io && tr_peerIoGetAge( peer->io ) < 45;
1812}
1813
1814static int
1815isSame( const tr_peer * peer )
1816{
1817    return peer && peer->client && strstr( peer->client, "Transmission" );
1818}
1819
1820/**
1821***
1822**/
1823
1824static void
1825rechoke( Torrent * t )
1826{
1827    int                i, peerCount, size, unchokedInterested;
1828    tr_peer **         peers = getConnectedPeers( t, &peerCount );
1829    struct ChokeData * choke = tr_new0( struct ChokeData, peerCount );
1830    const int          chokeAll = !tr_torrentIsPieceTransferAllowed( t->tor, TR_CLIENT_TO_PEER );
1831
1832    assert( torrentIsLocked( t ) );
1833
1834    /* sort the peers by preference and rate */
1835    for( i = 0, size = 0; i < peerCount; ++i )
1836    {
1837        tr_peer * peer = peers[i];
1838        if( peer->progress >= 1.0 ) /* choke all seeds */
1839            tr_peerMsgsSetChoke( peer->msgs, TRUE );
1840        else if( chokeAll )
1841            tr_peerMsgsSetChoke( peer->msgs, TRUE );
1842        else {
1843            struct ChokeData * n = &choke[size++];
1844            n->peer         = peer;
1845            n->isInterested = peer->peerIsInterested;
1846            n->isChoked     = peer->peerIsChoked;
1847            n->rate         = tr_peerGetPieceSpeed( peer, TR_CLIENT_TO_PEER ) * 1024;
1848        }
1849    }
1850
1851    qsort( choke, size, sizeof( struct ChokeData ), compareChoke );
1852
1853    /**
1854     * Reciprocation and number of uploads capping is managed by unchoking
1855     * the N peers which have the best upload rate and are interested.
1856     * This maximizes the client's download rate. These N peers are
1857     * referred to as downloaders, because they are interested in downloading
1858     * from the client.
1859     *
1860     * Peers which have a better upload rate (as compared to the downloaders)
1861     * but aren't interested get unchoked. If they become interested, the
1862     * downloader with the worst upload rate gets choked. If a client has
1863     * a complete file, it uses its upload rate rather than its download
1864     * rate to decide which peers to unchoke.
1865     */
1866    unchokedInterested = 0;
1867    for( i = 0; i < size && unchokedInterested < MAX_UNCHOKED_PEERS; ++i )
1868    {
1869        choke[i].doUnchoke = 1;
1870        if( choke[i].isInterested )
1871            ++unchokedInterested;
1872    }
1873
1874    /* optimistic unchoke */
1875    if( i < size )
1876    {
1877        int                n;
1878        struct ChokeData * c;
1879        tr_ptrArray *      randPool = tr_ptrArrayNew( );
1880
1881        for( ; i < size; ++i )
1882        {
1883            if( choke[i].isInterested )
1884            {
1885                const tr_peer * peer = choke[i].peer;
1886                int             x = 1, y;
1887                if( isNew( peer ) ) x *= 3;
1888                if( isSame( peer ) ) x *= 3;
1889                for( y = 0; y < x; ++y )
1890                    tr_ptrArrayAppend( randPool, &choke[i] );
1891            }
1892        }
1893
1894        if( ( n = tr_ptrArraySize( randPool ) ) )
1895        {
1896            c = tr_ptrArrayNth( randPool, tr_cryptoWeakRandInt( n ) );
1897            c->doUnchoke = 1;
1898            t->optimistic = c->peer;
1899        }
1900
1901        tr_ptrArrayFree( randPool, NULL );
1902    }
1903
1904    for( i = 0; i < size; ++i )
1905        tr_peerMsgsSetChoke( choke[i].peer->msgs, !choke[i].doUnchoke );
1906
1907    /* cleanup */
1908    tr_free( choke );
1909    tr_free( peers );
1910}
1911
1912static int
1913rechokePulse( void * vtorrent )
1914{
1915    Torrent * t = vtorrent;
1916
1917    torrentLock( t );
1918    rechoke( t );
1919    torrentUnlock( t );
1920    return TRUE;
1921}
1922
1923/***
1924****
1925****  Life and Death
1926****
1927***/
1928
1929static int
1930shouldPeerBeClosed( const Torrent * t,
1931                    const tr_peer * peer,
1932                    int             peerCount )
1933{
1934    const tr_torrent *       tor = t->tor;
1935    const time_t             now = time( NULL );
1936    const struct peer_atom * atom = getExistingAtom( t, &peer->addr );
1937
1938    /* if it's marked for purging, close it */
1939    if( peer->doPurge )
1940    {
1941        tordbg( t, "purging peer %s because its doPurge flag is set",
1942               tr_peerIoAddrStr( &atom->addr,
1943                                 atom->port ) );
1944        return TRUE;
1945    }
1946
1947    /* if we're seeding and the peer has everything we have,
1948     * and enough time has passed for a pex exchange, then disconnect */
1949    if( tr_torrentIsSeed( tor ) )
1950    {
1951        int peerHasEverything;
1952        if( atom->flags & ADDED_F_SEED_FLAG )
1953            peerHasEverything = TRUE;
1954        else if( peer->progress < tr_cpPercentDone( tor->completion ) )
1955            peerHasEverything = FALSE;
1956        else
1957        {
1958            tr_bitfield * tmp =
1959                tr_bitfieldDup( tr_cpPieceBitfield( tor->completion ) );
1960            tr_bitfieldDifference( tmp, peer->have );
1961            peerHasEverything = tr_bitfieldCountTrueBits( tmp ) == 0;
1962            tr_bitfieldFree( tmp );
1963        }
1964        if( peerHasEverything
1965          && ( !tr_torrentAllowsPex( tor ) || ( now - atom->time >= 30 ) ) )
1966        {
1967            tordbg( t, "purging peer %s because we're both seeds",
1968                   tr_peerIoAddrStr( &atom->addr,
1969                                     atom->port ) );
1970            return TRUE;
1971        }
1972    }
1973
1974    /* disconnect if it's been too long since piece data has been transferred.
1975     * this is on a sliding scale based on number of available peers... */
1976    {
1977        const int    relaxStrictnessIfFewerThanN =
1978            (int)( ( getMaxPeerCount( tor ) * 0.9 ) + 0.5 );
1979        /* if we have >= relaxIfFewerThan, strictness is 100%.
1980         * if we have zero connections, strictness is 0% */
1981        const float  strictness = peerCount >= relaxStrictnessIfFewerThanN
1982                                  ? 1.0
1983                                  : peerCount /
1984                                  (float)relaxStrictnessIfFewerThanN;
1985        const int    lo = MIN_UPLOAD_IDLE_SECS;
1986        const int    hi = MAX_UPLOAD_IDLE_SECS;
1987        const int    limit = lo + ( ( hi - lo ) * strictness );
1988        const time_t then = peer->pieceDataActivityDate;
1989        const int    idleTime = then ? ( now - then ) : 0;
1990        if( idleTime > limit )
1991        {
1992            tordbg(
1993                t,
1994                "purging peer %s because it's been %d secs since we shared anything",
1995                tr_peerIoAddrStr( &atom->addr, atom->port ), idleTime );
1996            return TRUE;
1997        }
1998    }
1999
2000    return FALSE;
2001}
2002
2003static tr_peer **
2004getPeersToClose( Torrent * t,
2005                 int *     setmeSize )
2006{
2007    int               i, peerCount, outsize;
2008    tr_peer **        peers = (tr_peer**) tr_ptrArrayPeek( t->peers,
2009                                                           &peerCount );
2010    struct tr_peer ** ret = tr_new( tr_peer *, peerCount );
2011
2012    assert( torrentIsLocked( t ) );
2013
2014    for( i = outsize = 0; i < peerCount; ++i )
2015        if( shouldPeerBeClosed( t, peers[i], peerCount ) )
2016            ret[outsize++] = peers[i];
2017
2018    *setmeSize = outsize;
2019    return ret;
2020}
2021
2022static int
2023compareCandidates( const void * va,
2024                   const void * vb )
2025{
2026    const struct peer_atom * a = *(const struct peer_atom**) va;
2027    const struct peer_atom * b = *(const struct peer_atom**) vb;
2028
2029    /* <Charles> Here we would probably want to try reconnecting to
2030     * peers that had most recently given us data. Lots of users have
2031     * trouble with resets due to their routers and/or ISPs. This way we
2032     * can quickly recover from an unwanted reset. So we sort
2033     * piece_data_time in descending order.
2034     */
2035
2036    if( a->piece_data_time != b->piece_data_time )
2037        return a->piece_data_time < b->piece_data_time ? 1 : -1;
2038
2039    if( a->numFails != b->numFails )
2040        return a->numFails < b->numFails ? -1 : 1;
2041
2042    if( a->time != b->time )
2043        return a->time < b->time ? -1 : 1;
2044
2045    return 0;
2046}
2047
2048static int
2049getReconnectIntervalSecs( const struct peer_atom * atom )
2050{
2051    int          sec;
2052    const time_t now = time( NULL );
2053
2054    /* if we were recently connected to this peer and transferring piece
2055     * data, try to reconnect to them sooner rather that later -- we don't
2056     * want network troubles to get in the way of a good peer. */
2057    if( ( now - atom->piece_data_time ) <=
2058       ( MINIMUM_RECONNECT_INTERVAL_SECS * 2 ) )
2059        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2060
2061    /* don't allow reconnects more often than our minimum */
2062    else if( ( now - atom->time ) < MINIMUM_RECONNECT_INTERVAL_SECS )
2063        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2064
2065    /* otherwise, the interval depends on how many times we've tried
2066     * and failed to connect to the peer */
2067    else switch( atom->numFails )
2068        {
2069            case 0:
2070                sec = 0; break;
2071
2072            case 1:
2073                sec = 5; break;
2074
2075            case 2:
2076                sec = 2 * 60; break;
2077
2078            case 3:
2079                sec = 15 * 60; break;
2080
2081            case 4:
2082                sec = 30 * 60; break;
2083
2084            case 5:
2085                sec = 60 * 60; break;
2086
2087            default:
2088                sec = 120 * 60; break;
2089        }
2090
2091    return sec;
2092}
2093
2094static struct peer_atom **
2095getPeerCandidates(                               Torrent * t,
2096                                           int * setmeSize )
2097{
2098    int                 i, atomCount, retCount;
2099    struct peer_atom ** atoms;
2100    struct peer_atom ** ret;
2101    const time_t        now = time( NULL );
2102    const int           seed = tr_torrentIsSeed( t->tor );
2103
2104    assert( torrentIsLocked( t ) );
2105
2106    atoms = (struct peer_atom**) tr_ptrArrayPeek( t->pool, &atomCount );
2107    ret = tr_new( struct peer_atom*, atomCount );
2108    for( i = retCount = 0; i < atomCount; ++i )
2109    {
2110        int                interval;
2111        struct peer_atom * atom = atoms[i];
2112
2113        /* peer fed us too much bad data ... we only keep it around
2114         * now to weed it out in case someone sends it to us via pex */
2115        if( atom->myflags & MYFLAG_BANNED )
2116            continue;
2117
2118        /* peer was unconnectable before, so we're not going to keep trying.
2119         * this is needs a separate flag from `banned', since if they try
2120         * to connect to us later, we'll let them in */
2121        if( atom->myflags & MYFLAG_UNREACHABLE )
2122            continue;
2123
2124        /* we don't need two connections to the same peer... */
2125        if( peerIsInUse( t, &atom->addr ) )
2126            continue;
2127
2128        /* no need to connect if we're both seeds... */
2129        if( seed && ( atom->flags & ADDED_F_SEED_FLAG ) )
2130            continue;
2131
2132        /* don't reconnect too often */
2133        interval = getReconnectIntervalSecs( atom );
2134        if( ( now - atom->time ) < interval )
2135        {
2136            tordbg(
2137                t,
2138                "RECONNECT peer %d (%s) is in its grace period of %d seconds..",
2139                i, tr_peerIoAddrStr( &atom->addr,
2140                                     atom->port ), interval );
2141            continue;
2142        }
2143
2144        /* Don't connect to peers in our blocklist */
2145        if( tr_sessionIsAddressBlocked( t->manager->session, &atom->addr ) )
2146            continue;
2147
2148        ret[retCount++] = atom;
2149    }
2150
2151    qsort( ret, retCount, sizeof( struct peer_atom* ), compareCandidates );
2152    *setmeSize = retCount;
2153    return ret;
2154}
2155
2156static int
2157reconnectPulse( void * vtorrent )
2158{
2159    Torrent *     t = vtorrent;
2160    static time_t prevTime = 0;
2161    static int    newConnectionsThisSecond = 0;
2162    time_t        now;
2163
2164    torrentLock( t );
2165
2166    now = time( NULL );
2167    if( prevTime != now )
2168    {
2169        prevTime = now;
2170        newConnectionsThisSecond = 0;
2171    }
2172
2173    if( !t->isRunning )
2174    {
2175        removeAllPeers( t );
2176    }
2177    else
2178    {
2179        int                 i, nCandidates, nBad;
2180        struct peer_atom ** candidates = getPeerCandidates( t, &nCandidates );
2181        struct tr_peer **   connections = getPeersToClose( t, &nBad );
2182
2183        if( nBad || nCandidates )
2184            tordbg(
2185                t, "reconnect pulse for [%s]: %d bad connections, "
2186                   "%d connection candidates, %d atoms, max per pulse is %d",
2187                t->tor->info.name, nBad, nCandidates,
2188                tr_ptrArraySize( t->pool ),
2189                (int)MAX_RECONNECTIONS_PER_PULSE );
2190
2191        /* disconnect some peers.
2192           if we transferred piece data, then they might be good peers,
2193           so reset their `numFails' weight to zero.  otherwise we connected
2194           to them fruitlessly, so mark it as another fail */
2195        for( i = 0; i < nBad; ++i )
2196        {
2197            tr_peer *          peer = connections[i];
2198            struct peer_atom * atom = getExistingAtom( t, &peer->addr );
2199            if( peer->pieceDataActivityDate )
2200                atom->numFails = 0;
2201            else
2202                ++atom->numFails;
2203            tordbg( t, "removing bad peer %s",
2204                   tr_peerIoGetAddrStr( peer->io ) );
2205            removePeer( t, peer );
2206        }
2207
2208        /* add some new ones */
2209        for( i = 0;    ( i < nCandidates )
2210           && ( i < MAX_RECONNECTIONS_PER_PULSE )
2211           && ( getPeerCount( t ) < getMaxPeerCount( t->tor ) )
2212           && ( newConnectionsThisSecond < MAX_CONNECTIONS_PER_SECOND );
2213             ++i )
2214        {
2215            tr_peerMgr *       mgr = t->manager;
2216            struct peer_atom * atom = candidates[i];
2217            tr_peerIo *        io;
2218
2219            tordbg( t, "Starting an OUTGOING connection with %s",
2220                   tr_peerIoAddrStr( &atom->addr, atom->port ) );
2221
2222            io =
2223                tr_peerIoNewOutgoing( mgr->session, &atom->addr, atom->port,
2224                                      t->hash );
2225            if( io == NULL )
2226            {
2227                atom->myflags |= MYFLAG_UNREACHABLE;
2228            }
2229            else
2230            {
2231                tr_handshake * handshake = tr_handshakeNew(
2232                    io,
2233                    mgr->session->
2234                    encryptionMode,
2235                    myHandshakeDoneCB,
2236                    mgr );
2237
2238                assert( tr_peerIoGetTorrentHash( io ) );
2239
2240                ++newConnectionsThisSecond;
2241
2242                tr_ptrArrayInsertSorted( t->outgoingHandshakes, handshake,
2243                                         handshakeCompare );
2244            }
2245
2246            atom->time = time( NULL );
2247        }
2248
2249        /* cleanup */
2250        tr_free( connections );
2251        tr_free( candidates );
2252    }
2253
2254    torrentUnlock( t );
2255    return TRUE;
2256}
2257
2258/****
2259*****
2260*****  BANDWIDTH ALLOCATION
2261*****
2262****/
2263
2264static void
2265pumpAllPeers( tr_peerMgr * mgr )
2266{
2267    const int torrentCount = tr_ptrArraySize( mgr->torrents );
2268    int       i, j;
2269
2270    for( i=0; i<torrentCount; ++i )
2271    {
2272        Torrent * t = tr_ptrArrayNth( mgr->torrents, i );
2273        for( j=0; j<tr_ptrArraySize( t->peers ); ++j )
2274        {
2275            tr_peer * peer = tr_ptrArrayNth( t->peers, j );
2276            tr_peerMsgsPulse( peer->msgs );
2277        }
2278    }
2279}
2280
2281static int
2282bandwidthPulse( void * vmgr )
2283{
2284    tr_peerMgr * mgr = vmgr;
2285    managerLock( mgr );
2286
2287    pumpAllPeers( mgr );
2288    tr_bandwidthAllocate( mgr->session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC );
2289    tr_bandwidthAllocate( mgr->session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC );
2290    pumpAllPeers( mgr );
2291
2292    managerUnlock( mgr );
2293    return TRUE;
2294}
Note: See TracBrowser for help on using the repository browser.