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

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

(trunk libT) as long as I'm committing these CPU tweaks, might as well throw in #3289 too. To undo all this, we can revert to r10745

  • Property svn:keywords set to Date Rev Author Id
File size: 101.0 KB
Line 
1/*
2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
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 10751 2010-06-14 12:01:50Z charles $
11 */
12
13#include <assert.h>
14#include <string.h> /* memcpy, memcmp, strstr */
15#include <stdlib.h> /* qsort */
16
17#include <event.h>
18
19#include "transmission.h"
20#include "announcer.h"
21#include "bandwidth.h"
22#include "bencode.h"
23#include "blocklist.h"
24#include "clients.h"
25#include "completion.h"
26#include "crypto.h"
27#include "handshake.h"
28#include "inout.h" /* tr_ioTestPiece */
29#include "net.h"
30#include "peer-io.h"
31#include "peer-mgr.h"
32#include "peer-msgs.h"
33#include "ptrarray.h"
34#include "session.h"
35#include "stats.h" /* tr_statsAddUploaded, tr_statsAddDownloaded */
36#include "torrent.h"
37#include "utils.h"
38#include "webseed.h"
39
40enum
41{
42    /* how frequently to cull old atoms */
43    ATOM_PERIOD_MSEC = ( 60 * 1000 ),
44
45    /* how frequently to change which peers are choked */
46    RECHOKE_PERIOD_MSEC = ( 10 * 1000 ),
47
48    /* how frequently to reallocate bandwidth */
49    BANDWIDTH_PERIOD_MSEC = 500,
50
51    /* how frequently to age out old piece request lists */
52    REFILL_UPKEEP_PERIOD_MSEC = ( 10 * 1000 ),
53
54    /* how frequently to decide which peers live and die */
55    RECONNECT_PERIOD_MSEC = 500,
56
57    /* when many peers are available, keep idle ones this long */
58    MIN_UPLOAD_IDLE_SECS = ( 60 ),
59
60    /* when few peers are available, keep idle ones this long */
61    MAX_UPLOAD_IDLE_SECS = ( 60 * 5 ),
62
63    /* max number of peers to ask for per second overall.
64    * this throttle is to avoid overloading the router */
65    MAX_CONNECTIONS_PER_SECOND = 8,
66
67    MAX_CONNECTIONS_PER_PULSE = (int)(MAX_CONNECTIONS_PER_SECOND * (RECONNECT_PERIOD_MSEC/1000.0)),
68
69    /* number of bad pieces a peer is allowed to send before we ban them */
70    MAX_BAD_PIECES_PER_PEER = 5,
71
72    /* amount of time to keep a list of request pieces lying around
73       before it's considered too old and needs to be rebuilt */
74    PIECE_LIST_SHELF_LIFE_SECS = 60,
75
76    /* use for bitwise operations w/peer_atom.myflags */
77    MYFLAG_BANNED = 1,
78
79    /* use for bitwise operations w/peer_atom.myflags */
80    /* unreachable for now... but not banned.
81     * if they try to connect to us it's okay */
82    MYFLAG_UNREACHABLE = 2,
83
84    /* the minimum we'll wait before attempting to reconnect to a peer */
85    MINIMUM_RECONNECT_INTERVAL_SECS = 5,
86
87    /** how long we'll let requests we've made linger before we cancel them */
88    REQUEST_TTL_SECS = 120,
89
90    CANCEL_HISTORY_SEC = 120
91};
92
93
94/**
95***
96**/
97
98enum
99{
100    UPLOAD_ONLY_UKNOWN,
101    UPLOAD_ONLY_YES,
102    UPLOAD_ONLY_NO
103};
104
105/**
106 * Peer information that should be kept even before we've connected and
107 * after we've disconnected.  These are kept in a pool of peer_atoms to decide
108 * which ones would make good candidates for connecting to, and to watch out
109 * for banned peers.
110 *
111 * @see tr_peer
112 * @see tr_peermsgs
113 */
114struct peer_atom
115{
116    uint8_t     from;
117    uint8_t     flags;              /* these match the added_f flags */
118    uint8_t     myflags;            /* flags that aren't defined in added_f */
119    uint8_t     uploadOnly;         /* UPLOAD_ONLY_ */
120    int8_t      seedProbability;    /* how likely is this to be a seed... [0..100] or -1 for unknown */
121    int8_t      blocklisted;        /* -1 for unknown, TRUE for blocklisted, FALSE for not blocklisted */
122
123    tr_port     port;
124    uint16_t    numFails;
125    time_t      time;               /* when the peer's connection status last changed */
126    time_t      piece_data_time;
127
128    time_t      lastConnectionAttemptAt;
129    time_t      lastConnectionAt;
130
131    /* similar to a TTL field, but less rigid --
132     * if the swarm is small, the atom will be kept past this date. */
133    time_t      shelf_date;
134    tr_peer   * peer;               /* will be NULL if not connected */
135    tr_address  addr;
136};
137
138#ifdef NDEBUG
139#define tr_isAtom(a) (TRUE)
140#else
141static tr_bool
142tr_isAtom( const struct peer_atom * atom )
143{
144    return ( atom != NULL )
145        && ( atom->from < TR_PEER_FROM__MAX )
146        && ( tr_isAddress( &atom->addr ) );
147}
148#endif
149
150static const char*
151tr_atomAddrStr( const struct peer_atom * atom )
152{
153    return tr_peerIoAddrStr( &atom->addr, atom->port );
154}
155
156struct block_request
157{
158    tr_block_index_t block;
159    tr_peer * peer;
160    time_t sentAt;
161};
162
163struct weighted_piece
164{
165    tr_piece_index_t index;
166    int16_t salt;
167    int16_t requestCount;
168};
169
170/** @brief Opaque, per-torrent data structure for peer connection information */
171typedef struct tr_torrent_peers
172{
173    tr_ptrArray                outgoingHandshakes; /* tr_handshake */
174    tr_ptrArray                pool; /* struct peer_atom */
175    tr_ptrArray                peers; /* tr_peer */
176    tr_ptrArray                webseeds; /* tr_webseed */
177
178    tr_torrent               * tor;
179    tr_peer                  * optimistic; /* the optimistic peer, or NULL if none */
180    struct tr_peerMgr        * manager;
181
182    tr_bool                    isRunning;
183    tr_bool                    needsCompletenessCheck;
184
185    struct block_request     * requests;
186    int                        requestCount;
187    int                        requestAlloc;
188
189    struct weighted_piece    * pieces;
190    int                        pieceCount;
191
192    int                        interestedCount;
193
194    /* An arbitrary metric of how congested the downloads are.
195     * Based on how many of requests are cancelled and how many are completed.
196     * Lower values indicate less congestion. */
197    double                     cancelRate;
198}
199Torrent;
200
201struct tr_peerMgr
202{
203    tr_session    * session;
204    tr_ptrArray     incomingHandshakes; /* tr_handshake */
205    struct event  * bandwidthTimer;
206    struct event  * rechokeTimer;
207    struct event  * refillUpkeepTimer;
208    struct event  * atomTimer;
209};
210
211#define tordbg( t, ... ) \
212    do { \
213        if( tr_deepLoggingIsActive( ) ) \
214            tr_deepLog( __FILE__, __LINE__, tr_torrentName( t->tor ), __VA_ARGS__ ); \
215    } while( 0 )
216
217#define dbgmsg( ... ) \
218    do { \
219        if( tr_deepLoggingIsActive( ) ) \
220            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
221    } while( 0 )
222
223/**
224***
225**/
226
227static inline void
228managerLock( const struct tr_peerMgr * manager )
229{
230    tr_sessionLock( manager->session );
231}
232
233static inline void
234managerUnlock( const struct tr_peerMgr * manager )
235{
236    tr_sessionUnlock( manager->session );
237}
238
239static inline void
240torrentLock( Torrent * torrent )
241{
242    managerLock( torrent->manager );
243}
244
245static inline void
246torrentUnlock( Torrent * torrent )
247{
248    managerUnlock( torrent->manager );
249}
250
251static inline int
252torrentIsLocked( const Torrent * t )
253{
254    return tr_sessionIsLocked( t->manager->session );
255}
256
257/**
258***
259**/
260
261static int
262handshakeCompareToAddr( const void * va, const void * vb )
263{
264    const tr_handshake * a = va;
265
266    return tr_compareAddresses( tr_handshakeGetAddr( a, NULL ), vb );
267}
268
269static int
270handshakeCompare( const void * a, const void * b )
271{
272    return handshakeCompareToAddr( a, tr_handshakeGetAddr( b, NULL ) );
273}
274
275static inline tr_handshake*
276getExistingHandshake( tr_ptrArray * handshakes, const tr_address * addr )
277{
278    if( tr_ptrArrayEmpty( handshakes ) )
279        return NULL;
280
281    return tr_ptrArrayFindSorted( handshakes, addr, handshakeCompareToAddr );
282}
283
284static int
285comparePeerAtomToAddress( const void * va, const void * vb )
286{
287    const struct peer_atom * a = va;
288
289    return tr_compareAddresses( &a->addr, vb );
290}
291
292static int
293compareAtomsByAddress( const void * va, const void * vb )
294{
295    const struct peer_atom * b = vb;
296
297    assert( tr_isAtom( b ) );
298
299    return comparePeerAtomToAddress( va, &b->addr );
300}
301
302/**
303***
304**/
305
306const tr_address *
307tr_peerAddress( const tr_peer * peer )
308{
309    return &peer->atom->addr;
310}
311
312static Torrent*
313getExistingTorrent( tr_peerMgr *    manager,
314                    const uint8_t * hash )
315{
316    tr_torrent * tor = tr_torrentFindFromHash( manager->session, hash );
317
318    return tor == NULL ? NULL : tor->torrentPeers;
319}
320
321static int
322peerCompare( const void * a, const void * b )
323{
324    return tr_compareAddresses( tr_peerAddress( a ), tr_peerAddress( b ) );
325}
326
327static struct peer_atom*
328getExistingAtom( const Torrent    * t,
329                 const tr_address * addr )
330{
331    Torrent * tt = (Torrent*)t;
332    assert( torrentIsLocked( t ) );
333    return tr_ptrArrayFindSorted( &tt->pool, addr, comparePeerAtomToAddress );
334}
335
336static tr_bool
337peerIsInUse( const Torrent * ct, const struct peer_atom * atom )
338{
339    Torrent * t = (Torrent*) ct;
340
341    assert( torrentIsLocked ( t ) );
342
343    return ( atom->peer != NULL )
344        || getExistingHandshake( &t->outgoingHandshakes, &atom->addr )
345        || getExistingHandshake( &t->manager->incomingHandshakes, &atom->addr );
346}
347
348static tr_peer*
349peerConstructor( struct peer_atom * atom )
350{
351    tr_peer * peer = tr_new0( tr_peer, 1 );
352
353    tr_bitsetConstructor( &peer->have, 0 );
354
355    peer->atom = atom;
356    atom->peer = peer;
357
358    peer->blocksSentToClient  = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
359    peer->blocksSentToPeer    = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
360    peer->cancelsSentToClient = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
361    peer->cancelsSentToPeer   = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
362
363    return peer;
364}
365
366static tr_peer*
367getPeer( Torrent * torrent, struct peer_atom * atom )
368{
369    tr_peer * peer;
370
371    assert( torrentIsLocked( torrent ) );
372
373    peer = atom->peer;
374
375    if( peer == NULL )
376    {
377        peer = peerConstructor( atom );
378        tr_ptrArrayInsertSorted( &torrent->peers, peer, peerCompare );
379    }
380
381    return peer;
382}
383
384static void peerDeclinedAllRequests( Torrent *, const tr_peer * );
385
386static void
387peerDestructor( Torrent * t, tr_peer * peer )
388{
389    assert( peer != NULL );
390
391    peerDeclinedAllRequests( t, peer );
392
393    if( peer->msgs != NULL )
394    {
395        tr_peerMsgsUnsubscribe( peer->msgs, peer->msgsTag );
396        tr_peerMsgsFree( peer->msgs );
397    }
398
399    tr_peerIoClear( peer->io );
400    tr_peerIoUnref( peer->io ); /* balanced by the ref in handshakeDoneCB() */
401
402    tr_historyFree( peer->blocksSentToClient  );
403    tr_historyFree( peer->blocksSentToPeer    );
404    tr_historyFree( peer->cancelsSentToClient );
405    tr_historyFree( peer->cancelsSentToPeer   );
406
407    tr_bitsetDestructor( &peer->have );
408    tr_bitfieldFree( peer->blame );
409    tr_free( peer->client );
410    peer->atom->peer = NULL;
411
412    tr_free( peer );
413}
414
415static void
416removePeer( Torrent * t, tr_peer * peer )
417{
418    tr_peer * removed;
419    struct peer_atom * atom = peer->atom;
420
421    assert( torrentIsLocked( t ) );
422    assert( atom );
423
424    atom->time = tr_time( );
425
426    removed = tr_ptrArrayRemoveSorted( &t->peers, peer, peerCompare );
427    assert( removed == peer );
428    peerDestructor( t, removed );
429}
430
431static void
432removeAllPeers( Torrent * t )
433{
434    while( !tr_ptrArrayEmpty( &t->peers ) )
435        removePeer( t, tr_ptrArrayNth( &t->peers, 0 ) );
436}
437
438static void
439torrentDestructor( void * vt )
440{
441    Torrent * t = vt;
442
443    assert( t );
444    assert( !t->isRunning );
445    assert( torrentIsLocked( t ) );
446    assert( tr_ptrArrayEmpty( &t->outgoingHandshakes ) );
447    assert( tr_ptrArrayEmpty( &t->peers ) );
448
449    tr_ptrArrayDestruct( &t->webseeds, (PtrArrayForeachFunc)tr_webseedFree );
450    tr_ptrArrayDestruct( &t->pool, (PtrArrayForeachFunc)tr_free );
451    tr_ptrArrayDestruct( &t->outgoingHandshakes, NULL );
452    tr_ptrArrayDestruct( &t->peers, NULL );
453
454    tr_free( t->requests );
455    tr_free( t->pieces );
456    tr_free( t );
457}
458
459static void peerCallbackFunc( void * vpeer, void * vevent, void * vt );
460
461static Torrent*
462torrentConstructor( tr_peerMgr * manager,
463                    tr_torrent * tor )
464{
465    int       i;
466    Torrent * t;
467
468    t = tr_new0( Torrent, 1 );
469    t->manager = manager;
470    t->tor = tor;
471    t->pool = TR_PTR_ARRAY_INIT;
472    t->peers = TR_PTR_ARRAY_INIT;
473    t->webseeds = TR_PTR_ARRAY_INIT;
474    t->outgoingHandshakes = TR_PTR_ARRAY_INIT;
475
476    for( i = 0; i < tor->info.webseedCount; ++i )
477    {
478        tr_webseed * w =
479            tr_webseedNew( tor, tor->info.webseeds[i], peerCallbackFunc, t );
480        tr_ptrArrayAppend( &t->webseeds, w );
481    }
482
483    return t;
484}
485
486tr_peerMgr*
487tr_peerMgrNew( tr_session * session )
488{
489    tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
490    m->session = session;
491    m->incomingHandshakes = TR_PTR_ARRAY_INIT;
492    return m;
493}
494
495static void
496deleteTimer( struct event ** t )
497{
498    if( *t != NULL )
499    {
500        evtimer_del( *t );
501        tr_free( *t );
502        *t = NULL;
503    }
504}
505
506static void
507deleteTimers( struct tr_peerMgr * m )
508{
509    deleteTimer( &m->atomTimer );
510    deleteTimer( &m->bandwidthTimer );
511    deleteTimer( &m->rechokeTimer );
512    deleteTimer( &m->refillUpkeepTimer );
513}
514
515void
516tr_peerMgrFree( tr_peerMgr * manager )
517{
518    managerLock( manager );
519
520    deleteTimers( manager );
521
522    /* free the handshakes.  Abort invokes handshakeDoneCB(), which removes
523     * the item from manager->handshakes, so this is a little roundabout... */
524    while( !tr_ptrArrayEmpty( &manager->incomingHandshakes ) )
525        tr_handshakeAbort( tr_ptrArrayNth( &manager->incomingHandshakes, 0 ) );
526
527    tr_ptrArrayDestruct( &manager->incomingHandshakes, NULL );
528
529    managerUnlock( manager );
530    tr_free( manager );
531}
532
533static int
534clientIsDownloadingFrom( const tr_torrent * tor, const tr_peer * peer )
535{
536    if( !tr_torrentHasMetadata( tor ) )
537        return TRUE;
538
539    return peer->clientIsInterested && !peer->clientIsChoked;
540}
541
542static int
543clientIsUploadingTo( const tr_peer * peer )
544{
545    return peer->peerIsInterested && !peer->peerIsChoked;
546}
547
548/***
549****
550***/
551
552void
553tr_peerMgrOnBlocklistChanged( tr_peerMgr * mgr )
554{
555    tr_torrent * tor = NULL;
556    tr_session * session = mgr->session;
557
558    /* we cache whether or not a peer is blocklisted...
559       since the blocklist has changed, erase that cached value */
560    while(( tor = tr_torrentNext( session, tor )))
561    {
562        int i;
563        Torrent * t = tor->torrentPeers;
564        const int n = tr_ptrArraySize( &t->pool );
565        for( i=0; i<n; ++i ) {
566            struct peer_atom * atom = tr_ptrArrayNth( &t->pool, i );
567            atom->blocklisted = -1;
568        }
569    }
570}
571
572static tr_bool
573isAtomBlocklisted( tr_session * session, struct peer_atom * atom )
574{
575    if( atom->blocklisted < 0 )
576        atom->blocklisted = tr_sessionIsAddressBlocked( session, &atom->addr );
577
578    assert( tr_isBool( atom->blocklisted ) );
579    return atom->blocklisted;
580}
581
582
583/***
584****
585***/
586
587static void
588atomSetSeedProbability( struct peer_atom * atom, int seedProbability )
589{
590    assert( atom != NULL );
591    assert( -1<=seedProbability && seedProbability<=100 );
592
593    atom->seedProbability = seedProbability;
594
595    if( seedProbability == 100 )
596        atom->flags |= ADDED_F_SEED_FLAG;
597    else if( seedProbability != -1 )
598        atom->flags &= ~ADDED_F_SEED_FLAG;
599}
600
601static void
602atomSetSeed( struct peer_atom * atom )
603{
604    atomSetSeedProbability( atom, 100 );
605}
606
607static inline tr_bool
608atomIsSeed( const struct peer_atom * atom )
609{
610    return atom->seedProbability == 100;
611}
612
613tr_bool
614tr_peerMgrPeerIsSeed( const tr_torrent  * tor,
615                      const tr_address  * addr )
616{
617    tr_bool isSeed = FALSE;
618    const Torrent * t = tor->torrentPeers;
619    const struct peer_atom * atom = getExistingAtom( t, addr );
620
621    if( atom )
622        isSeed = atomIsSeed( atom );
623
624    return isSeed;
625}
626
627/**
628***  REQUESTS
629***
630*** There are two data structures associated with managing block requests:
631***
632*** 1. Torrent::requests, an array of "struct block_request" which keeps
633***    track of which blocks have been requested, and when, and by which peers.
634***    This is list is used for (a) cancelling requests that have been pending
635***    for too long and (b) avoiding duplicate requests before endgame.
636***
637*** 2. Torrent::pieces, an array of "struct weighted_piece" which lists the
638***    pieces that we want to request.  It's used to decide which blocks to
639***    return next when tr_peerMgrGetBlockRequests() is called.
640**/
641
642/**
643*** struct block_request
644**/
645
646static int
647compareReqByBlock( const void * va, const void * vb )
648{
649    const struct block_request * a = va;
650    const struct block_request * b = vb;
651
652    /* primary key: block */
653    if( a->block < b->block ) return -1;
654    if( a->block > b->block ) return 1;
655
656    /* secondary key: peer */
657    if( a->peer < b->peer ) return -1;
658    if( a->peer > b->peer ) return 1;
659
660    return 0;
661}
662
663static void
664requestListAdd( Torrent * t, tr_block_index_t block, tr_peer * peer )
665{
666    struct block_request key;
667
668    /* ensure enough room is available... */
669    if( t->requestCount + 1 >= t->requestAlloc )
670    {
671        const int CHUNK_SIZE = 128;
672        t->requestAlloc += CHUNK_SIZE;
673        t->requests = tr_renew( struct block_request,
674                                t->requests, t->requestAlloc );
675    }
676
677    /* populate the record we're inserting */
678    key.block = block;
679    key.peer = peer;
680    key.sentAt = tr_time( );
681
682    /* insert the request to our array... */
683    {
684        tr_bool exact;
685        const int pos = tr_lowerBound( &key, t->requests, t->requestCount,
686                                       sizeof( struct block_request ),
687                                       compareReqByBlock, &exact );
688        assert( !exact );
689        memmove( t->requests + pos + 1,
690                 t->requests + pos,
691                 sizeof( struct block_request ) * ( t->requestCount++ - pos ) );
692        t->requests[pos] = key;
693    }
694
695    if( peer != NULL )
696    {
697        ++peer->pendingReqsToPeer;
698        assert( peer->pendingReqsToPeer >= 0 );
699    }
700
701    /*fprintf( stderr, "added request of block %lu from peer %s... "
702                       "there are now %d block\n",
703                       (unsigned long)block, tr_atomAddrStr( peer->atom ), t->requestCount );*/
704}
705
706static struct block_request *
707requestListLookup( Torrent * t, tr_block_index_t block, const tr_peer * peer )
708{
709    struct block_request key;
710    key.block = block;
711    key.peer = (tr_peer*) peer;
712
713    return bsearch( &key, t->requests, t->requestCount,
714                    sizeof( struct block_request ),
715                    compareReqByBlock );
716}
717
718/* how many peers are we currently requesting this block from... */
719static int
720countBlockRequests( Torrent * t, tr_block_index_t block )
721{
722    tr_bool exact;
723    int i, n, pos;
724    struct block_request key;
725
726    key.block = block;
727    key.peer = NULL;
728    pos = tr_lowerBound( &key, t->requests, t->requestCount,
729                         sizeof( struct block_request ),
730                         compareReqByBlock, &exact );
731
732    assert( !exact ); /* shouldn't have a request with .peer == NULL */
733
734    n = 0;
735    for( i=pos; i<t->requestCount; ++i ) {
736        if( t->requests[i].block == block )
737            ++n;
738        else
739            break;
740    }
741
742    return n;
743}
744
745static void
746decrementPendingReqCount( const struct block_request * b )
747{
748    if( b->peer != NULL )
749        if( b->peer->pendingReqsToPeer > 0 )
750            --b->peer->pendingReqsToPeer;
751}
752
753static void
754requestListRemove( Torrent * t, tr_block_index_t block, const tr_peer * peer )
755{
756    const struct block_request * b = requestListLookup( t, block, peer );
757    if( b != NULL )
758    {
759        const int pos = b - t->requests;
760        assert( pos < t->requestCount );
761
762        decrementPendingReqCount( b );
763
764        tr_removeElementFromArray( t->requests,
765                                   pos,
766                                   sizeof( struct block_request ),
767                                   t->requestCount-- );
768
769        /*fprintf( stderr, "removing request of block %lu from peer %s... "
770                           "there are now %d block requests left\n",
771                           (unsigned long)block, tr_atomAddrStr( peer->atom ), t->requestCount );*/
772    }
773}
774
775/**
776*** struct weighted_piece
777**/
778
779enum
780{
781    PIECES_UNSORTED,
782    PIECES_SORTED_BY_INDEX,
783    PIECES_SORTED_BY_WEIGHT
784};
785
786const tr_torrent * weightTorrent;
787
788/* we try to create a "weight" s.t. high-priority pieces come before others,
789 * and that partially-complete pieces come before empty ones. */
790static int
791comparePieceByWeight( const void * va, const void * vb )
792{
793    const struct weighted_piece * a = va;
794    const struct weighted_piece * b = vb;
795    int ia, ib, missing, pending;
796    const tr_torrent * tor = weightTorrent;
797
798    /* primary key: weight */
799    missing = tr_cpMissingBlocksInPiece( &tor->completion, a->index );
800    pending = a->requestCount;
801    ia = missing > pending ? missing - pending : (int)(tor->blockCountInPiece + pending);
802    missing = tr_cpMissingBlocksInPiece( &tor->completion, b->index );
803    pending = b->requestCount;
804    ib = missing > pending ? missing - pending : (int)(tor->blockCountInPiece + pending);
805    if( ia < ib ) return -1;
806    if( ia > ib ) return 1;
807
808    /* secondary key: higher priorities go first */
809    ia = tor->info.pieces[a->index].priority;
810    ib = tor->info.pieces[b->index].priority;
811    if( ia > ib ) return -1;
812    if( ia < ib ) return 1;
813
814    /* tertiary key: random */
815    if( a->salt < b->salt ) return -1;
816    if( a->salt > b->salt ) return 1;
817
818    /* okay, they're equal */
819    return 0;
820}
821
822static int
823comparePieceByIndex( const void * va, const void * vb )
824{
825    const struct weighted_piece * a = va;
826    const struct weighted_piece * b = vb;
827    if( a->index < b->index ) return -1;
828    if( a->index > b->index ) return 1;
829    return 0;
830}
831
832static void
833pieceListSort( Torrent * t, int mode )
834{
835    int(*compar)(const void *, const void *);
836
837    assert( mode==PIECES_SORTED_BY_INDEX
838         || mode==PIECES_SORTED_BY_WEIGHT );
839
840    switch( mode ) {
841        case PIECES_SORTED_BY_WEIGHT: compar = comparePieceByWeight; break;
842        case PIECES_SORTED_BY_INDEX: compar = comparePieceByIndex; break;
843        default: assert( 0 && "unhandled" );  break;
844    }
845
846    weightTorrent = t->tor;
847    qsort( t->pieces, t->pieceCount,
848           sizeof( struct weighted_piece ), compar );
849}
850
851static tr_bool
852isInEndgame( Torrent * t )
853{
854    tr_bool endgame = FALSE;
855
856    if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
857    {
858        const struct weighted_piece * p = t->pieces;
859        const int pending = p->requestCount;
860        const int missing = tr_cpMissingBlocksInPiece( &t->tor->completion, p->index );
861        endgame = pending >= missing;
862    }
863
864    /*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
865    return endgame;
866}
867
868/**
869 * This function is useful for sanity checking,
870 * but is too expensive even for nightly builds...
871 * let's leave it disabled but add an easy hook to compile it back in
872 */
873#if 0
874static void
875assertWeightedPiecesAreSorted( Torrent * t )
876{
877    if( !isInEndgame( t ) )
878    {
879        int i;
880        weightTorrent = t->tor;
881        for( i=0; i<t->pieceCount-1; ++i )
882            assert( comparePieceByWeight( &t->pieces[i], &t->pieces[i+1] ) <= 0 );
883    }
884}
885#else
886#define assertWeightedPiecesAreSorted(t)
887#endif
888
889static struct weighted_piece *
890pieceListLookup( Torrent * t, tr_piece_index_t index )
891{
892    int i;
893
894    for( i=0; i<t->pieceCount; ++i )
895        if( t->pieces[i].index == index )
896            return &t->pieces[i];
897
898    return NULL;
899}
900
901static void
902pieceListRebuild( Torrent * t )
903{
904    assertWeightedPiecesAreSorted( t );
905
906    if( !tr_torrentIsSeed( t->tor ) )
907    {
908        tr_piece_index_t i;
909        tr_piece_index_t * pool;
910        tr_piece_index_t poolCount = 0;
911        const tr_torrent * tor = t->tor;
912        const tr_info * inf = tr_torrentInfo( tor );
913        struct weighted_piece * pieces;
914        int pieceCount;
915
916        /* build the new list */
917        pool = tr_new( tr_piece_index_t, inf->pieceCount );
918        for( i=0; i<inf->pieceCount; ++i )
919            if( !inf->pieces[i].dnd )
920                if( !tr_cpPieceIsComplete( &tor->completion, i ) )
921                    pool[poolCount++] = i;
922        pieceCount = poolCount;
923        pieces = tr_new0( struct weighted_piece, pieceCount );
924        for( i=0; i<poolCount; ++i ) {
925            struct weighted_piece * piece = pieces + i;
926            piece->index = pool[i];
927            piece->requestCount = 0;
928            piece->salt = tr_cryptoWeakRandInt( 4096 );
929        }
930
931        /* if we already had a list of pieces, merge it into
932         * the new list so we don't lose its requestCounts */
933        if( t->pieces != NULL )
934        {
935            struct weighted_piece * o = t->pieces;
936            struct weighted_piece * oend = o + t->pieceCount;
937            struct weighted_piece * n = pieces;
938            struct weighted_piece * nend = n + pieceCount;
939
940            pieceListSort( t, PIECES_SORTED_BY_INDEX );
941
942            while( o!=oend && n!=nend ) {
943                if( o->index < n->index )
944                    ++o;
945                else if( o->index > n->index )
946                    ++n;
947                else
948                    *n++ = *o++;
949            }
950
951            tr_free( t->pieces );
952        }
953
954        t->pieces = pieces;
955        t->pieceCount = pieceCount;
956
957        pieceListSort( t, PIECES_SORTED_BY_WEIGHT );
958
959        /* cleanup */
960        tr_free( pool );
961    }
962}
963
964static void
965pieceListRemovePiece( Torrent * t, tr_piece_index_t piece )
966{
967    struct weighted_piece * p;
968
969    assertWeightedPiecesAreSorted( t );
970
971    if(( p = pieceListLookup( t, piece )))
972    {
973        const int pos = p - t->pieces;
974
975        tr_removeElementFromArray( t->pieces,
976                                   pos,
977                                   sizeof( struct weighted_piece ),
978                                   t->pieceCount-- );
979
980        if( t->pieceCount == 0 )
981        {
982            tr_free( t->pieces );
983            t->pieces = NULL;
984        }
985    }
986
987    assertWeightedPiecesAreSorted( t );
988}
989
990static void
991pieceListResortPiece( Torrent * t, struct weighted_piece * p )
992{
993    int pos;
994    tr_bool isSorted = TRUE;
995
996    if( p == NULL )
997        return;
998
999    /* is the torrent already sorted? */
1000    pos = p - t->pieces;
1001    weightTorrent = t->tor;
1002    if( isSorted && ( pos > 0 ) && ( comparePieceByWeight( p-1, p ) > 0 ) )
1003        isSorted = FALSE;
1004    if( isSorted && ( pos < t->pieceCount - 1 ) && ( comparePieceByWeight( p, p+1 ) > 0 ) )
1005        isSorted = FALSE;
1006
1007    /* if it's not sorted, move it around */
1008    if( !isSorted )
1009    {
1010        tr_bool exact;
1011        const struct weighted_piece tmp = *p;
1012
1013        tr_removeElementFromArray( t->pieces,
1014                                   pos,
1015                                   sizeof( struct weighted_piece ),
1016                                   t->pieceCount-- );
1017
1018        pos = tr_lowerBound( &tmp, t->pieces, t->pieceCount,
1019                             sizeof( struct weighted_piece ),
1020                             comparePieceByWeight, &exact );
1021
1022        memmove( &t->pieces[pos + 1],
1023                 &t->pieces[pos],
1024                 sizeof( struct weighted_piece ) * ( t->pieceCount++ - pos ) );
1025
1026        t->pieces[pos] = tmp;
1027    }
1028
1029    assertWeightedPiecesAreSorted( t );
1030}
1031
1032static void
1033pieceListRemoveRequest( Torrent * t, tr_block_index_t block )
1034{
1035    struct weighted_piece * p;
1036    const tr_piece_index_t index = tr_torBlockPiece( t->tor, block );
1037
1038    assertWeightedPiecesAreSorted( t );
1039
1040    if( ((p = pieceListLookup( t, index ))) && ( p->requestCount > 0 ) )
1041    {
1042        --p->requestCount;
1043        pieceListResortPiece( t, p );
1044    }
1045
1046    assertWeightedPiecesAreSorted( t );
1047}
1048
1049/**
1050***
1051**/
1052
1053void
1054tr_peerMgrRebuildRequests( tr_torrent * tor )
1055{
1056    assert( tr_isTorrent( tor ) );
1057
1058    pieceListRebuild( tor->torrentPeers );
1059}
1060
1061void
1062tr_peerMgrGetNextRequests( tr_torrent           * tor,
1063                           tr_peer              * peer,
1064                           int                    numwant,
1065                           tr_block_index_t     * setme,
1066                           int                  * numgot )
1067{
1068    int i;
1069    int got;
1070    Torrent * t;
1071    tr_bool endgame;
1072    struct weighted_piece * pieces;
1073    const tr_bitset * have = &peer->have;
1074
1075    /* sanity clause */
1076    assert( tr_isTorrent( tor ) );
1077    assert( peer->clientIsInterested );
1078    assert( !peer->clientIsChoked );
1079    assert( numwant > 0 );
1080
1081    /* walk through the pieces and find blocks that should be requested */
1082    got = 0;
1083    t = tor->torrentPeers;
1084    assertWeightedPiecesAreSorted( t );
1085
1086    /* prep the pieces list */
1087    if( t->pieces == NULL )
1088        pieceListRebuild( t );
1089
1090    endgame = isInEndgame( t );
1091
1092    pieces = t->pieces;
1093    for( i=0; i<t->pieceCount && got<numwant; ++i )
1094    {
1095        struct weighted_piece * p = pieces + i;
1096        const int missing = tr_cpMissingBlocksInPiece( &tor->completion, p->index );
1097        const int maxDuplicatesPerBlock = endgame ? 3 : 1;
1098
1099        if( p->requestCount > ( missing * maxDuplicatesPerBlock ) )
1100            continue;
1101
1102        /* if the peer has this piece that we want... */
1103        if( tr_bitsetHasFast( have, p->index ) )
1104        {
1105            tr_block_index_t b = tr_torPieceFirstBlock( tor, p->index );
1106            const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, p->index );
1107
1108            for( ; b!=e && got<numwant; ++b )
1109            {
1110                /* don't request blocks we've already got */
1111                if( tr_cpBlockIsCompleteFast( &tor->completion, b ) )
1112                    continue;
1113
1114                /* don't send the same request to the same peer twice */
1115                if( tr_peerMgrDidPeerRequest( tor, peer, b ) )
1116                    continue;
1117
1118                /* don't send the same request to any peer too many times */
1119                if( countBlockRequests( t, b ) >= maxDuplicatesPerBlock )
1120                    continue;
1121
1122                /* update the caller's table */
1123                setme[got++] = b;
1124
1125                /* update our own tables */
1126                requestListAdd( t, b, peer );
1127                ++p->requestCount;
1128            }
1129        }
1130    }
1131
1132    /* In most cases we've just changed the weights of a small number of pieces.
1133     * So rather than qsort()ing the entire array, it's faster to apply an
1134     * adaptive insertion sort algorithm. */
1135    if( got > 0 )
1136    {
1137        /* not enough requests || last piece modified */
1138        if ( i == t->pieceCount ) --i;
1139
1140        weightTorrent = t->tor;
1141        while( --i >= 0 )
1142        {
1143            tr_bool exact;
1144
1145            /* relative position! */
1146            const int newpos = tr_lowerBound( &t->pieces[i], &t->pieces[i + 1],
1147                                              t->pieceCount - (i + 1),
1148                                              sizeof( struct weighted_piece ),
1149                                              comparePieceByWeight, &exact );
1150            if( newpos > 0 )
1151            {
1152                const struct weighted_piece piece = t->pieces[i];
1153                memmove( &t->pieces[i],
1154                         &t->pieces[i + 1],
1155                         sizeof( struct weighted_piece ) * ( newpos ) );
1156                t->pieces[i + newpos] = piece;
1157            }
1158        }
1159    }
1160
1161    assertWeightedPiecesAreSorted( t );
1162    *numgot = got;
1163}
1164
1165tr_bool
1166tr_peerMgrDidPeerRequest( const tr_torrent  * tor,
1167                          const tr_peer     * peer,
1168                          tr_block_index_t    block )
1169{
1170    const Torrent * t = tor->torrentPeers;
1171    return requestListLookup( (Torrent*)t, block, peer ) != NULL;
1172}
1173
1174/* cancel requests that are too old */
1175static void
1176refillUpkeep( int foo UNUSED, short bar UNUSED, void * vmgr )
1177{
1178    time_t now;
1179    uint64_t now_msec;
1180    time_t too_old;
1181    tr_torrent * tor;
1182    tr_peerMgr * mgr = vmgr;
1183    managerLock( mgr );
1184
1185    now = tr_time( );
1186    now_msec = tr_date( );
1187    too_old = now - REQUEST_TTL_SECS;
1188
1189    tor = NULL;
1190    while(( tor = tr_torrentNext( mgr->session, tor )))
1191    {
1192        Torrent * t = tor->torrentPeers;
1193        const int n = t->requestCount;
1194        if( n > 0 )
1195        {
1196            int keepCount = 0;
1197            int cancelCount = 0;
1198            struct block_request * cancel = tr_new( struct block_request, n );
1199            const struct block_request * it;
1200            const struct block_request * end;
1201
1202            for( it=t->requests, end=it+n; it!=end; ++it )
1203            {
1204                if( ( it->sentAt <= too_old ) && !tr_peerMsgsIsReadingBlock( it->peer->msgs, it->block ) )
1205                    cancel[cancelCount++] = *it;
1206                else
1207                {
1208                    if( it != &t->requests[keepCount] )
1209                        t->requests[keepCount] = *it;
1210                    keepCount++;
1211                }
1212            }
1213
1214            /* prune out the ones we aren't keeping */
1215            t->requestCount = keepCount;
1216
1217            /* send cancel messages for all the "cancel" ones */
1218            for( it=cancel, end=it+cancelCount; it!=end; ++it ) {
1219                if( ( it->peer != NULL ) && ( it->peer->msgs != NULL ) ) {
1220                    tr_historyAdd( it->peer->cancelsSentToPeer, now_msec, 1 );
1221                    tr_peerMsgsCancel( it->peer->msgs, it->block );
1222                    decrementPendingReqCount( it );
1223                }
1224            }
1225
1226            /* decrement the pending request counts for the timed-out blocks */
1227            for( it=cancel, end=it+cancelCount; it!=end; ++it )
1228                pieceListRemoveRequest( t, it->block );
1229
1230            /* cleanup loop */
1231            tr_free( cancel );
1232        }
1233    }
1234
1235    tr_timerAddMsec( mgr->refillUpkeepTimer, REFILL_UPKEEP_PERIOD_MSEC );
1236    managerUnlock( mgr );
1237}
1238
1239static void
1240addStrike( Torrent * t, tr_peer * peer )
1241{
1242    tordbg( t, "increasing peer %s strike count to %d",
1243            tr_atomAddrStr( peer->atom ), peer->strikes + 1 );
1244
1245    if( ++peer->strikes >= MAX_BAD_PIECES_PER_PEER )
1246    {
1247        struct peer_atom * atom = peer->atom;
1248        atom->myflags |= MYFLAG_BANNED;
1249        peer->doPurge = 1;
1250        tordbg( t, "banning peer %s", tr_atomAddrStr( atom ) );
1251    }
1252}
1253
1254static void
1255gotBadPiece( Torrent * t, tr_piece_index_t pieceIndex )
1256{
1257    tr_torrent *   tor = t->tor;
1258    const uint32_t byteCount = tr_torPieceCountBytes( tor, pieceIndex );
1259
1260    tor->corruptCur += byteCount;
1261    tor->downloadedCur -= MIN( tor->downloadedCur, byteCount );
1262
1263    tr_announcerAddBytes( tor, TR_ANN_CORRUPT, byteCount );
1264}
1265
1266static void
1267peerSuggestedPiece( Torrent            * t UNUSED,
1268                    tr_peer            * peer UNUSED,
1269                    tr_piece_index_t     pieceIndex UNUSED,
1270                    int                  isFastAllowed UNUSED )
1271{
1272#if 0
1273    assert( t );
1274    assert( peer );
1275    assert( peer->msgs );
1276
1277    /* is this a valid piece? */
1278    if(  pieceIndex >= t->tor->info.pieceCount )
1279        return;
1280
1281    /* don't ask for it if we've already got it */
1282    if( tr_cpPieceIsComplete( t->tor->completion, pieceIndex ) )
1283        return;
1284
1285    /* don't ask for it if they don't have it */
1286    if( !tr_bitfieldHas( peer->have, pieceIndex ) )
1287        return;
1288
1289    /* don't ask for it if we're choked and it's not fast */
1290    if( !isFastAllowed && peer->clientIsChoked )
1291        return;
1292
1293    /* request the blocks that we don't have in this piece */
1294    {
1295        tr_block_index_t block;
1296        const tr_torrent * tor = t->tor;
1297        const tr_block_index_t start = tr_torPieceFirstBlock( tor, pieceIndex );
1298        const tr_block_index_t end = start + tr_torPieceCountBlocks( tor, pieceIndex );
1299
1300        for( block=start; block<end; ++block )
1301        {
1302            if( !tr_cpBlockIsComplete( tor->completion, block ) )
1303            {
1304                const uint32_t offset = getBlockOffsetInPiece( tor, block );
1305                const uint32_t length = tr_torBlockCountBytes( tor, block );
1306                tr_peerMsgsAddRequest( peer->msgs, pieceIndex, offset, length );
1307                incrementPieceRequests( t, pieceIndex );
1308            }
1309        }
1310    }
1311#endif
1312}
1313
1314static void
1315removeRequestFromTables( Torrent * t, tr_block_index_t block, const tr_peer * peer )
1316{
1317    requestListRemove( t, block, peer );
1318    pieceListRemoveRequest( t, block );
1319}
1320
1321/* peer choked us, or maybe it disconnected.
1322   either way we need to remove all its requests */
1323static void
1324peerDeclinedAllRequests( Torrent * t, const tr_peer * peer )
1325{
1326    int i, n;
1327    tr_block_index_t * blocks = tr_new( tr_block_index_t, t->requestCount );
1328
1329    for( i=n=0; i<t->requestCount; ++i )
1330        if( peer == t->requests[i].peer )
1331            blocks[n++] = t->requests[i].block;
1332
1333    for( i=0; i<n; ++i )
1334        removeRequestFromTables( t, blocks[i], peer );
1335
1336    tr_free( blocks );
1337}
1338
1339static void
1340peerCallbackFunc( void * vpeer, void * vevent, void * vt )
1341{
1342    tr_peer * peer = vpeer; /* may be NULL if peer is a webseed */
1343    Torrent * t = vt;
1344    const tr_peer_event * e = vevent;
1345
1346    torrentLock( t );
1347
1348    switch( e->eventType )
1349    {
1350        case TR_PEER_PEER_GOT_DATA:
1351        {
1352            const time_t now = tr_time( );
1353            tr_torrent * tor = t->tor;
1354
1355            tr_torrentSetActivityDate( tor, now );
1356
1357            if( e->wasPieceData ) {
1358                tor->uploadedCur += e->length;
1359                tr_announcerAddBytes( tor, TR_ANN_UP, e->length );
1360                tr_torrentSetDirty( tor );
1361            }
1362
1363            /* update the stats */
1364            if( e->wasPieceData )
1365                tr_statsAddUploaded( tor->session, e->length );
1366
1367            /* update our atom */
1368            if( peer && e->wasPieceData )
1369                peer->atom->piece_data_time = now;
1370
1371            break;
1372        }
1373
1374        case TR_PEER_CLIENT_GOT_REJ:
1375            removeRequestFromTables( t, _tr_block( t->tor, e->pieceIndex, e->offset ), peer );
1376            break;
1377
1378        case TR_PEER_CLIENT_GOT_CHOKE:
1379            peerDeclinedAllRequests( t, peer );
1380            break;
1381
1382        case TR_PEER_CLIENT_GOT_PORT:
1383            if( peer )
1384                peer->atom->port = e->port;
1385            break;
1386
1387        case TR_PEER_CLIENT_GOT_SUGGEST:
1388            if( peer )
1389                peerSuggestedPiece( t, peer, e->pieceIndex, FALSE );
1390            break;
1391
1392        case TR_PEER_CLIENT_GOT_ALLOWED_FAST:
1393            if( peer )
1394                peerSuggestedPiece( t, peer, e->pieceIndex, TRUE );
1395            break;
1396
1397        case TR_PEER_CLIENT_GOT_DATA:
1398        {
1399            const time_t now = tr_time( );
1400            tr_torrent * tor = t->tor;
1401
1402            tr_torrentSetActivityDate( tor, now );
1403
1404            if( e->wasPieceData ) {
1405                tor->downloadedCur += e->length;
1406                tr_torrentSetDirty( tor );
1407            }
1408
1409            /* update the stats */
1410            if( e->wasPieceData )
1411                tr_statsAddDownloaded( tor->session, e->length );
1412
1413            /* update our atom */
1414            if( peer && e->wasPieceData )
1415                peer->atom->piece_data_time = now;
1416
1417            break;
1418        }
1419
1420        case TR_PEER_PEER_PROGRESS:
1421        {
1422            if( peer )
1423            {
1424                struct peer_atom * atom = peer->atom;
1425                if( e->progress >= 1.0 ) {
1426                    tordbg( t, "marking peer %s as a seed", tr_atomAddrStr( atom ) );
1427                    atomSetSeed( atom );
1428                }
1429            }
1430            break;
1431        }
1432
1433        case TR_PEER_CLIENT_GOT_BLOCK:
1434        {
1435            tr_torrent * tor = t->tor;
1436            tr_block_index_t block = _tr_block( tor, e->pieceIndex, e->offset );
1437
1438            requestListRemove( t, block, peer );
1439            pieceListRemoveRequest( t, block );
1440
1441            if( peer != NULL )
1442                tr_historyAdd( peer->blocksSentToClient, tr_date( ), 1 );
1443
1444            if( tr_cpBlockIsComplete( &tor->completion, block ) )
1445            {
1446                /* we already have this block... */
1447                const uint32_t n = tr_torBlockCountBytes( tor, block );
1448                tor->downloadedCur -= MIN( tor->downloadedCur, n );
1449                tordbg( t, "we have this block already..." );
1450            }
1451            else
1452            {
1453                tr_cpBlockAdd( &tor->completion, block );
1454                pieceListResortPiece( t, pieceListLookup( t, e->pieceIndex ) );
1455                tr_torrentSetDirty( tor );
1456
1457                if( tr_cpPieceIsComplete( &tor->completion, e->pieceIndex ) )
1458                {
1459                    const tr_piece_index_t p = e->pieceIndex;
1460                    const tr_bool ok = tr_ioTestPiece( tor, p );
1461
1462                    if( !ok )
1463                    {
1464                        tr_torerr( tor, _( "Piece %lu, which was just downloaded, failed its checksum test" ),
1465                                   (unsigned long)p );
1466                    }
1467
1468                    tr_torrentSetHasPiece( tor, p, ok );
1469                    tr_torrentSetPieceChecked( tor, p, TRUE );
1470                    tr_peerMgrSetBlame( tor, p, ok );
1471
1472                    if( !ok )
1473                    {
1474                        gotBadPiece( t, p );
1475                    }
1476                    else
1477                    {
1478                        int i;
1479                        int peerCount;
1480                        tr_peer ** peers;
1481                        tr_file_index_t fileIndex;
1482
1483                        /* only add this to downloadedCur if we got it from a peer --
1484                         * webseeds shouldn't count against our ratio.  As one tracker
1485                         * admin put it, "Those pieces are downloaded directly from the
1486                         * content distributor, not the peers, it is the tracker's job
1487                         * to manage the swarms, not the web server and does not fit
1488                         * into the jurisdiction of the tracker." */
1489                        if( peer != NULL ) {
1490                            const uint32_t n = tr_torPieceCountBytes( tor, p );
1491                            tr_announcerAddBytes( tor, TR_ANN_DOWN, n );
1492                        }
1493
1494                        peerCount = tr_ptrArraySize( &t->peers );
1495                        peers = (tr_peer**) tr_ptrArrayBase( &t->peers );
1496                        for( i=0; i<peerCount; ++i )
1497                            tr_peerMsgsHave( peers[i]->msgs, p );
1498
1499                        for( fileIndex=0; fileIndex<tor->info.fileCount; ++fileIndex ) {
1500                            const tr_file * file = &tor->info.files[fileIndex];
1501                            if( ( file->firstPiece <= p ) && ( p <= file->lastPiece ) )
1502                                if( tr_cpFileIsComplete( &tor->completion, fileIndex ) )
1503                                    tr_torrentFileCompleted( tor, fileIndex );
1504                        }
1505
1506                        pieceListRemovePiece( t, p );
1507                    }
1508                }
1509
1510                t->needsCompletenessCheck = TRUE;
1511            }
1512            break;
1513        }
1514
1515        case TR_PEER_ERROR:
1516            if( ( e->err == ERANGE ) || ( e->err == EMSGSIZE ) || ( e->err == ENOTCONN ) )
1517            {
1518                /* some protocol error from the peer */
1519                peer->doPurge = 1;
1520                tordbg( t, "setting %s doPurge flag because we got an ERANGE, EMSGSIZE, or ENOTCONN error",
1521                        tr_atomAddrStr( peer->atom ) );
1522            }
1523            else
1524            {
1525                tordbg( t, "unhandled error: %s", tr_strerror( e->err ) );
1526            }
1527            break;
1528
1529        default:
1530            assert( 0 );
1531    }
1532
1533    torrentUnlock( t );
1534}
1535
1536static int
1537getDefaultShelfLife( uint8_t from )
1538{
1539    /* in general, peers obtained from firsthand contact
1540     * are better than those from secondhand, etc etc */
1541    switch( from )
1542    {
1543        case TR_PEER_FROM_INCOMING : return 60 * 60 * 6;
1544        case TR_PEER_FROM_LTEP     : return 60 * 60 * 6;
1545        case TR_PEER_FROM_TRACKER  : return 60 * 60 * 3;
1546        case TR_PEER_FROM_DHT      : return 60 * 60 * 3;
1547        case TR_PEER_FROM_PEX      : return 60 * 60 * 2;
1548        case TR_PEER_FROM_RESUME   : return 60 * 60;
1549        case TR_PEER_FROM_LPD      : return 10 * 60;
1550        default                    : return 60 * 60;
1551    }
1552}
1553
1554static void
1555ensureAtomExists( Torrent           * t,
1556                  const tr_address  * addr,
1557                  const tr_port       port,
1558                  const uint8_t       flags,
1559                  const int8_t        seedProbability,
1560                  const uint8_t       from )
1561{
1562    struct peer_atom * a;
1563
1564    assert( tr_isAddress( addr ) );
1565    assert( from < TR_PEER_FROM__MAX );
1566
1567    a = getExistingAtom( t, addr );
1568
1569    if( a == NULL )
1570    {
1571        const int jitter = tr_cryptoWeakRandInt( 60*10 );
1572        a = tr_new0( struct peer_atom, 1 );
1573        a->addr = *addr;
1574        a->port = port;
1575        a->flags = flags;
1576        a->from = from;
1577        a->shelf_date = tr_time( ) + getDefaultShelfLife( from ) + jitter;
1578        a->blocklisted = -1;
1579        atomSetSeedProbability( a, seedProbability );
1580        tr_ptrArrayInsertSorted( &t->pool, a, compareAtomsByAddress );
1581
1582        tordbg( t, "got a new atom: %s", tr_atomAddrStr( a ) );
1583    }
1584    else if( a->seedProbability == -1 )
1585    {
1586        atomSetSeedProbability( a, seedProbability );
1587    }
1588}
1589
1590static int
1591getMaxPeerCount( const tr_torrent * tor )
1592{
1593    return tor->maxConnectedPeers;
1594}
1595
1596static int
1597getPeerCount( const Torrent * t )
1598{
1599    return tr_ptrArraySize( &t->peers );/* + tr_ptrArraySize( &t->outgoingHandshakes ); */
1600}
1601
1602/* FIXME: this is kind of a mess. */
1603static tr_bool
1604myHandshakeDoneCB( tr_handshake  * handshake,
1605                   tr_peerIo     * io,
1606                   tr_bool         readAnythingFromPeer,
1607                   tr_bool         isConnected,
1608                   const uint8_t * peer_id,
1609                   void          * vmanager )
1610{
1611    tr_bool            ok = isConnected;
1612    tr_bool            success = FALSE;
1613    tr_port            port;
1614    const tr_address * addr;
1615    tr_peerMgr       * manager = vmanager;
1616    Torrent          * t;
1617    tr_handshake     * ours;
1618
1619    assert( io );
1620    assert( tr_isBool( ok ) );
1621
1622    t = tr_peerIoHasTorrentHash( io )
1623        ? getExistingTorrent( manager, tr_peerIoGetTorrentHash( io ) )
1624        : NULL;
1625
1626    if( tr_peerIoIsIncoming ( io ) )
1627        ours = tr_ptrArrayRemoveSorted( &manager->incomingHandshakes,
1628                                        handshake, handshakeCompare );
1629    else if( t )
1630        ours = tr_ptrArrayRemoveSorted( &t->outgoingHandshakes,
1631                                        handshake, handshakeCompare );
1632    else
1633        ours = handshake;
1634
1635    assert( ours );
1636    assert( ours == handshake );
1637
1638    if( t )
1639        torrentLock( t );
1640
1641    addr = tr_peerIoGetAddress( io, &port );
1642
1643    if( !ok || !t || !t->isRunning )
1644    {
1645        if( t )
1646        {
1647            struct peer_atom * atom = getExistingAtom( t, addr );
1648            if( atom )
1649            {
1650                ++atom->numFails;
1651
1652                if( !readAnythingFromPeer )
1653                {
1654                    tordbg( t, "marking peer %s as unreachable... numFails is %d", tr_atomAddrStr( atom ), (int)atom->numFails );
1655                    atom->myflags |= MYFLAG_UNREACHABLE;
1656                }
1657            }
1658        }
1659    }
1660    else /* looking good */
1661    {
1662        struct peer_atom * atom;
1663
1664        ensureAtomExists( t, addr, port, 0, -1, TR_PEER_FROM_INCOMING );
1665        atom = getExistingAtom( t, addr );
1666        atom->time = tr_time( );
1667        atom->piece_data_time = 0;
1668        atom->lastConnectionAt = tr_time( );
1669        atom->myflags &= ~MYFLAG_UNREACHABLE;
1670
1671        if( atom->myflags & MYFLAG_BANNED )
1672        {
1673            tordbg( t, "banned peer %s tried to reconnect",
1674                    tr_atomAddrStr( atom ) );
1675        }
1676        else if( tr_peerIoIsIncoming( io )
1677               && ( getPeerCount( t ) >= getMaxPeerCount( t->tor ) ) )
1678
1679        {
1680        }
1681        else
1682        {
1683            tr_peer * peer = atom->peer;
1684
1685            if( peer ) /* we already have this peer */
1686            {
1687            }
1688            else
1689            {
1690                peer = getPeer( t, atom );
1691                tr_free( peer->client );
1692
1693                if( !peer_id )
1694                    peer->client = NULL;
1695                else {
1696                    char client[128];
1697                    tr_clientForId( client, sizeof( client ), peer_id );
1698                    peer->client = tr_strdup( client );
1699                }
1700
1701                peer->io = tr_handshakeStealIO( handshake ); /* this steals its refcount too, which is
1702                                                                balanced by our unref in peerDestructor()  */
1703                tr_peerIoSetParent( peer->io, t->tor->bandwidth );
1704                tr_peerMsgsNew( t->tor, peer, peerCallbackFunc, t, &peer->msgsTag );
1705
1706                success = TRUE;
1707            }
1708        }
1709    }
1710
1711    if( t )
1712        torrentUnlock( t );
1713
1714    return success;
1715}
1716
1717void
1718tr_peerMgrAddIncoming( tr_peerMgr * manager,
1719                       tr_address * addr,
1720                       tr_port      port,
1721                       int          socket )
1722{
1723    tr_session * session;
1724
1725    managerLock( manager );
1726
1727    assert( tr_isSession( manager->session ) );
1728    session = manager->session;
1729
1730    if( tr_sessionIsAddressBlocked( session, addr ) )
1731    {
1732        tr_dbg( "Banned IP address \"%s\" tried to connect to us", tr_ntop_non_ts( addr ) );
1733        tr_netClose( session, socket );
1734    }
1735    else if( getExistingHandshake( &manager->incomingHandshakes, addr ) )
1736    {
1737        tr_netClose( session, socket );
1738    }
1739    else /* we don't have a connection to them yet... */
1740    {
1741        tr_peerIo *    io;
1742        tr_handshake * handshake;
1743
1744        io = tr_peerIoNewIncoming( session, session->bandwidth, addr, port, socket );
1745
1746        handshake = tr_handshakeNew( io,
1747                                     session->encryptionMode,
1748                                     myHandshakeDoneCB,
1749                                     manager );
1750
1751        tr_peerIoUnref( io ); /* balanced by the implicit ref in tr_peerIoNewIncoming() */
1752
1753        tr_ptrArrayInsertSorted( &manager->incomingHandshakes, handshake,
1754                                 handshakeCompare );
1755    }
1756
1757    managerUnlock( manager );
1758}
1759
1760static tr_bool
1761tr_isPex( const tr_pex * pex )
1762{
1763    return pex && tr_isAddress( &pex->addr );
1764}
1765
1766void
1767tr_peerMgrAddPex( tr_torrent * tor, uint8_t from,
1768                  const tr_pex * pex, int8_t seedProbability )
1769{
1770    if( tr_isPex( pex ) ) /* safeguard against corrupt data */
1771    {
1772        Torrent * t = tor->torrentPeers;
1773        managerLock( t->manager );
1774
1775        if( !tr_sessionIsAddressBlocked( t->manager->session, &pex->addr ) )
1776            if( tr_isValidPeerAddress( &pex->addr, pex->port ) )
1777                ensureAtomExists( t, &pex->addr, pex->port, pex->flags, seedProbability, from );
1778
1779        managerUnlock( t->manager );
1780    }
1781}
1782
1783void
1784tr_peerMgrMarkAllAsSeeds( tr_torrent * tor )
1785{
1786    Torrent * t = tor->torrentPeers;
1787    const int n = tr_ptrArraySize( &t->pool );
1788    struct peer_atom ** it = (struct peer_atom**) tr_ptrArrayBase( &t->pool );
1789    struct peer_atom ** end = it + n;
1790
1791    while( it != end )
1792        atomSetSeed( *it++ );
1793}
1794
1795tr_pex *
1796tr_peerMgrCompactToPex( const void *    compact,
1797                        size_t          compactLen,
1798                        const uint8_t * added_f,
1799                        size_t          added_f_len,
1800                        size_t *        pexCount )
1801{
1802    size_t          i;
1803    size_t          n = compactLen / 6;
1804    const uint8_t * walk = compact;
1805    tr_pex *        pex = tr_new0( tr_pex, n );
1806
1807    for( i = 0; i < n; ++i )
1808    {
1809        pex[i].addr.type = TR_AF_INET;
1810        memcpy( &pex[i].addr.addr, walk, 4 ); walk += 4;
1811        memcpy( &pex[i].port, walk, 2 ); walk += 2;
1812        if( added_f && ( n == added_f_len ) )
1813            pex[i].flags = added_f[i];
1814    }
1815
1816    *pexCount = n;
1817    return pex;
1818}
1819
1820tr_pex *
1821tr_peerMgrCompact6ToPex( const void    * compact,
1822                         size_t          compactLen,
1823                         const uint8_t * added_f,
1824                         size_t          added_f_len,
1825                         size_t        * pexCount )
1826{
1827    size_t          i;
1828    size_t          n = compactLen / 18;
1829    const uint8_t * walk = compact;
1830    tr_pex *        pex = tr_new0( tr_pex, n );
1831
1832    for( i = 0; i < n; ++i )
1833    {
1834        pex[i].addr.type = TR_AF_INET6;
1835        memcpy( &pex[i].addr.addr.addr6.s6_addr, walk, 16 ); walk += 16;
1836        memcpy( &pex[i].port, walk, 2 ); walk += 2;
1837        if( added_f && ( n == added_f_len ) )
1838            pex[i].flags = added_f[i];
1839    }
1840
1841    *pexCount = n;
1842    return pex;
1843}
1844
1845tr_pex *
1846tr_peerMgrArrayToPex( const void * array,
1847                      size_t       arrayLen,
1848                      size_t      * pexCount )
1849{
1850    size_t          i;
1851    size_t          n = arrayLen / ( sizeof( tr_address ) + 2 );
1852    /*size_t          n = arrayLen / sizeof( tr_peerArrayElement );*/
1853    const uint8_t * walk = array;
1854    tr_pex        * pex = tr_new0( tr_pex, n );
1855
1856    for( i = 0 ; i < n ; i++ ) {
1857        memcpy( &pex[i].addr, walk, sizeof( tr_address ) );
1858        memcpy( &pex[i].port, walk + sizeof( tr_address ), 2 );
1859        pex[i].flags = 0x00;
1860        walk += sizeof( tr_address ) + 2;
1861    }
1862
1863    *pexCount = n;
1864    return pex;
1865}
1866
1867/**
1868***
1869**/
1870
1871void
1872tr_peerMgrSetBlame( tr_torrent     * tor,
1873                    tr_piece_index_t pieceIndex,
1874                    int              success )
1875{
1876    if( !success )
1877    {
1878        int        peerCount, i;
1879        Torrent *  t = tor->torrentPeers;
1880        tr_peer ** peers;
1881
1882        assert( torrentIsLocked( t ) );
1883
1884        peers = (tr_peer **) tr_ptrArrayPeek( &t->peers, &peerCount );
1885        for( i = 0; i < peerCount; ++i )
1886        {
1887            tr_peer * peer = peers[i];
1888            if( tr_bitfieldHas( peer->blame, pieceIndex ) )
1889            {
1890                tordbg( t, "peer %s contributed to corrupt piece (%d); now has %d strikes",
1891                        tr_atomAddrStr( peer->atom ),
1892                        pieceIndex, (int)peer->strikes + 1 );
1893                addStrike( t, peer );
1894            }
1895        }
1896    }
1897}
1898
1899int
1900tr_pexCompare( const void * va, const void * vb )
1901{
1902    const tr_pex * a = va;
1903    const tr_pex * b = vb;
1904    int i;
1905
1906    assert( tr_isPex( a ) );
1907    assert( tr_isPex( b ) );
1908
1909    if(( i = tr_compareAddresses( &a->addr, &b->addr )))
1910        return i;
1911
1912    if( a->port != b->port )
1913        return a->port < b->port ? -1 : 1;
1914
1915    return 0;
1916}
1917
1918#if 0
1919static int
1920peerPrefersCrypto( const tr_peer * peer )
1921{
1922    if( peer->encryption_preference == ENCRYPTION_PREFERENCE_YES )
1923        return TRUE;
1924
1925    if( peer->encryption_preference == ENCRYPTION_PREFERENCE_NO )
1926        return FALSE;
1927
1928    return tr_peerIoIsEncrypted( peer->io );
1929}
1930#endif
1931
1932/* better goes first */
1933static int
1934compareAtomsByUsefulness( const void * va, const void *vb )
1935{
1936    const struct peer_atom * a = * (const struct peer_atom**) va;
1937    const struct peer_atom * b = * (const struct peer_atom**) vb;
1938
1939    assert( tr_isAtom( a ) );
1940    assert( tr_isAtom( b ) );
1941
1942    if( a->piece_data_time != b->piece_data_time )
1943        return a->piece_data_time > b->piece_data_time ? -1 : 1;
1944    if( a->from != b->from )
1945        return a->from < b->from ? -1 : 1;
1946    if( a->numFails != b->numFails )
1947        return a->numFails < b->numFails ? -1 : 1;
1948
1949    return 0;
1950}
1951
1952int
1953tr_peerMgrGetPeers( tr_torrent   * tor,
1954                    tr_pex      ** setme_pex,
1955                    uint8_t        af,
1956                    uint8_t        list_mode,
1957                    int            maxCount )
1958{
1959    int i;
1960    int n;
1961    int count = 0;
1962    int atomCount = 0;
1963    const Torrent * t = tor->torrentPeers;
1964    struct peer_atom ** atoms = NULL;
1965    tr_pex * pex;
1966    tr_pex * walk;
1967
1968    assert( tr_isTorrent( tor ) );
1969    assert( setme_pex != NULL );
1970    assert( af==TR_AF_INET || af==TR_AF_INET6 );
1971    assert( list_mode==TR_PEERS_CONNECTED || list_mode==TR_PEERS_ALL );
1972
1973    managerLock( t->manager );
1974
1975    /**
1976    ***  build a list of atoms
1977    **/
1978
1979    if( list_mode == TR_PEERS_CONNECTED ) /* connected peers only */
1980    {
1981        int i;
1982        const tr_peer ** peers = (const tr_peer **) tr_ptrArrayBase( &t->peers );
1983        atomCount = tr_ptrArraySize( &t->peers );
1984        atoms = tr_new( struct peer_atom *, atomCount );
1985        for( i=0; i<atomCount; ++i )
1986            atoms[i] = peers[i]->atom;
1987    }
1988    else /* TR_PEERS_ALL */
1989    {
1990        const struct peer_atom ** atomsBase = (const struct peer_atom**) tr_ptrArrayBase( &t->pool );
1991        atomCount = tr_ptrArraySize( &t->pool );
1992        atoms = tr_memdup( atomsBase, atomCount * sizeof( struct peer_atom * ) );
1993    }
1994
1995    qsort( atoms, atomCount, sizeof( struct peer_atom * ), compareAtomsByUsefulness );
1996
1997    /**
1998    ***  add the first N of them into our return list
1999    **/
2000
2001    n = MIN( atomCount, maxCount );
2002    pex = walk = tr_new0( tr_pex, n );
2003
2004    for( i=0; i<atomCount && count<n; ++i )
2005    {
2006        const struct peer_atom * atom = atoms[i];
2007        if( atom->addr.type == af )
2008        {
2009            assert( tr_isAddress( &atom->addr ) );
2010            walk->addr = atom->addr;
2011            walk->port = atom->port;
2012            walk->flags = atom->flags;
2013            ++count;
2014            ++walk;
2015        }
2016    }
2017
2018    qsort( pex, count, sizeof( tr_pex ), tr_pexCompare );
2019
2020    assert( ( walk - pex ) == count );
2021    *setme_pex = pex;
2022
2023    /* cleanup */
2024    tr_free( atoms );
2025    managerUnlock( t->manager );
2026    return count;
2027}
2028
2029static void atomPulse      ( int, short, void * );
2030static void bandwidthPulse ( int, short, void * );
2031static void rechokePulse   ( int, short, void * );
2032static void reconnectPulse ( int, short, void * );
2033
2034static struct event *
2035createTimer( int msec, void (*callback)(int, short, void *), void * cbdata )
2036{
2037    struct event * timer = tr_new0( struct event, 1 );
2038    evtimer_set( timer, callback, cbdata );
2039    tr_timerAddMsec( timer, msec );
2040    return timer;
2041}
2042
2043static void
2044ensureMgrTimersExist( struct tr_peerMgr * m )
2045{
2046    if( m->atomTimer == NULL )
2047        m->atomTimer = createTimer( ATOM_PERIOD_MSEC, atomPulse, m );
2048
2049    if( m->bandwidthTimer == NULL )
2050        m->bandwidthTimer = createTimer( BANDWIDTH_PERIOD_MSEC, bandwidthPulse, m );
2051
2052    if( m->rechokeTimer == NULL )
2053        m->rechokeTimer = createTimer( RECHOKE_PERIOD_MSEC, rechokePulse, m );
2054
2055   if( m->refillUpkeepTimer == NULL )
2056        m->refillUpkeepTimer = createTimer( REFILL_UPKEEP_PERIOD_MSEC, refillUpkeep, m );
2057}
2058
2059void
2060tr_peerMgrStartTorrent( tr_torrent * tor )
2061{
2062    Torrent * t = tor->torrentPeers;
2063
2064    assert( t != NULL );
2065    managerLock( t->manager );
2066    ensureMgrTimersExist( t->manager );
2067
2068    t->isRunning = TRUE;
2069
2070    rechokePulse( 0, 0, t->manager );
2071    managerUnlock( t->manager );
2072}
2073
2074static void
2075stopTorrent( Torrent * t )
2076{
2077    int i, n;
2078
2079    assert( torrentIsLocked( t ) );
2080
2081    t->isRunning = FALSE;
2082
2083    /* disconnect the peers. */
2084    for( i=0, n=tr_ptrArraySize( &t->peers ); i<n; ++i )
2085        peerDestructor( t, tr_ptrArrayNth( &t->peers, i ) );
2086    tr_ptrArrayClear( &t->peers );
2087
2088    /* disconnect the handshakes.  handshakeAbort calls handshakeDoneCB(),
2089     * which removes the handshake from t->outgoingHandshakes... */
2090    while( !tr_ptrArrayEmpty( &t->outgoingHandshakes ) )
2091        tr_handshakeAbort( tr_ptrArrayNth( &t->outgoingHandshakes, 0 ) );
2092}
2093
2094void
2095tr_peerMgrStopTorrent( tr_torrent * tor )
2096{
2097    Torrent * t = tor->torrentPeers;
2098
2099    managerLock( t->manager );
2100
2101    stopTorrent( t );
2102
2103    managerUnlock( t->manager );
2104}
2105
2106void
2107tr_peerMgrAddTorrent( tr_peerMgr * manager,
2108                      tr_torrent * tor )
2109{
2110    managerLock( manager );
2111
2112    assert( tor );
2113    assert( tor->torrentPeers == NULL );
2114
2115    tor->torrentPeers = torrentConstructor( manager, tor );
2116
2117    managerUnlock( manager );
2118}
2119
2120void
2121tr_peerMgrRemoveTorrent( tr_torrent * tor )
2122{
2123    tr_torrentLock( tor );
2124
2125    stopTorrent( tor->torrentPeers );
2126    torrentDestructor( tor->torrentPeers );
2127
2128    tr_torrentUnlock( tor );
2129}
2130
2131void
2132tr_peerMgrTorrentAvailability( const tr_torrent * tor,
2133                               int8_t           * tab,
2134                               unsigned int       tabCount )
2135{
2136    tr_piece_index_t   i;
2137    const Torrent *    t;
2138    float              interval;
2139    tr_bool            isSeed;
2140    int                peerCount;
2141    const tr_peer **   peers;
2142    tr_torrentLock( tor );
2143
2144    t = tor->torrentPeers;
2145    tor = t->tor;
2146    interval = tor->info.pieceCount / (float)tabCount;
2147    isSeed = tor && ( tr_cpGetStatus ( &tor->completion ) == TR_SEED );
2148    peers = (const tr_peer **) tr_ptrArrayBase( &t->peers );
2149    peerCount = tr_ptrArraySize( &t->peers );
2150
2151    memset( tab, 0, tabCount );
2152
2153    for( i = 0; tor && i < tabCount; ++i )
2154    {
2155        const int piece = i * interval;
2156
2157        if( isSeed || tr_cpPieceIsComplete( &tor->completion, piece ) )
2158            tab[i] = -1;
2159        else if( peerCount ) {
2160            int j;
2161            for( j = 0; j < peerCount; ++j )
2162                if( tr_bitsetHas( &peers[j]->have, i ) )
2163                    ++tab[i];
2164        }
2165    }
2166
2167    tr_torrentUnlock( tor );
2168}
2169
2170/* Returns the pieces that are available from peers */
2171tr_bitfield*
2172tr_peerMgrGetAvailable( const tr_torrent * tor )
2173{
2174    int i;
2175    int peerCount;
2176    Torrent * t = tor->torrentPeers;
2177    const tr_peer ** peers;
2178    tr_bitfield * pieces;
2179    managerLock( t->manager );
2180
2181    pieces = tr_bitfieldNew( t->tor->info.pieceCount );
2182    peerCount = tr_ptrArraySize( &t->peers );
2183    peers = (const tr_peer**) tr_ptrArrayBase( &t->peers );
2184    for( i=0; i<peerCount; ++i )
2185        tr_bitsetOr( pieces, &peers[i]->have );
2186
2187    managerUnlock( t->manager );
2188    return pieces;
2189}
2190
2191void
2192tr_peerMgrTorrentStats( tr_torrent       * tor,
2193                        int              * setmePeersKnown,
2194                        int              * setmePeersConnected,
2195                        int              * setmeSeedsConnected,
2196                        int              * setmeWebseedsSendingToUs,
2197                        int              * setmePeersSendingToUs,
2198                        int              * setmePeersGettingFromUs,
2199                        int              * setmePeersFrom )
2200{
2201    int i, size;
2202    const Torrent * t = tor->torrentPeers;
2203    const tr_peer ** peers;
2204    const tr_webseed ** webseeds;
2205
2206    managerLock( t->manager );
2207
2208    peers = (const tr_peer **) tr_ptrArrayBase( &t->peers );
2209    size = tr_ptrArraySize( &t->peers );
2210
2211    *setmePeersKnown           = tr_ptrArraySize( &t->pool );
2212    *setmePeersConnected       = 0;
2213    *setmeSeedsConnected       = 0;
2214    *setmePeersGettingFromUs   = 0;
2215    *setmePeersSendingToUs     = 0;
2216    *setmeWebseedsSendingToUs  = 0;
2217
2218    for( i=0; i<TR_PEER_FROM__MAX; ++i )
2219        setmePeersFrom[i] = 0;
2220
2221    for( i=0; i<size; ++i )
2222    {
2223        const tr_peer * peer = peers[i];
2224        const struct peer_atom * atom = peer->atom;
2225
2226        if( peer->io == NULL ) /* not connected */
2227            continue;
2228
2229        ++*setmePeersConnected;
2230
2231        ++setmePeersFrom[atom->from];
2232
2233        if( clientIsDownloadingFrom( tor, peer ) )
2234            ++*setmePeersSendingToUs;
2235
2236        if( clientIsUploadingTo( peer ) )
2237            ++*setmePeersGettingFromUs;
2238
2239        if( atomIsSeed( atom ) )
2240            ++*setmeSeedsConnected;
2241    }
2242
2243    webseeds = (const tr_webseed**) tr_ptrArrayBase( &t->webseeds );
2244    size = tr_ptrArraySize( &t->webseeds );
2245    for( i=0; i<size; ++i )
2246        if( tr_webseedIsActive( webseeds[i] ) )
2247            ++*setmeWebseedsSendingToUs;
2248
2249    managerUnlock( t->manager );
2250}
2251
2252float
2253tr_peerMgrGetWebseedSpeed( const tr_torrent * tor, uint64_t now )
2254{
2255    int i;
2256    float tmp;
2257    float ret = 0;
2258
2259    const Torrent * t = tor->torrentPeers;
2260    const int n = tr_ptrArraySize( &t->webseeds );
2261    const tr_webseed ** webseeds = (const tr_webseed**) tr_ptrArrayBase( &t->webseeds );
2262
2263    for( i=0; i<n; ++i )
2264        if( tr_webseedGetSpeed( webseeds[i], now, &tmp ) )
2265            ret += tmp;
2266
2267    return ret;
2268}
2269
2270
2271float*
2272tr_peerMgrWebSpeeds( const tr_torrent * tor )
2273{
2274    const Torrent * t = tor->torrentPeers;
2275    const tr_webseed ** webseeds;
2276    int i;
2277    int webseedCount;
2278    float * ret;
2279    uint64_t now;
2280
2281    assert( t->manager );
2282    managerLock( t->manager );
2283
2284    webseeds = (const tr_webseed**) tr_ptrArrayBase( &t->webseeds );
2285    webseedCount = tr_ptrArraySize( &t->webseeds );
2286    assert( webseedCount == tor->info.webseedCount );
2287    ret = tr_new0( float, webseedCount );
2288    now = tr_date( );
2289
2290    for( i=0; i<webseedCount; ++i )
2291        if( !tr_webseedGetSpeed( webseeds[i], now, &ret[i] ) )
2292            ret[i] = -1.0;
2293
2294    managerUnlock( t->manager );
2295    return ret;
2296}
2297
2298double
2299tr_peerGetPieceSpeed( const tr_peer * peer, uint64_t now, tr_direction direction )
2300{
2301    return peer->io ? tr_peerIoGetPieceSpeed( peer->io, now, direction ) : 0.0;
2302}
2303
2304
2305struct tr_peer_stat *
2306tr_peerMgrPeerStats( const tr_torrent    * tor,
2307                     int                 * setmeCount )
2308{
2309    int i, size;
2310    const Torrent * t = tor->torrentPeers;
2311    const tr_peer ** peers;
2312    tr_peer_stat * ret;
2313    uint64_t now;
2314
2315    assert( t->manager );
2316    managerLock( t->manager );
2317
2318    size = tr_ptrArraySize( &t->peers );
2319    peers = (const tr_peer**) tr_ptrArrayBase( &t->peers );
2320    ret = tr_new0( tr_peer_stat, size );
2321    now = tr_date( );
2322
2323    for( i=0; i<size; ++i )
2324    {
2325        char *                   pch;
2326        const tr_peer *          peer = peers[i];
2327        const struct peer_atom * atom = peer->atom;
2328        tr_peer_stat *           stat = ret + i;
2329
2330        tr_ntop( &atom->addr, stat->addr, sizeof( stat->addr ) );
2331        tr_strlcpy( stat->client, ( peer->client ? peer->client : "" ),
2332                   sizeof( stat->client ) );
2333        stat->port                = ntohs( peer->atom->port );
2334        stat->from                = atom->from;
2335        stat->progress            = peer->progress;
2336        stat->isEncrypted         = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
2337        stat->rateToPeer          = tr_peerGetPieceSpeed( peer, now, TR_CLIENT_TO_PEER );
2338        stat->rateToClient        = tr_peerGetPieceSpeed( peer, now, TR_PEER_TO_CLIENT );
2339        stat->peerIsChoked        = peer->peerIsChoked;
2340        stat->peerIsInterested    = peer->peerIsInterested;
2341        stat->clientIsChoked      = peer->clientIsChoked;
2342        stat->clientIsInterested  = peer->clientIsInterested;
2343        stat->isIncoming          = tr_peerIoIsIncoming( peer->io );
2344        stat->isDownloadingFrom   = clientIsDownloadingFrom( tor, peer );
2345        stat->isUploadingTo       = clientIsUploadingTo( peer );
2346        stat->isSeed              = ( atom->uploadOnly == UPLOAD_ONLY_YES ) || ( peer->progress >= 1.0 );
2347
2348        stat->blocksToPeer        = tr_historyGet( peer->blocksSentToPeer,    now, CANCEL_HISTORY_SEC*1000 );
2349        stat->blocksToClient      = tr_historyGet( peer->blocksSentToClient,  now, CANCEL_HISTORY_SEC*1000 );
2350        stat->cancelsToPeer       = tr_historyGet( peer->cancelsSentToPeer,   now, CANCEL_HISTORY_SEC*1000 );
2351        stat->cancelsToClient     = tr_historyGet( peer->cancelsSentToClient, now, CANCEL_HISTORY_SEC*1000 );
2352
2353        stat->pendingReqsToPeer   = peer->pendingReqsToPeer;
2354        stat->pendingReqsToClient = peer->pendingReqsToClient;
2355
2356        pch = stat->flagStr;
2357        if( t->optimistic == peer ) *pch++ = 'O';
2358        if( stat->isDownloadingFrom ) *pch++ = 'D';
2359        else if( stat->clientIsInterested ) *pch++ = 'd';
2360        if( stat->isUploadingTo ) *pch++ = 'U';
2361        else if( stat->peerIsInterested ) *pch++ = 'u';
2362        if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ = 'K';
2363        if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
2364        if( stat->isEncrypted ) *pch++ = 'E';
2365        if( stat->from == TR_PEER_FROM_DHT ) *pch++ = 'H';
2366        if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
2367        if( stat->isIncoming ) *pch++ = 'I';
2368        *pch = '\0';
2369    }
2370
2371    *setmeCount = size;
2372
2373    managerUnlock( t->manager );
2374    return ret;
2375}
2376
2377/**
2378***
2379**/
2380
2381/* do we still want this piece and does the peer have it? */
2382static tr_bool
2383isPieceInteresting( const tr_torrent * tor, const tr_peer * peer, tr_piece_index_t index )
2384{
2385    return ( !tor->info.pieces[index].dnd ) /* we want it */
2386        && ( !tr_cpPieceIsComplete( &tor->completion, index ) )  /* we don't have it */
2387        && ( tr_bitsetHas( &peer->have, index ) ); /* peer has it */
2388}
2389
2390/* does this peer have any pieces that we want? */
2391static tr_bool
2392isPeerInteresting( const tr_torrent * tor, const tr_peer * peer )
2393{
2394    tr_piece_index_t i, n;
2395
2396    if ( tr_torrentIsSeed( tor ) )
2397        return FALSE;
2398
2399    if( !tr_torrentIsPieceTransferAllowed( tor, TR_PEER_TO_CLIENT ) )
2400        return FALSE;
2401
2402    for( i=0, n=tor->info.pieceCount; i<n; ++i )
2403        if( isPieceInteresting( tor, peer, i ) )
2404            return TRUE;
2405
2406    return FALSE;
2407}
2408
2409/* determines who we send "interested" messages to */
2410static void
2411rechokeDownloads( Torrent * t )
2412{
2413    int i;
2414    const uint64_t now = tr_date( );
2415    const int msec = 60 * 1000;
2416    const int MIN_INTERESTING_PEERS = 5;
2417    const int peerCount = tr_ptrArraySize( &t->peers );
2418    int maxPeers;
2419
2420    int badCount         = 0;
2421    int goodCount        = 0;
2422    int untestedCount    = 0;
2423    tr_peer ** bad       = tr_new( tr_peer*, peerCount );
2424    tr_peer ** good      = tr_new( tr_peer*, peerCount );
2425    tr_peer ** untested  = tr_new( tr_peer*, peerCount );
2426
2427    /* decide how many peers to be interested in */
2428    {
2429        int blocks = 0;
2430        int cancels = 0;
2431
2432        /* Count up how many blocks & cancels each peer has.
2433         *
2434         * There are two situations where we send out cancels --
2435         *
2436         * 1. We've got unresponsive peers, which is handled by deciding
2437         *    -which- peers to be interested in.
2438         *
2439         * 2. We've hit our bandwidth cap, which is handled by deciding
2440         *    -how many- peers to be interested in.
2441         *
2442         * We're working on 2. here, so we need to ignore unresponsive
2443         * peers in our calculations lest they confuse Transmission into
2444         * thinking it's hit its bandwidth cap.
2445         */
2446        for( i=0; i<peerCount; ++i )
2447        {
2448            const tr_peer * peer = tr_ptrArrayNth( &t->peers, i );
2449            const int b = tr_historyGet( peer->blocksSentToClient, now, msec );
2450            const int c = tr_historyGet( peer->cancelsSentToPeer, now, msec );
2451
2452            if( b == 0 ) /* ignore unresponsive peers, as described above */
2453                continue;
2454
2455            blocks += b;
2456            cancels += c;
2457        }
2458
2459        if( !t->interestedCount )
2460        {
2461            /* this is the torrent's first time to call this function...
2462             * start off optimistically by allowing interest in many peers */
2463            maxPeers = t->tor->maxConnectedPeers;
2464        }
2465        else if( !blocks )
2466        {
2467            /* we've gotten cancels but zero blocks...
2468             * something is seriously wrong.  throttle back sharply */
2469            maxPeers = t->interestedCount * 0.5;
2470        }
2471        else
2472        {
2473            const double cancelRate = cancels / (double)(cancels + blocks);
2474                 if( cancelRate >= 0.20 ) maxPeers = t->interestedCount * 0.7;
2475            else if( cancelRate >= 0.10 ) maxPeers = t->interestedCount * 0.8;
2476            else if( cancelRate >= 0.05 ) maxPeers = t->interestedCount * 0.9;
2477            else if( cancelRate >= 0.01 ) maxPeers = t->interestedCount;
2478            else                          maxPeers = t->interestedCount + 1;
2479
2480            /* if things are getting worse, don't add more peers */
2481            if( ( t->cancelRate > 0.01 ) && ( cancelRate > t->cancelRate ) )
2482                maxPeers = MIN( maxPeers, t->interestedCount );
2483
2484            t->cancelRate = cancelRate;
2485
2486            tordbg( t, "cancel rate is %.3f -- changing the "
2487                       "number of peers we're interested in from %d to %d",
2488                       cancelRate, t->interestedCount, maxPeers );
2489        }
2490    }
2491
2492    /* don't let the previous paragraph's number tweaking go too far... */
2493    if( maxPeers < MIN_INTERESTING_PEERS )
2494        maxPeers = MIN_INTERESTING_PEERS;
2495    if( maxPeers > t->tor->maxConnectedPeers )
2496        maxPeers = t->tor->maxConnectedPeers;
2497
2498    /* separate the peers into "good" (ones with a low cancel-to-block ratio),
2499     * untested peers, and "bad" (ones with a high cancel-to-block ratio).
2500     * That's the order in which we'll choose who to show interest in */
2501    for( i=0; i<peerCount; ++i )
2502    {
2503        tr_peer * peer = tr_ptrArrayNth( &t->peers, i );
2504
2505        if( !isPeerInteresting( t->tor, peer ) )
2506        {
2507            tr_peerMsgsSetInterested( peer->msgs, FALSE );
2508        }
2509        else
2510        {
2511            const int blocks = tr_historyGet( peer->blocksSentToClient, now, msec );
2512            const int cancels = tr_historyGet( peer->cancelsSentToPeer, now, msec );
2513
2514            if( !blocks && !cancels )
2515                untested[untestedCount++] = peer;
2516            else if( !cancels )
2517                good[goodCount++] = peer;
2518            else if( !blocks )
2519                bad[badCount++] = peer;
2520            else if( ( cancels * 10 ) < blocks )
2521                good[goodCount++] = peer;
2522            else
2523                bad[badCount++] = peer;
2524        }
2525    }
2526
2527    t->interestedCount = 0;
2528
2529    /* We've decided (1) how many peers to be interested in,
2530     * and (2) which peers are the best candidates,
2531     * Now it's time to update our `interest' flags. */
2532    for( i=0; i<goodCount; ++i ) {
2533        const tr_bool b = t->interestedCount < maxPeers;
2534        tr_peerMsgsSetInterested( good[i]->msgs, b );
2535        if( b )
2536            ++t->interestedCount;
2537    }
2538    for( i=0; i<untestedCount; ++i ) {
2539        const tr_bool b = t->interestedCount < maxPeers;
2540        tr_peerMsgsSetInterested( untested[i]->msgs, b );
2541        if( b )
2542            ++t->interestedCount;
2543    }
2544    for( i=0; i<badCount; ++i ) {
2545        const tr_bool b = t->interestedCount < maxPeers;
2546        tr_peerMsgsSetInterested( bad[i]->msgs, b );
2547        if( b )
2548            ++t->interestedCount;
2549    }
2550
2551/*fprintf( stderr, "num interested: %d\n", t->interestedCount );*/
2552
2553    /* cleanup */
2554    tr_free( untested );
2555    tr_free( good );
2556    tr_free( bad );
2557}
2558
2559/**
2560***
2561**/
2562
2563struct ChokeData
2564{
2565    tr_bool         doUnchoke;
2566    tr_bool         isInterested;
2567    tr_bool         isChoked;
2568    int             rate;
2569    tr_peer *       peer;
2570};
2571
2572static int
2573compareChoke( const void * va,
2574              const void * vb )
2575{
2576    const struct ChokeData * a = va;
2577    const struct ChokeData * b = vb;
2578
2579    if( a->rate != b->rate ) /* prefer higher overall speeds */
2580        return a->rate > b->rate ? -1 : 1;
2581
2582    if( a->isChoked != b->isChoked ) /* prefer unchoked */
2583        return a->isChoked ? 1 : -1;
2584
2585    return 0;
2586}
2587
2588/* is this a new connection? */
2589static int
2590isNew( const tr_peer * peer )
2591{
2592    return peer && peer->io && tr_peerIoGetAge( peer->io ) < 45;
2593}
2594
2595static void
2596rechokeUploads( Torrent * t, const uint64_t now )
2597{
2598    int i, size, unchokedInterested;
2599    const int peerCount = tr_ptrArraySize( &t->peers );
2600    tr_peer ** peers = (tr_peer**) tr_ptrArrayBase( &t->peers );
2601    struct ChokeData * choke = tr_new0( struct ChokeData, peerCount );
2602    const tr_session * session = t->manager->session;
2603    const int chokeAll = !tr_torrentIsPieceTransferAllowed( t->tor, TR_CLIENT_TO_PEER );
2604
2605    assert( torrentIsLocked( t ) );
2606
2607    /* sort the peers by preference and rate */
2608    for( i = 0, size = 0; i < peerCount; ++i )
2609    {
2610        tr_peer * peer = peers[i];
2611        struct peer_atom * atom = peer->atom;
2612
2613        if( peer->progress >= 1.0 ) /* choke all seeds */
2614        {
2615            tr_peerMsgsSetChoke( peer->msgs, TRUE );
2616        }
2617        else if( atom->uploadOnly == UPLOAD_ONLY_YES ) /* choke partial seeds */
2618        {
2619            tr_peerMsgsSetChoke( peer->msgs, TRUE );
2620        }
2621        else if( chokeAll ) /* choke everyone if we're not uploading */
2622        {
2623            tr_peerMsgsSetChoke( peer->msgs, TRUE );
2624        }
2625        else
2626        {
2627            struct ChokeData * n = &choke[size++];
2628            n->peer         = peer;
2629            n->isInterested = peer->peerIsInterested;
2630            n->isChoked     = peer->peerIsChoked;
2631            n->rate         = tr_peerGetPieceSpeed( peer, now, TR_CLIENT_TO_PEER ) * 1024;
2632        }
2633    }
2634
2635    qsort( choke, size, sizeof( struct ChokeData ), compareChoke );
2636
2637    /**
2638     * Reciprocation and number of uploads capping is managed by unchoking
2639     * the N peers which have the best upload rate and are interested.
2640     * This maximizes the client's download rate. These N peers are
2641     * referred to as downloaders, because they are interested in downloading
2642     * from the client.
2643     *
2644     * Peers which have a better upload rate (as compared to the downloaders)
2645     * but aren't interested get unchoked. If they become interested, the
2646     * downloader with the worst upload rate gets choked. If a client has
2647     * a complete file, it uses its upload rate rather than its download
2648     * rate to decide which peers to unchoke.
2649     */
2650    unchokedInterested = 0;
2651    for( i=0; i<size && unchokedInterested<session->uploadSlotsPerTorrent; ++i ) {
2652        choke[i].doUnchoke = 1;
2653        if( choke[i].isInterested )
2654            ++unchokedInterested;
2655    }
2656
2657    /* optimistic unchoke */
2658    if( i < size )
2659    {
2660        int n;
2661        struct ChokeData * c;
2662        tr_ptrArray randPool = TR_PTR_ARRAY_INIT;
2663
2664        for( ; i<size; ++i )
2665        {
2666            if( choke[i].isInterested )
2667            {
2668                const tr_peer * peer = choke[i].peer;
2669                int x = 1, y;
2670                if( isNew( peer ) ) x *= 3;
2671                for( y=0; y<x; ++y )
2672                    tr_ptrArrayAppend( &randPool, &choke[i] );
2673            }
2674        }
2675
2676        if(( n = tr_ptrArraySize( &randPool )))
2677        {
2678            c = tr_ptrArrayNth( &randPool, tr_cryptoWeakRandInt( n ));
2679            c->doUnchoke = 1;
2680            t->optimistic = c->peer;
2681        }
2682
2683        tr_ptrArrayDestruct( &randPool, NULL );
2684    }
2685
2686    for( i=0; i<size; ++i )
2687        tr_peerMsgsSetChoke( choke[i].peer->msgs, !choke[i].doUnchoke );
2688
2689    /* cleanup */
2690    tr_free( choke );
2691}
2692
2693static void
2694rechokePulse( int foo UNUSED, short bar UNUSED, void * vmgr )
2695{
2696    uint64_t now;
2697    tr_torrent * tor = NULL;
2698    tr_peerMgr * mgr = vmgr;
2699    managerLock( mgr );
2700
2701    now = tr_date( );
2702    while(( tor = tr_torrentNext( mgr->session, tor ))) {
2703        if( tor->isRunning ) {
2704            rechokeUploads( tor->torrentPeers, now );
2705            if( !tr_torrentIsSeed( tor ) )
2706                rechokeDownloads( tor->torrentPeers );
2707        }
2708    }
2709
2710    tr_timerAddMsec( mgr->rechokeTimer, RECHOKE_PERIOD_MSEC );
2711    managerUnlock( mgr );
2712}
2713
2714/***
2715****
2716****  Life and Death
2717****
2718***/
2719
2720typedef enum
2721{
2722    TR_CAN_KEEP,
2723    TR_CAN_CLOSE,
2724    TR_MUST_CLOSE,
2725}
2726tr_close_type_t;
2727
2728static tr_close_type_t
2729shouldPeerBeClosed( const Torrent    * t,
2730                    const tr_peer    * peer,
2731                    int                peerCount,
2732                    const time_t       now )
2733{
2734    const tr_torrent *       tor = t->tor;
2735    const struct peer_atom * atom = peer->atom;
2736
2737    /* if it's marked for purging, close it */
2738    if( peer->doPurge )
2739    {
2740        tordbg( t, "purging peer %s because its doPurge flag is set",
2741                tr_atomAddrStr( atom ) );
2742        return TR_MUST_CLOSE;
2743    }
2744
2745    /* if we're seeding and the peer has everything we have,
2746     * and enough time has passed for a pex exchange, then disconnect */
2747    if( tr_torrentIsSeed( tor ) )
2748    {
2749        tr_bool peerHasEverything;
2750
2751        if( atom->seedProbability != -1 )
2752        {
2753            peerHasEverything = atomIsSeed( atom );
2754        }
2755        else
2756        {
2757            tr_bitfield * tmp = tr_bitfieldDup( tr_cpPieceBitfield( &tor->completion ) );
2758            tr_bitsetDifference( tmp, &peer->have );
2759            peerHasEverything = tr_bitfieldCountTrueBits( tmp ) == 0;
2760            tr_bitfieldFree( tmp );
2761        }
2762
2763        if( peerHasEverything && ( !tr_torrentAllowsPex(tor) || (now-atom->time>=30 )))
2764        {
2765            tordbg( t, "purging peer %s because we're both seeds",
2766                    tr_atomAddrStr( atom ) );
2767            return TR_MUST_CLOSE;
2768        }
2769    }
2770
2771    /* disconnect if it's been too long since piece data has been transferred.
2772     * this is on a sliding scale based on number of available peers... */
2773    {
2774        const int relaxStrictnessIfFewerThanN = (int)( ( getMaxPeerCount( tor ) * 0.9 ) + 0.5 );
2775        /* if we have >= relaxIfFewerThan, strictness is 100%.
2776         * if we have zero connections, strictness is 0% */
2777        const float strictness = peerCount >= relaxStrictnessIfFewerThanN
2778                               ? 1.0
2779                               : peerCount / (float)relaxStrictnessIfFewerThanN;
2780        const int lo = MIN_UPLOAD_IDLE_SECS;
2781        const int hi = MAX_UPLOAD_IDLE_SECS;
2782        const int limit = hi - ( ( hi - lo ) * strictness );
2783        const int idleTime = now - MAX( atom->time, atom->piece_data_time );
2784/*fprintf( stderr, "strictness is %.3f, limit is %d seconds... time since connect is %d, time since piece is %d ... idleTime is %d, doPurge is %d\n", (double)strictness, limit, (int)(now - atom->time), (int)(now - atom->piece_data_time), idleTime, idleTime > limit );*/
2785        if( idleTime > limit ) {
2786            tordbg( t, "purging peer %s because it's been %d secs since we shared anything",
2787                       tr_atomAddrStr( atom ), idleTime );
2788            return TR_CAN_CLOSE;
2789        }
2790    }
2791
2792    return TR_CAN_KEEP;
2793}
2794
2795static void sortPeersByLivelinessReverse( tr_peer ** peers, void ** clientData, int n, uint64_t now );
2796
2797static tr_peer **
2798getPeersToClose( Torrent * t, tr_close_type_t closeType, const time_t now, int * setmeSize )
2799{
2800    int i, peerCount, outsize;
2801    tr_peer ** peers = (tr_peer**) tr_ptrArrayPeek( &t->peers, &peerCount );
2802    struct tr_peer ** ret = tr_new( tr_peer *, peerCount );
2803
2804    assert( torrentIsLocked( t ) );
2805
2806    for( i = outsize = 0; i < peerCount; ++i )
2807        if( shouldPeerBeClosed( t, peers[i], peerCount, now ) == closeType )
2808            ret[outsize++] = peers[i];
2809
2810    sortPeersByLivelinessReverse ( ret, NULL, outsize, tr_date( ) );
2811
2812    *setmeSize = outsize;
2813    return ret;
2814}
2815
2816static int
2817getReconnectIntervalSecs( const struct peer_atom * atom, const time_t now )
2818{
2819    int sec;
2820
2821    /* if we were recently connected to this peer and transferring piece
2822     * data, try to reconnect to them sooner rather that later -- we don't
2823     * want network troubles to get in the way of a good peer. */
2824    if( ( now - atom->piece_data_time ) <= ( MINIMUM_RECONNECT_INTERVAL_SECS * 2 ) )
2825        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2826
2827    /* don't allow reconnects more often than our minimum */
2828    else if( ( now - atom->time ) < MINIMUM_RECONNECT_INTERVAL_SECS )
2829        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2830
2831    /* otherwise, the interval depends on how many times we've tried
2832     * and failed to connect to the peer */
2833    else switch( atom->numFails ) {
2834        case 0: sec = 0; break;
2835        case 1: sec = 5; break;
2836        case 2: sec = 2 * 60; break;
2837        case 3: sec = 15 * 60; break;
2838        case 4: sec = 30 * 60; break;
2839        case 5: sec = 60 * 60; break;
2840        default: sec = 120 * 60; break;
2841    }
2842
2843    /* penalize peers that were unreachable the last time we tried */
2844    if( atom->myflags & MYFLAG_UNREACHABLE )
2845        sec += sec;
2846
2847    dbgmsg( "reconnect interval for %s is %d seconds", tr_atomAddrStr( atom ), sec );
2848    return sec;
2849}
2850
2851static void
2852closePeer( Torrent * t, tr_peer * peer )
2853{
2854    struct peer_atom * atom;
2855
2856    assert( t != NULL );
2857    assert( peer != NULL );
2858
2859    atom = peer->atom;
2860
2861    /* if we transferred piece data, then they might be good peers,
2862       so reset their `numFails' weight to zero.  otherwise we connected
2863       to them fruitlessly, so mark it as another fail */
2864    if( atom->piece_data_time ) {
2865        tordbg( t, "resetting atom %s numFails to 0", tr_atomAddrStr(atom) );
2866        atom->numFails = 0;
2867    } else {
2868        ++atom->numFails;
2869        tordbg( t, "incremented atom %s numFails to %d", tr_atomAddrStr(atom), (int)atom->numFails );
2870    }
2871
2872    tordbg( t, "removing bad peer %s", tr_peerIoGetAddrStr( peer->io ) );
2873    removePeer( t, peer );
2874}
2875
2876static void
2877closeBadPeers( Torrent * t )
2878{
2879    const time_t  now = tr_time( );
2880
2881    if( !t->isRunning )
2882    {
2883        removeAllPeers( t );
2884    }
2885    else
2886    {
2887        int i;
2888        int mustCloseCount;
2889        struct tr_peer ** mustClose;
2890
2891        /* disconnect the really bad peers */
2892        mustClose = getPeersToClose( t, TR_MUST_CLOSE, now, &mustCloseCount );
2893        for( i=0; i<mustCloseCount; ++i )
2894            closePeer( t, mustClose[i] );
2895        tr_free( mustClose );
2896    }
2897}
2898
2899struct peer_liveliness
2900{
2901    tr_peer * peer;
2902    void * clientData;
2903    time_t pieceDataTime;
2904    time_t time;
2905    int speed;
2906    tr_bool doPurge;
2907};
2908
2909static int
2910comparePeerLiveliness( const void * va, const void * vb )
2911{
2912    const struct peer_liveliness * a = va;
2913    const struct peer_liveliness * b = vb;
2914
2915    if( a->doPurge != b->doPurge )
2916        return a->doPurge ? 1 : -1;
2917
2918    if( a->speed != b->speed ) /* faster goes first */
2919        return a->speed > b->speed ? -1 : 1;
2920
2921    /* the one to give us data more recently goes first */
2922    if( a->pieceDataTime != b->pieceDataTime )
2923        return a->pieceDataTime > b->pieceDataTime ? -1 : 1;
2924
2925    /* the one we connected to most recently goes first */
2926    if( a->time != b->time )
2927        return a->time > b->time ? -1 : 1;
2928
2929    return 0;
2930}
2931
2932static int
2933comparePeerLivelinessReverse( const void * va, const void * vb )
2934{
2935    return -comparePeerLiveliness (va, vb);
2936}
2937
2938static void
2939sortPeersByLivelinessImpl( tr_peer  ** peers,
2940                           void     ** clientData,
2941                           int         n,
2942                           uint64_t    now,
2943                           int (*compare) ( const void *va, const void *vb ) )
2944{
2945    int i;
2946    struct peer_liveliness *lives, *l;
2947
2948    /* build a sortable array of peer + extra info */
2949    lives = l = tr_new0( struct peer_liveliness, n );
2950    for( i=0; i<n; ++i, ++l )
2951    {
2952        tr_peer * p = peers[i];
2953        l->peer = p;
2954        l->doPurge = p->doPurge;
2955        l->pieceDataTime = p->atom->piece_data_time;
2956        l->time = p->atom->time;
2957        l->speed = 1024.0 * (   tr_peerGetPieceSpeed( p, now, TR_UP )
2958                              + tr_peerGetPieceSpeed( p, now, TR_DOWN ) );
2959        if( clientData )
2960            l->clientData = clientData[i];
2961    }
2962
2963    /* sort 'em */
2964    assert( n == ( l - lives ) );
2965    qsort( lives, n, sizeof( struct peer_liveliness ), compare );
2966
2967    /* build the peer array */
2968    for( i=0, l=lives; i<n; ++i, ++l ) {
2969        peers[i] = l->peer;
2970        if( clientData )
2971            clientData[i] = l->clientData;
2972    }
2973    assert( n == ( l - lives ) );
2974
2975    /* cleanup */
2976    tr_free( lives );
2977}
2978
2979static void
2980sortPeersByLiveliness( tr_peer ** peers, void ** clientData, int n, uint64_t now )
2981{
2982    sortPeersByLivelinessImpl( peers, clientData, n, now, comparePeerLiveliness );
2983}
2984
2985static void
2986sortPeersByLivelinessReverse( tr_peer ** peers, void ** clientData, int n, uint64_t now )
2987{
2988    sortPeersByLivelinessImpl( peers, clientData, n, now, comparePeerLivelinessReverse );
2989}
2990
2991
2992static void
2993enforceTorrentPeerLimit( Torrent * t, uint64_t now )
2994{
2995    int n = tr_ptrArraySize( &t->peers );
2996    const int max = tr_torrentGetPeerLimit( t->tor );
2997    if( n > max )
2998    {
2999        void * base = tr_ptrArrayBase( &t->peers );
3000        tr_peer ** peers = tr_memdup( base, n*sizeof( tr_peer* ) );
3001        sortPeersByLiveliness( peers, NULL, n, now );
3002        while( n > max )
3003            closePeer( t, peers[--n] );
3004        tr_free( peers );
3005    }
3006}
3007
3008static void
3009enforceSessionPeerLimit( tr_session * session, uint64_t now )
3010{
3011    int n = 0;
3012    tr_torrent * tor = NULL;
3013    const int max = tr_sessionGetPeerLimit( session );
3014
3015    /* count the total number of peers */
3016    while(( tor = tr_torrentNext( session, tor )))
3017        n += tr_ptrArraySize( &tor->torrentPeers->peers );
3018
3019    /* if there are too many, prune out the worst */
3020    if( n > max )
3021    {
3022        tr_peer ** peers = tr_new( tr_peer*, n );
3023        Torrent ** torrents = tr_new( Torrent*, n );
3024
3025        /* populate the peer array */
3026        n = 0;
3027        tor = NULL;
3028        while(( tor = tr_torrentNext( session, tor ))) {
3029            int i;
3030            Torrent * t = tor->torrentPeers;
3031            const int tn = tr_ptrArraySize( &t->peers );
3032            for( i=0; i<tn; ++i, ++n ) {
3033                peers[n] = tr_ptrArrayNth( &t->peers, i );
3034                torrents[n] = t;
3035            }
3036        }
3037
3038        /* sort 'em */
3039        sortPeersByLiveliness( peers, (void**)torrents, n, now );
3040
3041        /* cull out the crappiest */
3042        while( n-- > max )
3043            closePeer( torrents[n], peers[n] );
3044
3045        /* cleanup */
3046        tr_free( torrents );
3047        tr_free( peers );
3048    }
3049}
3050
3051static void makeNewPeerConnections( tr_peerMgr * mgr, const int max );
3052
3053static void
3054reconnectPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
3055{
3056    tr_torrent * tor;
3057    tr_peerMgr * mgr = vmgr;
3058    const uint64_t now = tr_date( );
3059
3060    /**
3061    ***  enforce the per-session and per-torrent peer limits
3062    **/
3063
3064    /* if we're over the per-torrent peer limits, cull some peers */
3065    tor = NULL;
3066    while(( tor = tr_torrentNext( mgr->session, tor )))
3067        if( tor->isRunning )
3068            enforceTorrentPeerLimit( tor->torrentPeers, now );
3069
3070    /* if we're over the per-session peer limits, cull some peers */
3071    enforceSessionPeerLimit( mgr->session, now );
3072
3073    /* remove crappy peers */
3074    tor = NULL;
3075    while(( tor = tr_torrentNext( mgr->session, tor )))
3076        closeBadPeers( tor->torrentPeers );
3077
3078    /* try to make new peer connections */
3079    makeNewPeerConnections( mgr, MAX_CONNECTIONS_PER_PULSE );
3080}
3081
3082/****
3083*****
3084*****  BANDWIDTH ALLOCATION
3085*****
3086****/
3087
3088static void
3089pumpAllPeers( tr_peerMgr * mgr )
3090{
3091    tr_torrent * tor = NULL;
3092
3093    while(( tor = tr_torrentNext( mgr->session, tor )))
3094    {
3095        int j;
3096        Torrent * t = tor->torrentPeers;
3097
3098        for( j=0; j<tr_ptrArraySize( &t->peers ); ++j )
3099        {
3100            tr_peer * peer = tr_ptrArrayNth( &t->peers, j );
3101            tr_peerMsgsPulse( peer->msgs );
3102        }
3103    }
3104}
3105
3106static void
3107bandwidthPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
3108{
3109    tr_torrent * tor;
3110    tr_peerMgr * mgr = vmgr;
3111    managerLock( mgr );
3112
3113    /* FIXME: this next line probably isn't necessary... */
3114    pumpAllPeers( mgr );
3115
3116    /* allocate bandwidth to the peers */
3117    tr_bandwidthAllocate( mgr->session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC );
3118    tr_bandwidthAllocate( mgr->session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC );
3119
3120    /* possibly stop torrents that have seeded enough */
3121    tor = NULL;
3122    while(( tor = tr_torrentNext( mgr->session, tor )))
3123        tr_torrentCheckSeedRatio( tor );
3124
3125    /* run the completeness check for any torrents that need it */
3126    tor = NULL;
3127    while(( tor = tr_torrentNext( mgr->session, tor ))) {
3128        if( tor->torrentPeers->needsCompletenessCheck ) {
3129            tor->torrentPeers->needsCompletenessCheck  = FALSE;
3130            tr_torrentRecheckCompleteness( tor );
3131        }
3132    }
3133
3134    /* possibly stop torrents that have an error */
3135    tor = NULL;
3136    while(( tor = tr_torrentNext( mgr->session, tor )))
3137        if( tor->isRunning && ( tor->error == TR_STAT_LOCAL_ERROR ))
3138            tr_torrentStop( tor );
3139
3140    reconnectPulse( 0, 0, mgr );
3141
3142    tr_timerAddMsec( mgr->bandwidthTimer, BANDWIDTH_PERIOD_MSEC );
3143    managerUnlock( mgr );
3144}
3145
3146/***
3147****
3148***/
3149
3150static int
3151compareAtomPtrsByAddress( const void * va, const void *vb )
3152{
3153    const struct peer_atom * a = * (const struct peer_atom**) va;
3154    const struct peer_atom * b = * (const struct peer_atom**) vb;
3155
3156    assert( tr_isAtom( a ) );
3157    assert( tr_isAtom( b ) );
3158
3159    return tr_compareAddresses( &a->addr, &b->addr );
3160}
3161
3162/* best come first, worst go last */
3163static int
3164compareAtomPtrsByShelfDate( const void * va, const void *vb )
3165{
3166    time_t atime;
3167    time_t btime;
3168    const struct peer_atom * a = * (const struct peer_atom**) va;
3169    const struct peer_atom * b = * (const struct peer_atom**) vb;
3170    const int data_time_cutoff_secs = 60 * 60;
3171    const time_t tr_now = tr_time( );
3172
3173    assert( tr_isAtom( a ) );
3174    assert( tr_isAtom( b ) );
3175
3176    /* primary key: the last piece data time *if* it was within the last hour */
3177    atime = a->piece_data_time; if( atime + data_time_cutoff_secs < tr_now ) atime = 0;
3178    btime = b->piece_data_time; if( btime + data_time_cutoff_secs < tr_now ) btime = 0;
3179    if( atime != btime )
3180        return atime > btime ? -1 : 1;
3181
3182    /* secondary key: shelf date. */
3183    if( a->shelf_date != b->shelf_date )
3184        return a->shelf_date > b->shelf_date ? -1 : 1;
3185
3186    return 0;
3187}
3188
3189static int
3190getMaxAtomCount( const tr_torrent * tor )
3191{
3192    /* FIXME: this curve should be smoother... */
3193    const int n = tor->maxConnectedPeers;
3194    if( n >= 200 ) return n * 1.5;
3195    if( n >= 100 ) return n * 2;
3196    if( n >=  50 ) return n * 3;
3197    if( n >=  20 ) return n * 5;
3198    return n * 10;
3199}
3200
3201static void
3202atomPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
3203{
3204    tr_torrent * tor = NULL;
3205    tr_peerMgr * mgr = vmgr;
3206    managerLock( mgr );
3207
3208    while(( tor = tr_torrentNext( mgr->session, tor )))
3209    {
3210        int atomCount;
3211        Torrent * t = tor->torrentPeers;
3212        const int maxAtomCount = getMaxAtomCount( tor );
3213        struct peer_atom ** atoms = (struct peer_atom**) tr_ptrArrayPeek( &t->pool, &atomCount );
3214
3215        if( atomCount > maxAtomCount ) /* we've got too many atoms... time to prune */
3216        {
3217            int i;
3218            int keepCount = 0;
3219            int testCount = 0;
3220            struct peer_atom ** keep = tr_new( struct peer_atom*, atomCount );
3221            struct peer_atom ** test = tr_new( struct peer_atom*, atomCount );
3222
3223            /* keep the ones that are in use */
3224            for( i=0; i<atomCount; ++i ) {
3225                struct peer_atom * atom = atoms[i];
3226                if( peerIsInUse( t, atom ) )
3227                    keep[keepCount++] = atom;
3228                else
3229                    test[testCount++] = atom;
3230            }
3231
3232            /* if there's room, keep the best of what's left */
3233            i = 0;
3234            if( keepCount < maxAtomCount ) {
3235                qsort( test, testCount, sizeof( struct peer_atom * ), compareAtomPtrsByShelfDate );
3236                while( i<testCount && keepCount<maxAtomCount )
3237                    keep[keepCount++] = test[i++];
3238            }
3239
3240            /* free the culled atoms */
3241            while( i<testCount )
3242                tr_free( test[i++] );
3243
3244            /* rebuild Torrent.pool with what's left */
3245            tr_ptrArrayDestruct( &t->pool, NULL );
3246            t->pool = TR_PTR_ARRAY_INIT;
3247            qsort( keep, keepCount, sizeof( struct peer_atom * ), compareAtomPtrsByAddress );
3248            for( i=0; i<keepCount; ++i )
3249                tr_ptrArrayAppend( &t->pool, keep[i] );
3250
3251            tordbg( t, "max atom count is %d... pruned from %d to %d\n", maxAtomCount, atomCount, keepCount );
3252
3253            /* cleanup */
3254            tr_free( test );
3255            tr_free( keep );
3256        }
3257    }
3258
3259    tr_timerAddMsec( mgr->atomTimer, ATOM_PERIOD_MSEC );
3260    managerUnlock( mgr );
3261}
3262
3263/***
3264****
3265****
3266****
3267***/
3268
3269static inline tr_bool
3270isBandwidthMaxedOut( const tr_bandwidth * b,
3271                     const uint64_t now_msec, tr_direction dir )
3272{
3273    if( !tr_bandwidthIsLimited( b, dir ) )
3274        return FALSE;
3275    else {
3276        const double got = tr_bandwidthGetPieceSpeed( b, now_msec, dir );
3277        const double want = tr_bandwidthGetDesiredSpeed( b, dir );
3278        return got >= want;
3279    }
3280}
3281
3282/* is this atom someone that we'd want to initiate a connection to? */
3283static tr_bool
3284isPeerCandidate( const tr_torrent * tor, struct peer_atom * atom, const time_t now )
3285{
3286    /* not if they're banned... */
3287    if( atom->myflags & MYFLAG_BANNED )
3288        return FALSE;
3289
3290    /* not if we just tried them already */
3291    if( ( now - atom->time ) < getReconnectIntervalSecs( atom, now ) )
3292        return FALSE;
3293
3294    /* not if we're both seeds */
3295    if( tr_torrentIsSeed( tor ) )
3296        if( atomIsSeed( atom ) || ( atom->uploadOnly == UPLOAD_ONLY_YES ) )
3297            return FALSE;
3298 
3299    /* not if they're blocklisted */
3300    if( isAtomBlocklisted( tor->session, atom ) )
3301        return FALSE;
3302
3303    /* not if we've already got a connection to them...  */
3304    if( peerIsInUse( tor->torrentPeers, atom ) )
3305        return FALSE;
3306
3307    return TRUE;
3308}
3309
3310struct peer_candidate
3311{
3312    uint64_t score;
3313    tr_torrent * tor;
3314    struct peer_atom * atom;
3315};
3316
3317static tr_bool
3318torrentWasRecentlyStarted( const tr_torrent * tor )
3319{
3320    return difftime( tr_time( ), tor->startDate ) < 120;
3321}
3322
3323static uint64_t
3324addValToKey( uint64_t value, int width, uint64_t addme )
3325{
3326    value = (value << (uint64_t)width);
3327    value |= addme;
3328    return value;
3329}
3330
3331/* smaller value is better */
3332static uint64_t
3333getPeerCandidateScore( const tr_torrent * tor, const struct peer_atom * atom, uint8_t salt  )
3334{
3335    uint64_t i;
3336    uint64_t score = 0;
3337    const tr_bool failed = atom->lastConnectionAt < atom->lastConnectionAttemptAt;
3338
3339    /* prefer peers we've connected to, or never tried, over peers we failed to connect to. */
3340    i = failed ? 1 : 0;
3341    score = addValToKey( score, 1, i );
3342
3343    /* prefer the one we attempted least recently (to cycle through all peers) */
3344    i = atom->lastConnectionAttemptAt;
3345    score = addValToKey( score, 32, i );
3346
3347    /* prefer peers belonging to a torrent of a higher priority */
3348    switch( tr_torrentGetPriority( tor ) ) {
3349        case TR_PRI_HIGH:    i = 0; break;
3350        case TR_PRI_NORMAL:  i = 1; break;
3351        case TR_PRI_LOW:     i = 2; break;
3352    }
3353    score = addValToKey( score, 4, i );
3354
3355    /* prefer recently-started torrents */
3356    i = torrentWasRecentlyStarted( tor ) ? 0 : 1;
3357    score = addValToKey( score, 1, i );
3358
3359    /* prefer peers that we might have a chance of uploading to...
3360       so lower seed probability is better */
3361    if( atom->seedProbability == 100 ) i = 101;
3362    else if( atom->seedProbability == -1 ) i = 100;
3363    else i = atom->seedProbability;
3364    score = addValToKey( score, 8, i );
3365
3366    /* Prefer peers that we got from more trusted sources.
3367     * lower `from' values indicate more trusted sources */
3368    score = addValToKey( score, 4, atom->from );
3369
3370    /* salt */
3371    score = addValToKey( score, 8, salt );
3372
3373    return score;
3374}
3375
3376/* sort an array of peer candidates */
3377static int
3378comparePeerCandidates( const void * va, const void * vb )
3379{
3380    const struct peer_candidate * a = va;
3381    const struct peer_candidate * b = vb;
3382
3383    if( a->score < b->score ) return -1;
3384    if( a->score > b->score ) return 1;
3385
3386    return 0;
3387}
3388
3389/** @return an array of all the atoms we might want to connect to */
3390static struct peer_candidate*
3391getPeerCandidates( tr_session * session, int * candidateCount )
3392{
3393    int n;
3394    tr_torrent * tor;
3395    struct peer_candidate * candidates;
3396    struct peer_candidate * walk;
3397    const time_t now = tr_time( );
3398    const uint64_t now_msec = tr_date( );
3399    /* leave 5% of connection slots for incoming connections -- ticket #2609 */
3400    const int maxCandidates = tr_sessionGetPeerLimit( session ) * 0.95;
3401
3402    /* don't start any new handshakes if we're full up */
3403    n = 0;
3404    tor= NULL;
3405    while(( tor = tr_torrentNext( session, tor )))
3406        n += tr_ptrArraySize( &tor->torrentPeers->peers );
3407    if( maxCandidates <= n ) {
3408        *candidateCount = 0;
3409        return NULL;
3410    }
3411
3412    /* allocate an array of candidates */
3413    n = 0;
3414    tor= NULL;
3415    while(( tor = tr_torrentNext( session, tor )))
3416        n += tr_ptrArraySize( &tor->torrentPeers->pool );
3417    walk = candidates = tr_new( struct peer_candidate, n );
3418
3419    /* populate the candidate array */
3420    tor = NULL;
3421    while(( tor = tr_torrentNext( session, tor )))
3422    {
3423        int i, nAtoms;
3424        struct peer_atom ** atoms;
3425
3426        if( !tor->torrentPeers->isRunning )
3427            continue;
3428
3429        /* if we've already got enough peers in this torrent... */
3430        if( tr_torrentGetPeerLimit( tor ) <= tr_ptrArraySize( &tor->torrentPeers->peers ) )
3431            continue;
3432
3433        /* if we've already got enough speed in this torrent... */
3434        if( tr_torrentIsSeed( tor ) && isBandwidthMaxedOut( tor->bandwidth, now_msec, TR_UP ) )
3435            continue;
3436
3437        atoms = (struct peer_atom**) tr_ptrArrayPeek( &tor->torrentPeers->pool, &nAtoms );
3438        for( i=0; i<nAtoms; ++i )
3439        {
3440            struct peer_atom * atom = atoms[i];
3441
3442            if( isPeerCandidate( tor, atom, now ) )
3443            {
3444                const uint8_t salt = tr_cryptoWeakRandInt( 1024 );
3445                walk->tor = tor;
3446                walk->atom = atom;
3447                walk->score = getPeerCandidateScore( tor, atom, salt );
3448                ++walk;
3449            }
3450        }
3451    }
3452
3453    *candidateCount = walk - candidates;
3454    if( *candidateCount > 1 )
3455        qsort( candidates, *candidateCount, sizeof( struct peer_candidate ), comparePeerCandidates );
3456    return candidates;
3457}
3458
3459static void
3460initiateConnection( tr_peerMgr * mgr, Torrent * t, struct peer_atom * atom )
3461{
3462    tr_peerIo * io;
3463    const time_t now = tr_time( );
3464
3465    tordbg( t, "Starting an OUTGOING connection with %s", tr_atomAddrStr( atom ) );
3466
3467    io = tr_peerIoNewOutgoing( mgr->session,
3468                               mgr->session->bandwidth,
3469                               &atom->addr,
3470                               atom->port,
3471                               t->tor->info.hash,
3472                               t->tor->completeness == TR_SEED );
3473
3474    if( io == NULL )
3475    {
3476        tordbg( t, "peerIo not created; marking peer %s as unreachable",
3477                tr_atomAddrStr( atom ) );
3478        atom->myflags |= MYFLAG_UNREACHABLE;
3479        atom->numFails++;
3480    }
3481    else
3482    {
3483        tr_handshake * handshake = tr_handshakeNew( io,
3484                                                    mgr->session->encryptionMode,
3485                                                    myHandshakeDoneCB,
3486                                                    mgr );
3487
3488        assert( tr_peerIoGetTorrentHash( io ) );
3489
3490        tr_peerIoUnref( io ); /* balanced by the initial ref
3491                                 in tr_peerIoNewOutgoing() */
3492
3493        tr_ptrArrayInsertSorted( &t->outgoingHandshakes, handshake,
3494                                 handshakeCompare );
3495    }
3496
3497    atom->lastConnectionAttemptAt = now;
3498    atom->time = now;
3499}
3500
3501static void
3502initiateCandidateConnection( tr_peerMgr * mgr, struct peer_candidate * c )
3503{
3504#if 0
3505    fprintf( stderr, "Starting an OUTGOING connection with %s - [%s] seedProbability==%d; %s, %s\n",
3506             tr_atomAddrStr( c->atom ),
3507             tr_torrentName( c->tor ),
3508             (int)c->atom->seedProbability,
3509             tr_torrentIsPrivate( c->tor ) ? "private" : "public",
3510             tr_torrentIsSeed( c->tor ) ? "seed" : "downloader" );
3511#endif
3512
3513    initiateConnection( mgr, c->tor->torrentPeers, c->atom );
3514}
3515
3516static void
3517makeNewPeerConnections( struct tr_peerMgr * mgr, const int max )
3518{
3519    int i, n;
3520    struct peer_candidate * candidates;
3521
3522    candidates = getPeerCandidates( mgr->session, &n );
3523
3524    for( i=0; i<n && i<max; ++i )
3525        initiateCandidateConnection( mgr, &candidates[i] );
3526
3527    tr_free( candidates );
3528}
Note: See TracBrowser for help on using the repository browser.