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

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

(trunk libT) when comparing peer speed to decide which peers to unchoke, use different criteria for seeding, downloading public, and downloading private. Loosely related to #3334.

  • Property svn:keywords set to Date Rev Author Id
File size: 102.4 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 10824 2010-06-23 04:36:16Z 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 "cache.h"
25#include "clients.h"
26#include "completion.h"
27#include "crypto.h"
28#include "handshake.h"
29#include "inout.h" /* tr_ioTestPiece */
30#include "net.h"
31#include "peer-io.h"
32#include "peer-mgr.h"
33#include "peer-msgs.h"
34#include "ptrarray.h"
35#include "session.h"
36#include "stats.h" /* tr_statsAddUploaded, tr_statsAddDownloaded */
37#include "torrent.h"
38#include "utils.h"
39#include "webseed.h"
40
41enum
42{
43    /* how frequently to cull old atoms */
44    ATOM_PERIOD_MSEC = ( 60 * 1000 ),
45
46    /* how frequently to change which peers are choked */
47    RECHOKE_PERIOD_MSEC = ( 10 * 1000 ),
48
49    /* how frequently to reallocate bandwidth */
50    BANDWIDTH_PERIOD_MSEC = 500,
51
52    /* how frequently to age out old piece request lists */
53    REFILL_UPKEEP_PERIOD_MSEC = ( 10 * 1000 ),
54
55    /* how frequently to decide which peers live and die */
56    RECONNECT_PERIOD_MSEC = 500,
57
58    /* when many peers are available, keep idle ones this long */
59    MIN_UPLOAD_IDLE_SECS = ( 60 ),
60
61    /* when few peers are available, keep idle ones this long */
62    MAX_UPLOAD_IDLE_SECS = ( 60 * 5 ),
63
64    /* max number of peers to ask for per second overall.
65    * this throttle is to avoid overloading the router */
66    MAX_CONNECTIONS_PER_SECOND = 8,
67
68    MAX_CONNECTIONS_PER_PULSE = (int)(MAX_CONNECTIONS_PER_SECOND * (RECONNECT_PERIOD_MSEC/1000.0)),
69
70    /* number of bad pieces a peer is allowed to send before we ban them */
71    MAX_BAD_PIECES_PER_PEER = 5,
72
73    /* amount of time to keep a list of request pieces lying around
74       before it's considered too old and needs to be rebuilt */
75    PIECE_LIST_SHELF_LIFE_SECS = 60,
76
77    /* use for bitwise operations w/peer_atom.myflags */
78    MYFLAG_BANNED = 1,
79
80    /* use for bitwise operations w/peer_atom.myflags */
81    /* unreachable for now... but not banned.
82     * if they try to connect to us it's okay */
83    MYFLAG_UNREACHABLE = 2,
84
85    /* the minimum we'll wait before attempting to reconnect to a peer */
86    MINIMUM_RECONNECT_INTERVAL_SECS = 5,
87
88    /** how long we'll let requests we've made linger before we cancel them */
89    REQUEST_TTL_SECS = 120,
90
91    CANCEL_HISTORY_SEC = 120
92};
93
94
95/**
96***
97**/
98
99enum
100{
101    UPLOAD_ONLY_UKNOWN,
102    UPLOAD_ONLY_YES,
103    UPLOAD_ONLY_NO
104};
105
106/**
107 * Peer information that should be kept even before we've connected and
108 * after we've disconnected.  These are kept in a pool of peer_atoms to decide
109 * which ones would make good candidates for connecting to, and to watch out
110 * for banned peers.
111 *
112 * @see tr_peer
113 * @see tr_peermsgs
114 */
115struct peer_atom
116{
117    uint8_t     from;
118    uint8_t     flags;              /* these match the added_f flags */
119    uint8_t     myflags;            /* flags that aren't defined in added_f */
120    uint8_t     uploadOnly;         /* UPLOAD_ONLY_ */
121    int8_t      seedProbability;    /* how likely is this to be a seed... [0..100] or -1 for unknown */
122    int8_t      blocklisted;        /* -1 for unknown, TRUE for blocklisted, FALSE for not blocklisted */
123
124    tr_port     port;
125    uint16_t    numFails;
126    time_t      time;               /* when the peer's connection status last changed */
127    time_t      piece_data_time;
128
129    time_t      lastConnectionAttemptAt;
130    time_t      lastConnectionAt;
131
132    /* similar to a TTL field, but less rigid --
133     * if the swarm is small, the atom will be kept past this date. */
134    time_t      shelf_date;
135    tr_peer   * peer;               /* will be NULL if not connected */
136    tr_address  addr;
137};
138
139#ifdef NDEBUG
140#define tr_isAtom(a) (TRUE)
141#else
142static tr_bool
143tr_isAtom( const struct peer_atom * atom )
144{
145    return ( atom != NULL )
146        && ( atom->from < TR_PEER_FROM__MAX )
147        && ( tr_isAddress( &atom->addr ) );
148}
149#endif
150
151static const char*
152tr_atomAddrStr( const struct peer_atom * atom )
153{
154    return tr_peerIoAddrStr( &atom->addr, atom->port );
155}
156
157struct block_request
158{
159    tr_block_index_t block;
160    tr_peer * peer;
161    time_t sentAt;
162};
163
164struct weighted_piece
165{
166    tr_piece_index_t index;
167    int16_t salt;
168    int16_t requestCount;
169};
170
171/** @brief Opaque, per-torrent data structure for peer connection information */
172typedef struct tr_torrent_peers
173{
174    tr_ptrArray                outgoingHandshakes; /* tr_handshake */
175    tr_ptrArray                pool; /* struct peer_atom */
176    tr_ptrArray                peers; /* tr_peer */
177    tr_ptrArray                webseeds; /* tr_webseed */
178
179    tr_torrent               * tor;
180    tr_peer                  * optimistic; /* the optimistic peer, or NULL if none */
181    struct tr_peerMgr        * manager;
182
183    tr_bool                    isRunning;
184    tr_bool                    needsCompletenessCheck;
185
186    struct block_request     * requests;
187    int                        requestCount;
188    int                        requestAlloc;
189
190    struct weighted_piece    * pieces;
191    int                        pieceCount;
192
193    int                        interestedCount;
194
195    /* An arbitrary metric of how congested the downloads are.
196     * Based on how many of requests are cancelled and how many are completed.
197     * Lower values indicate less congestion. */
198    double                     cancelRate;
199}
200Torrent;
201
202struct tr_peerMgr
203{
204    tr_session    * session;
205    tr_ptrArray     incomingHandshakes; /* tr_handshake */
206    struct event  * bandwidthTimer;
207    struct event  * rechokeTimer;
208    struct event  * refillUpkeepTimer;
209    struct event  * atomTimer;
210};
211
212#define tordbg( t, ... ) \
213    do { \
214        if( tr_deepLoggingIsActive( ) ) \
215            tr_deepLog( __FILE__, __LINE__, tr_torrentName( t->tor ), __VA_ARGS__ ); \
216    } while( 0 )
217
218#define dbgmsg( ... ) \
219    do { \
220        if( tr_deepLoggingIsActive( ) ) \
221            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
222    } while( 0 )
223
224/**
225***
226**/
227
228static inline void
229managerLock( const struct tr_peerMgr * manager )
230{
231    tr_sessionLock( manager->session );
232}
233
234static inline void
235managerUnlock( const struct tr_peerMgr * manager )
236{
237    tr_sessionUnlock( manager->session );
238}
239
240static inline void
241torrentLock( Torrent * torrent )
242{
243    managerLock( torrent->manager );
244}
245
246static inline void
247torrentUnlock( Torrent * torrent )
248{
249    managerUnlock( torrent->manager );
250}
251
252static inline int
253torrentIsLocked( const Torrent * t )
254{
255    return tr_sessionIsLocked( t->manager->session );
256}
257
258/**
259***
260**/
261
262static int
263handshakeCompareToAddr( const void * va, const void * vb )
264{
265    const tr_handshake * a = va;
266
267    return tr_compareAddresses( tr_handshakeGetAddr( a, NULL ), vb );
268}
269
270static int
271handshakeCompare( const void * a, const void * b )
272{
273    return handshakeCompareToAddr( a, tr_handshakeGetAddr( b, NULL ) );
274}
275
276static inline tr_handshake*
277getExistingHandshake( tr_ptrArray * handshakes, const tr_address * addr )
278{
279    if( tr_ptrArrayEmpty( handshakes ) )
280        return NULL;
281
282    return tr_ptrArrayFindSorted( handshakes, addr, handshakeCompareToAddr );
283}
284
285static int
286comparePeerAtomToAddress( const void * va, const void * vb )
287{
288    const struct peer_atom * a = va;
289
290    return tr_compareAddresses( &a->addr, vb );
291}
292
293static int
294compareAtomsByAddress( const void * va, const void * vb )
295{
296    const struct peer_atom * b = vb;
297
298    assert( tr_isAtom( b ) );
299
300    return comparePeerAtomToAddress( va, &b->addr );
301}
302
303/**
304***
305**/
306
307const tr_address *
308tr_peerAddress( const tr_peer * peer )
309{
310    return &peer->atom->addr;
311}
312
313static Torrent*
314getExistingTorrent( tr_peerMgr *    manager,
315                    const uint8_t * hash )
316{
317    tr_torrent * tor = tr_torrentFindFromHash( manager->session, hash );
318
319    return tor == NULL ? NULL : tor->torrentPeers;
320}
321
322static int
323peerCompare( const void * a, const void * b )
324{
325    return tr_compareAddresses( tr_peerAddress( a ), tr_peerAddress( b ) );
326}
327
328static struct peer_atom*
329getExistingAtom( const Torrent    * t,
330                 const tr_address * addr )
331{
332    Torrent * tt = (Torrent*)t;
333    assert( torrentIsLocked( t ) );
334    return tr_ptrArrayFindSorted( &tt->pool, addr, comparePeerAtomToAddress );
335}
336
337static tr_bool
338peerIsInUse( const Torrent * ct, const struct peer_atom * atom )
339{
340    Torrent * t = (Torrent*) ct;
341
342    assert( torrentIsLocked ( t ) );
343
344    return ( atom->peer != NULL )
345        || getExistingHandshake( &t->outgoingHandshakes, &atom->addr )
346        || getExistingHandshake( &t->manager->incomingHandshakes, &atom->addr );
347}
348
349static tr_peer*
350peerConstructor( struct peer_atom * atom )
351{
352    tr_peer * peer = tr_new0( tr_peer, 1 );
353
354    tr_bitsetConstructor( &peer->have, 0 );
355
356    peer->atom = atom;
357    atom->peer = peer;
358
359    peer->blocksSentToClient  = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
360    peer->blocksSentToPeer    = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
361    peer->cancelsSentToClient = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
362    peer->cancelsSentToPeer   = tr_historyNew( CANCEL_HISTORY_SEC, 1 );
363
364    return peer;
365}
366
367static tr_peer*
368getPeer( Torrent * torrent, struct peer_atom * atom )
369{
370    tr_peer * peer;
371
372    assert( torrentIsLocked( torrent ) );
373
374    peer = atom->peer;
375
376    if( peer == NULL )
377    {
378        peer = peerConstructor( atom );
379        tr_ptrArrayInsertSorted( &torrent->peers, peer, peerCompare );
380    }
381
382    return peer;
383}
384
385static void peerDeclinedAllRequests( Torrent *, const tr_peer * );
386
387static void
388peerDestructor( Torrent * t, tr_peer * peer )
389{
390    assert( peer != NULL );
391
392    peerDeclinedAllRequests( t, peer );
393
394    if( peer->msgs != NULL )
395        tr_peerMsgsFree( peer->msgs );
396
397    tr_peerIoClear( peer->io );
398    tr_peerIoUnref( peer->io ); /* balanced by the ref in handshakeDoneCB() */
399
400    tr_historyFree( peer->blocksSentToClient  );
401    tr_historyFree( peer->blocksSentToPeer    );
402    tr_historyFree( peer->cancelsSentToClient );
403    tr_historyFree( peer->cancelsSentToPeer   );
404
405    tr_bitsetDestructor( &peer->have );
406    tr_bitfieldFree( peer->blame );
407    tr_free( peer->client );
408    peer->atom->peer = NULL;
409
410    tr_free( peer );
411}
412
413static void
414removePeer( Torrent * t, tr_peer * peer )
415{
416    tr_peer * removed;
417    struct peer_atom * atom = peer->atom;
418
419    assert( torrentIsLocked( t ) );
420    assert( atom );
421
422    atom->time = tr_time( );
423
424    removed = tr_ptrArrayRemoveSorted( &t->peers, peer, peerCompare );
425    assert( removed == peer );
426    peerDestructor( t, removed );
427}
428
429static void
430removeAllPeers( Torrent * t )
431{
432    while( !tr_ptrArrayEmpty( &t->peers ) )
433        removePeer( t, tr_ptrArrayNth( &t->peers, 0 ) );
434}
435
436static void
437torrentDestructor( void * vt )
438{
439    Torrent * t = vt;
440
441    assert( t );
442    assert( !t->isRunning );
443    assert( torrentIsLocked( t ) );
444    assert( tr_ptrArrayEmpty( &t->outgoingHandshakes ) );
445    assert( tr_ptrArrayEmpty( &t->peers ) );
446
447    tr_ptrArrayDestruct( &t->webseeds, (PtrArrayForeachFunc)tr_webseedFree );
448    tr_ptrArrayDestruct( &t->pool, (PtrArrayForeachFunc)tr_free );
449    tr_ptrArrayDestruct( &t->outgoingHandshakes, NULL );
450    tr_ptrArrayDestruct( &t->peers, NULL );
451
452    tr_free( t->requests );
453    tr_free( t->pieces );
454    tr_free( t );
455}
456
457static void peerCallbackFunc( tr_peer *, const tr_peer_event *, void * );
458
459static Torrent*
460torrentConstructor( tr_peerMgr * manager,
461                    tr_torrent * tor )
462{
463    int       i;
464    Torrent * t;
465
466    t = tr_new0( Torrent, 1 );
467    t->manager = manager;
468    t->tor = tor;
469    t->pool = TR_PTR_ARRAY_INIT;
470    t->peers = TR_PTR_ARRAY_INIT;
471    t->webseeds = TR_PTR_ARRAY_INIT;
472    t->outgoingHandshakes = TR_PTR_ARRAY_INIT;
473
474    for( i = 0; i < tor->info.webseedCount; ++i )
475    {
476        tr_webseed * w =
477            tr_webseedNew( tor, tor->info.webseeds[i], peerCallbackFunc, t );
478        tr_ptrArrayAppend( &t->webseeds, w );
479    }
480
481    return t;
482}
483
484tr_peerMgr*
485tr_peerMgrNew( tr_session * session )
486{
487    tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
488    m->session = session;
489    m->incomingHandshakes = TR_PTR_ARRAY_INIT;
490    return m;
491}
492
493static void
494deleteTimer( struct event ** t )
495{
496    if( *t != NULL )
497    {
498        evtimer_del( *t );
499        tr_free( *t );
500        *t = NULL;
501    }
502}
503
504static void
505deleteTimers( struct tr_peerMgr * m )
506{
507    deleteTimer( &m->atomTimer );
508    deleteTimer( &m->bandwidthTimer );
509    deleteTimer( &m->rechokeTimer );
510    deleteTimer( &m->refillUpkeepTimer );
511}
512
513void
514tr_peerMgrFree( tr_peerMgr * manager )
515{
516    managerLock( manager );
517
518    deleteTimers( manager );
519
520    /* free the handshakes.  Abort invokes handshakeDoneCB(), which removes
521     * the item from manager->handshakes, so this is a little roundabout... */
522    while( !tr_ptrArrayEmpty( &manager->incomingHandshakes ) )
523        tr_handshakeAbort( tr_ptrArrayNth( &manager->incomingHandshakes, 0 ) );
524
525    tr_ptrArrayDestruct( &manager->incomingHandshakes, NULL );
526
527    managerUnlock( manager );
528    tr_free( manager );
529}
530
531static int
532clientIsDownloadingFrom( const tr_torrent * tor, const tr_peer * peer )
533{
534    if( !tr_torrentHasMetadata( tor ) )
535        return TRUE;
536
537    return peer->clientIsInterested && !peer->clientIsChoked;
538}
539
540static int
541clientIsUploadingTo( const tr_peer * peer )
542{
543    return peer->peerIsInterested && !peer->peerIsChoked;
544}
545
546/***
547****
548***/
549
550void
551tr_peerMgrOnBlocklistChanged( tr_peerMgr * mgr )
552{
553    tr_torrent * tor = NULL;
554    tr_session * session = mgr->session;
555
556    /* we cache whether or not a peer is blocklisted...
557       since the blocklist has changed, erase that cached value */
558    while(( tor = tr_torrentNext( session, tor )))
559    {
560        int i;
561        Torrent * t = tor->torrentPeers;
562        const int n = tr_ptrArraySize( &t->pool );
563        for( i=0; i<n; ++i ) {
564            struct peer_atom * atom = tr_ptrArrayNth( &t->pool, i );
565            atom->blocklisted = -1;
566        }
567    }
568}
569
570static tr_bool
571isAtomBlocklisted( tr_session * session, struct peer_atom * atom )
572{
573    if( atom->blocklisted < 0 )
574        atom->blocklisted = tr_sessionIsAddressBlocked( session, &atom->addr );
575
576    assert( tr_isBool( atom->blocklisted ) );
577    return atom->blocklisted;
578}
579
580
581/***
582****
583***/
584
585static void
586atomSetSeedProbability( struct peer_atom * atom, int seedProbability )
587{
588    assert( atom != NULL );
589    assert( -1<=seedProbability && seedProbability<=100 );
590
591    atom->seedProbability = seedProbability;
592
593    if( seedProbability == 100 )
594        atom->flags |= ADDED_F_SEED_FLAG;
595    else if( seedProbability != -1 )
596        atom->flags &= ~ADDED_F_SEED_FLAG;
597}
598
599static void
600atomSetSeed( struct peer_atom * atom )
601{
602    atomSetSeedProbability( atom, 100 );
603}
604
605static inline tr_bool
606atomIsSeed( const struct peer_atom * atom )
607{
608    return atom->seedProbability == 100;
609}
610
611tr_bool
612tr_peerMgrPeerIsSeed( const tr_torrent  * tor,
613                      const tr_address  * addr )
614{
615    tr_bool isSeed = FALSE;
616    const Torrent * t = tor->torrentPeers;
617    const struct peer_atom * atom = getExistingAtom( t, addr );
618
619    if( atom )
620        isSeed = atomIsSeed( atom );
621
622    return isSeed;
623}
624
625/**
626***  REQUESTS
627***
628*** There are two data structures associated with managing block requests:
629***
630*** 1. Torrent::requests, an array of "struct block_request" which keeps
631***    track of which blocks have been requested, and when, and by which peers.
632***    This is list is used for (a) cancelling requests that have been pending
633***    for too long and (b) avoiding duplicate requests before endgame.
634***
635*** 2. Torrent::pieces, an array of "struct weighted_piece" which lists the
636***    pieces that we want to request.  It's used to decide which blocks to
637***    return next when tr_peerMgrGetBlockRequests() is called.
638**/
639
640/**
641*** struct block_request
642**/
643
644static int
645compareReqByBlock( const void * va, const void * vb )
646{
647    const struct block_request * a = va;
648    const struct block_request * b = vb;
649
650    /* primary key: block */
651    if( a->block < b->block ) return -1;
652    if( a->block > b->block ) return 1;
653
654    /* secondary key: peer */
655    if( a->peer < b->peer ) return -1;
656    if( a->peer > b->peer ) return 1;
657
658    return 0;
659}
660
661static void
662requestListAdd( Torrent * t, tr_block_index_t block, tr_peer * peer )
663{
664    struct block_request key;
665
666    /* ensure enough room is available... */
667    if( t->requestCount + 1 >= t->requestAlloc )
668    {
669        const int CHUNK_SIZE = 128;
670        t->requestAlloc += CHUNK_SIZE;
671        t->requests = tr_renew( struct block_request,
672                                t->requests, t->requestAlloc );
673    }
674
675    /* populate the record we're inserting */
676    key.block = block;
677    key.peer = peer;
678    key.sentAt = tr_time( );
679
680    /* insert the request to our array... */
681    {
682        tr_bool exact;
683        const int pos = tr_lowerBound( &key, t->requests, t->requestCount,
684                                       sizeof( struct block_request ),
685                                       compareReqByBlock, &exact );
686        assert( !exact );
687        memmove( t->requests + pos + 1,
688                 t->requests + pos,
689                 sizeof( struct block_request ) * ( t->requestCount++ - pos ) );
690        t->requests[pos] = key;
691    }
692
693    if( peer != NULL )
694    {
695        ++peer->pendingReqsToPeer;
696        assert( peer->pendingReqsToPeer >= 0 );
697    }
698
699    /*fprintf( stderr, "added request of block %lu from peer %s... "
700                       "there are now %d block\n",
701                       (unsigned long)block, tr_atomAddrStr( peer->atom ), t->requestCount );*/
702}
703
704static struct block_request *
705requestListLookup( Torrent * t, tr_block_index_t block, const tr_peer * peer )
706{
707    struct block_request key;
708    key.block = block;
709    key.peer = (tr_peer*) peer;
710
711    return bsearch( &key, t->requests, t->requestCount,
712                    sizeof( struct block_request ),
713                    compareReqByBlock );
714}
715
716/* how many peers are we currently requesting this block from... */
717static int
718countBlockRequests( Torrent * t, tr_block_index_t block )
719{
720    tr_bool exact;
721    int i, n, pos;
722    struct block_request key;
723
724    key.block = block;
725    key.peer = NULL;
726    pos = tr_lowerBound( &key, t->requests, t->requestCount,
727                         sizeof( struct block_request ),
728                         compareReqByBlock, &exact );
729
730    assert( !exact ); /* shouldn't have a request with .peer == NULL */
731
732    n = 0;
733    for( i=pos; i<t->requestCount; ++i ) {
734        if( t->requests[i].block == block )
735            ++n;
736        else
737            break;
738    }
739
740    return n;
741}
742
743static void
744decrementPendingReqCount( const struct block_request * b )
745{
746    if( b->peer != NULL )
747        if( b->peer->pendingReqsToPeer > 0 )
748            --b->peer->pendingReqsToPeer;
749}
750
751static void
752requestListRemove( Torrent * t, tr_block_index_t block, const tr_peer * peer )
753{
754    const struct block_request * b = requestListLookup( t, block, peer );
755    if( b != NULL )
756    {
757        const int pos = b - t->requests;
758        assert( pos < t->requestCount );
759
760        decrementPendingReqCount( b );
761
762        tr_removeElementFromArray( t->requests,
763                                   pos,
764                                   sizeof( struct block_request ),
765                                   t->requestCount-- );
766
767        /*fprintf( stderr, "removing request of block %lu from peer %s... "
768                           "there are now %d block requests left\n",
769                           (unsigned long)block, tr_atomAddrStr( peer->atom ), t->requestCount );*/
770    }
771}
772
773/**
774*** struct weighted_piece
775**/
776
777enum
778{
779    PIECES_UNSORTED,
780    PIECES_SORTED_BY_INDEX,
781    PIECES_SORTED_BY_WEIGHT
782};
783
784const tr_torrent * weightTorrent;
785
786/* we try to create a "weight" s.t. high-priority pieces come before others,
787 * and that partially-complete pieces come before empty ones. */
788static int
789comparePieceByWeight( const void * va, const void * vb )
790{
791    const struct weighted_piece * a = va;
792    const struct weighted_piece * b = vb;
793    int ia, ib, missing, pending;
794    const tr_torrent * tor = weightTorrent;
795
796    /* primary key: weight */
797    missing = tr_cpMissingBlocksInPiece( &tor->completion, a->index );
798    pending = a->requestCount;
799    ia = missing > pending ? missing - pending : (int)(tor->blockCountInPiece + pending);
800    missing = tr_cpMissingBlocksInPiece( &tor->completion, b->index );
801    pending = b->requestCount;
802    ib = missing > pending ? missing - pending : (int)(tor->blockCountInPiece + pending);
803    if( ia < ib ) return -1;
804    if( ia > ib ) return 1;
805
806    /* secondary key: higher priorities go first */
807    ia = tor->info.pieces[a->index].priority;
808    ib = tor->info.pieces[b->index].priority;
809    if( ia > ib ) return -1;
810    if( ia < ib ) return 1;
811
812    /* tertiary key: random */
813    if( a->salt < b->salt ) return -1;
814    if( a->salt > b->salt ) return 1;
815
816    /* okay, they're equal */
817    return 0;
818}
819
820static int
821comparePieceByIndex( const void * va, const void * vb )
822{
823    const struct weighted_piece * a = va;
824    const struct weighted_piece * b = vb;
825    if( a->index < b->index ) return -1;
826    if( a->index > b->index ) return 1;
827    return 0;
828}
829
830static void
831pieceListSort( Torrent * t, int mode )
832{
833    int(*compar)(const void *, const void *);
834
835    assert( mode==PIECES_SORTED_BY_INDEX
836         || mode==PIECES_SORTED_BY_WEIGHT );
837
838    switch( mode ) {
839        case PIECES_SORTED_BY_WEIGHT: compar = comparePieceByWeight; break;
840        case PIECES_SORTED_BY_INDEX: compar = comparePieceByIndex; break;
841        default: assert( 0 && "unhandled" );  break;
842    }
843
844    weightTorrent = t->tor;
845    qsort( t->pieces, t->pieceCount,
846           sizeof( struct weighted_piece ), compar );
847}
848
849static tr_bool
850isInEndgame( Torrent * t )
851{
852    tr_bool endgame = FALSE;
853
854    if( ( t->pieces != NULL ) && ( t->pieceCount > 0 ) )
855    {
856        const struct weighted_piece * p = t->pieces;
857        const int pending = p->requestCount;
858        const int missing = tr_cpMissingBlocksInPiece( &t->tor->completion, p->index );
859        endgame = pending >= missing;
860    }
861
862    /*if( endgame ) fprintf( stderr, "ENDGAME reached\n" );*/
863    return endgame;
864}
865
866/**
867 * This function is useful for sanity checking,
868 * but is too expensive even for nightly builds...
869 * let's leave it disabled but add an easy hook to compile it back in
870 */
871#if 0
872static void
873assertWeightedPiecesAreSorted( Torrent * t )
874{
875    if( !isInEndgame( t ) )
876    {
877        int i;
878        weightTorrent = t->tor;
879        for( i=0; i<t->pieceCount-1; ++i )
880            assert( comparePieceByWeight( &t->pieces[i], &t->pieces[i+1] ) <= 0 );
881    }
882}
883#else
884#define assertWeightedPiecesAreSorted(t)
885#endif
886
887static struct weighted_piece *
888pieceListLookup( Torrent * t, tr_piece_index_t index )
889{
890    int i;
891
892    for( i=0; i<t->pieceCount; ++i )
893        if( t->pieces[i].index == index )
894            return &t->pieces[i];
895
896    return NULL;
897}
898
899static void
900pieceListRebuild( Torrent * t )
901{
902    assertWeightedPiecesAreSorted( t );
903
904    if( !tr_torrentIsSeed( t->tor ) )
905    {
906        tr_piece_index_t i;
907        tr_piece_index_t * pool;
908        tr_piece_index_t poolCount = 0;
909        const tr_torrent * tor = t->tor;
910        const tr_info * inf = tr_torrentInfo( tor );
911        struct weighted_piece * pieces;
912        int pieceCount;
913
914        /* build the new list */
915        pool = tr_new( tr_piece_index_t, inf->pieceCount );
916        for( i=0; i<inf->pieceCount; ++i )
917            if( !inf->pieces[i].dnd )
918                if( !tr_cpPieceIsComplete( &tor->completion, i ) )
919                    pool[poolCount++] = i;
920        pieceCount = poolCount;
921        pieces = tr_new0( struct weighted_piece, pieceCount );
922        for( i=0; i<poolCount; ++i ) {
923            struct weighted_piece * piece = pieces + i;
924            piece->index = pool[i];
925            piece->requestCount = 0;
926            piece->salt = tr_cryptoWeakRandInt( 4096 );
927        }
928
929        /* if we already had a list of pieces, merge it into
930         * the new list so we don't lose its requestCounts */
931        if( t->pieces != NULL )
932        {
933            struct weighted_piece * o = t->pieces;
934            struct weighted_piece * oend = o + t->pieceCount;
935            struct weighted_piece * n = pieces;
936            struct weighted_piece * nend = n + pieceCount;
937
938            pieceListSort( t, PIECES_SORTED_BY_INDEX );
939
940            while( o!=oend && n!=nend ) {
941                if( o->index < n->index )
942                    ++o;
943                else if( o->index > n->index )
944                    ++n;
945                else
946                    *n++ = *o++;
947            }
948
949            tr_free( t->pieces );
950        }
951
952        t->pieces = pieces;
953        t->pieceCount = pieceCount;
954
955        pieceListSort( t, PIECES_SORTED_BY_WEIGHT );
956
957        /* cleanup */
958        tr_free( pool );
959    }
960}
961
962static void
963pieceListRemovePiece( Torrent * t, tr_piece_index_t piece )
964{
965    struct weighted_piece * p;
966
967    assertWeightedPiecesAreSorted( t );
968
969    if(( p = pieceListLookup( t, piece )))
970    {
971        const int pos = p - t->pieces;
972
973        tr_removeElementFromArray( t->pieces,
974                                   pos,
975                                   sizeof( struct weighted_piece ),
976                                   t->pieceCount-- );
977
978        if( t->pieceCount == 0 )
979        {
980            tr_free( t->pieces );
981            t->pieces = NULL;
982        }
983    }
984
985    assertWeightedPiecesAreSorted( t );
986}
987
988static void
989pieceListResortPiece( Torrent * t, struct weighted_piece * p )
990{
991    int pos;
992    tr_bool isSorted = TRUE;
993
994    if( p == NULL )
995        return;
996
997    /* is the torrent already sorted? */
998    pos = p - t->pieces;
999    weightTorrent = t->tor;
1000    if( isSorted && ( pos > 0 ) && ( comparePieceByWeight( p-1, p ) > 0 ) )
1001        isSorted = FALSE;
1002    if( isSorted && ( pos < t->pieceCount - 1 ) && ( comparePieceByWeight( p, p+1 ) > 0 ) )
1003        isSorted = FALSE;
1004
1005    /* if it's not sorted, move it around */
1006    if( !isSorted )
1007    {
1008        tr_bool exact;
1009        const struct weighted_piece tmp = *p;
1010
1011        tr_removeElementFromArray( t->pieces,
1012                                   pos,
1013                                   sizeof( struct weighted_piece ),
1014                                   t->pieceCount-- );
1015
1016        pos = tr_lowerBound( &tmp, t->pieces, t->pieceCount,
1017                             sizeof( struct weighted_piece ),
1018                             comparePieceByWeight, &exact );
1019
1020        memmove( &t->pieces[pos + 1],
1021                 &t->pieces[pos],
1022                 sizeof( struct weighted_piece ) * ( t->pieceCount++ - pos ) );
1023
1024        t->pieces[pos] = tmp;
1025    }
1026
1027    assertWeightedPiecesAreSorted( t );
1028}
1029
1030static void
1031pieceListRemoveRequest( Torrent * t, tr_block_index_t block )
1032{
1033    struct weighted_piece * p;
1034    const tr_piece_index_t index = tr_torBlockPiece( t->tor, block );
1035
1036    assertWeightedPiecesAreSorted( t );
1037
1038    if( ((p = pieceListLookup( t, index ))) && ( p->requestCount > 0 ) )
1039    {
1040        --p->requestCount;
1041        pieceListResortPiece( t, p );
1042    }
1043
1044    assertWeightedPiecesAreSorted( t );
1045}
1046
1047/**
1048***
1049**/
1050
1051void
1052tr_peerMgrRebuildRequests( tr_torrent * tor )
1053{
1054    assert( tr_isTorrent( tor ) );
1055
1056    pieceListRebuild( tor->torrentPeers );
1057}
1058
1059void
1060tr_peerMgrGetNextRequests( tr_torrent           * tor,
1061                           tr_peer              * peer,
1062                           int                    numwant,
1063                           tr_block_index_t     * setme,
1064                           int                  * numgot )
1065{
1066    int i;
1067    int got;
1068    Torrent * t;
1069    tr_bool endgame;
1070    struct weighted_piece * pieces;
1071    const tr_bitset * have = &peer->have;
1072
1073    /* sanity clause */
1074    assert( tr_isTorrent( tor ) );
1075    assert( peer->clientIsInterested );
1076    assert( !peer->clientIsChoked );
1077    assert( numwant > 0 );
1078
1079    /* walk through the pieces and find blocks that should be requested */
1080    got = 0;
1081    t = tor->torrentPeers;
1082    assertWeightedPiecesAreSorted( t );
1083
1084    /* prep the pieces list */
1085    if( t->pieces == NULL )
1086        pieceListRebuild( t );
1087
1088    endgame = isInEndgame( t );
1089
1090    pieces = t->pieces;
1091    for( i=0; i<t->pieceCount && got<numwant; ++i )
1092    {
1093        struct weighted_piece * p = pieces + i;
1094        const int missing = tr_cpMissingBlocksInPiece( &tor->completion, p->index );
1095        const int maxDuplicatesPerBlock = endgame ? 3 : 1;
1096
1097        if( p->requestCount > ( missing * maxDuplicatesPerBlock ) )
1098            continue;
1099
1100        /* if the peer has this piece that we want... */
1101        if( tr_bitsetHasFast( have, p->index ) )
1102        {
1103            tr_block_index_t b = tr_torPieceFirstBlock( tor, p->index );
1104            const tr_block_index_t e = b + tr_torPieceCountBlocks( tor, p->index );
1105
1106            for( ; b!=e && got<numwant; ++b )
1107            {
1108                /* don't request blocks we've already got */
1109                if( tr_cpBlockIsCompleteFast( &tor->completion, b ) )
1110                    continue;
1111
1112                /* don't send the same request to the same peer twice */
1113                if( tr_peerMgrDidPeerRequest( tor, peer, b ) )
1114                    continue;
1115
1116                /* don't send the same request to any peer too many times */
1117                if( countBlockRequests( t, b ) >= maxDuplicatesPerBlock )
1118                    continue;
1119
1120                /* update the caller's table */
1121                setme[got++] = b;
1122
1123                /* update our own tables */
1124                requestListAdd( t, b, peer );
1125                ++p->requestCount;
1126            }
1127        }
1128    }
1129
1130    /* In most cases we've just changed the weights of a small number of pieces.
1131     * So rather than qsort()ing the entire array, it's faster to apply an
1132     * adaptive insertion sort algorithm. */
1133    if( got > 0 )
1134    {
1135        /* not enough requests || last piece modified */
1136        if ( i == t->pieceCount ) --i;
1137
1138        weightTorrent = t->tor;
1139        while( --i >= 0 )
1140        {
1141            tr_bool exact;
1142
1143            /* relative position! */
1144            const int newpos = tr_lowerBound( &t->pieces[i], &t->pieces[i + 1],
1145                                              t->pieceCount - (i + 1),
1146                                              sizeof( struct weighted_piece ),
1147                                              comparePieceByWeight, &exact );
1148            if( newpos > 0 )
1149            {
1150                const struct weighted_piece piece = t->pieces[i];
1151                memmove( &t->pieces[i],
1152                         &t->pieces[i + 1],
1153                         sizeof( struct weighted_piece ) * ( newpos ) );
1154                t->pieces[i + newpos] = piece;
1155            }
1156        }
1157    }
1158
1159    assertWeightedPiecesAreSorted( t );
1160    *numgot = got;
1161}
1162
1163tr_bool
1164tr_peerMgrDidPeerRequest( const tr_torrent  * tor,
1165                          const tr_peer     * peer,
1166                          tr_block_index_t    block )
1167{
1168    const Torrent * t = tor->torrentPeers;
1169    return requestListLookup( (Torrent*)t, block, peer ) != NULL;
1170}
1171
1172/* cancel requests that are too old */
1173static void
1174refillUpkeep( int foo UNUSED, short bar UNUSED, void * vmgr )
1175{
1176    time_t now;
1177    uint64_t now_msec;
1178    time_t too_old;
1179    tr_torrent * tor;
1180    tr_peerMgr * mgr = vmgr;
1181    managerLock( mgr );
1182
1183    now = tr_time( );
1184    now_msec = tr_date( );
1185    too_old = now - REQUEST_TTL_SECS;
1186
1187    tor = NULL;
1188    while(( tor = tr_torrentNext( mgr->session, tor )))
1189    {
1190        Torrent * t = tor->torrentPeers;
1191        const int n = t->requestCount;
1192        if( n > 0 )
1193        {
1194            int keepCount = 0;
1195            int cancelCount = 0;
1196            struct block_request * cancel = tr_new( struct block_request, n );
1197            const struct block_request * it;
1198            const struct block_request * end;
1199
1200            for( it=t->requests, end=it+n; it!=end; ++it )
1201            {
1202                if( ( it->sentAt <= too_old ) && !tr_peerMsgsIsReadingBlock( it->peer->msgs, it->block ) )
1203                    cancel[cancelCount++] = *it;
1204                else
1205                {
1206                    if( it != &t->requests[keepCount] )
1207                        t->requests[keepCount] = *it;
1208                    keepCount++;
1209                }
1210            }
1211
1212            /* prune out the ones we aren't keeping */
1213            t->requestCount = keepCount;
1214
1215            /* send cancel messages for all the "cancel" ones */
1216            for( it=cancel, end=it+cancelCount; it!=end; ++it ) {
1217                if( ( it->peer != NULL ) && ( it->peer->msgs != NULL ) ) {
1218                    tr_historyAdd( it->peer->cancelsSentToPeer, now_msec, 1 );
1219                    tr_peerMsgsCancel( it->peer->msgs, it->block );
1220                    decrementPendingReqCount( it );
1221                }
1222            }
1223
1224            /* decrement the pending request counts for the timed-out blocks */
1225            for( it=cancel, end=it+cancelCount; it!=end; ++it )
1226                pieceListRemoveRequest( t, it->block );
1227
1228            /* cleanup loop */
1229            tr_free( cancel );
1230        }
1231    }
1232
1233    tr_timerAddMsec( mgr->refillUpkeepTimer, REFILL_UPKEEP_PERIOD_MSEC );
1234    managerUnlock( mgr );
1235}
1236
1237static void
1238addStrike( Torrent * t, tr_peer * peer )
1239{
1240    tordbg( t, "increasing peer %s strike count to %d",
1241            tr_atomAddrStr( peer->atom ), peer->strikes + 1 );
1242
1243    if( ++peer->strikes >= MAX_BAD_PIECES_PER_PEER )
1244    {
1245        struct peer_atom * atom = peer->atom;
1246        atom->myflags |= MYFLAG_BANNED;
1247        peer->doPurge = 1;
1248        tordbg( t, "banning peer %s", tr_atomAddrStr( atom ) );
1249    }
1250}
1251
1252static void
1253gotBadPiece( Torrent * t, tr_piece_index_t pieceIndex )
1254{
1255    tr_torrent *   tor = t->tor;
1256    const uint32_t byteCount = tr_torPieceCountBytes( tor, pieceIndex );
1257
1258    tor->corruptCur += byteCount;
1259    tor->downloadedCur -= MIN( tor->downloadedCur, byteCount );
1260
1261    tr_announcerAddBytes( tor, TR_ANN_CORRUPT, byteCount );
1262}
1263
1264static void
1265peerSuggestedPiece( Torrent            * t UNUSED,
1266                    tr_peer            * peer UNUSED,
1267                    tr_piece_index_t     pieceIndex UNUSED,
1268                    int                  isFastAllowed UNUSED )
1269{
1270#if 0
1271    assert( t );
1272    assert( peer );
1273    assert( peer->msgs );
1274
1275    /* is this a valid piece? */
1276    if(  pieceIndex >= t->tor->info.pieceCount )
1277        return;
1278
1279    /* don't ask for it if we've already got it */
1280    if( tr_cpPieceIsComplete( t->tor->completion, pieceIndex ) )
1281        return;
1282
1283    /* don't ask for it if they don't have it */
1284    if( !tr_bitfieldHas( peer->have, pieceIndex ) )
1285        return;
1286
1287    /* don't ask for it if we're choked and it's not fast */
1288    if( !isFastAllowed && peer->clientIsChoked )
1289        return;
1290
1291    /* request the blocks that we don't have in this piece */
1292    {
1293        tr_block_index_t block;
1294        const tr_torrent * tor = t->tor;
1295        const tr_block_index_t start = tr_torPieceFirstBlock( tor, pieceIndex );
1296        const tr_block_index_t end = start + tr_torPieceCountBlocks( tor, pieceIndex );
1297
1298        for( block=start; block<end; ++block )
1299        {
1300            if( !tr_cpBlockIsComplete( tor->completion, block ) )
1301            {
1302                const uint32_t offset = getBlockOffsetInPiece( tor, block );
1303                const uint32_t length = tr_torBlockCountBytes( tor, block );
1304                tr_peerMsgsAddRequest( peer->msgs, pieceIndex, offset, length );
1305                incrementPieceRequests( t, pieceIndex );
1306            }
1307        }
1308    }
1309#endif
1310}
1311
1312static void
1313removeRequestFromTables( Torrent * t, tr_block_index_t block, const tr_peer * peer )
1314{
1315    requestListRemove( t, block, peer );
1316    pieceListRemoveRequest( t, block );
1317}
1318
1319/* peer choked us, or maybe it disconnected.
1320   either way we need to remove all its requests */
1321static void
1322peerDeclinedAllRequests( Torrent * t, const tr_peer * peer )
1323{
1324    int i, n;
1325    tr_block_index_t * blocks = tr_new( tr_block_index_t, t->requestCount );
1326
1327    for( i=n=0; i<t->requestCount; ++i )
1328        if( peer == t->requests[i].peer )
1329            blocks[n++] = t->requests[i].block;
1330
1331    for( i=0; i<n; ++i )
1332        removeRequestFromTables( t, blocks[i], peer );
1333
1334    tr_free( blocks );
1335}
1336
1337static void
1338peerCallbackFunc( tr_peer * peer, const tr_peer_event * e, void * vt )
1339{
1340    Torrent * t = vt;
1341
1342    torrentLock( t );
1343
1344    switch( e->eventType )
1345    {
1346        case TR_PEER_PEER_GOT_DATA:
1347        {
1348            const time_t now = tr_time( );
1349            tr_torrent * tor = t->tor;
1350
1351            tr_torrentSetActivityDate( tor, now );
1352
1353            if( e->wasPieceData ) {
1354                tor->uploadedCur += e->length;
1355                tr_announcerAddBytes( tor, TR_ANN_UP, e->length );
1356                tr_torrentSetDirty( tor );
1357            }
1358
1359            /* update the stats */
1360            if( e->wasPieceData )
1361                tr_statsAddUploaded( tor->session, e->length );
1362
1363            /* update our atom */
1364            if( peer && e->wasPieceData )
1365                peer->atom->piece_data_time = now;
1366
1367            break;
1368        }
1369
1370        case TR_PEER_CLIENT_GOT_REJ:
1371            removeRequestFromTables( t, _tr_block( t->tor, e->pieceIndex, e->offset ), peer );
1372            break;
1373
1374        case TR_PEER_CLIENT_GOT_CHOKE:
1375            peerDeclinedAllRequests( t, peer );
1376            break;
1377
1378        case TR_PEER_CLIENT_GOT_PORT:
1379            if( peer )
1380                peer->atom->port = e->port;
1381            break;
1382
1383        case TR_PEER_CLIENT_GOT_SUGGEST:
1384            if( peer )
1385                peerSuggestedPiece( t, peer, e->pieceIndex, FALSE );
1386            break;
1387
1388        case TR_PEER_CLIENT_GOT_ALLOWED_FAST:
1389            if( peer )
1390                peerSuggestedPiece( t, peer, e->pieceIndex, TRUE );
1391            break;
1392
1393        case TR_PEER_CLIENT_GOT_DATA:
1394        {
1395            const time_t now = tr_time( );
1396            tr_torrent * tor = t->tor;
1397
1398            tr_torrentSetActivityDate( tor, now );
1399
1400            if( e->wasPieceData ) {
1401                tor->downloadedCur += e->length;
1402                tr_torrentSetDirty( tor );
1403            }
1404
1405            /* update the stats */
1406            if( e->wasPieceData )
1407                tr_statsAddDownloaded( tor->session, e->length );
1408
1409            /* update our atom */
1410            if( peer && e->wasPieceData )
1411                peer->atom->piece_data_time = now;
1412
1413            break;
1414        }
1415
1416        case TR_PEER_PEER_PROGRESS:
1417        {
1418            if( peer )
1419            {
1420                struct peer_atom * atom = peer->atom;
1421                if( e->progress >= 1.0 ) {
1422                    tordbg( t, "marking peer %s as a seed", tr_atomAddrStr( atom ) );
1423                    atomSetSeed( atom );
1424                }
1425            }
1426            break;
1427        }
1428
1429        case TR_PEER_CLIENT_GOT_BLOCK:
1430        {
1431            tr_torrent * tor = t->tor;
1432            tr_block_index_t block = _tr_block( tor, e->pieceIndex, e->offset );
1433
1434            requestListRemove( t, block, peer );
1435            pieceListRemoveRequest( t, block );
1436
1437            if( peer != NULL )
1438                tr_historyAdd( peer->blocksSentToClient, tr_date( ), 1 );
1439
1440            if( tr_cpBlockIsComplete( &tor->completion, block ) )
1441            {
1442                /* we already have this block... */
1443                const uint32_t n = tr_torBlockCountBytes( tor, block );
1444                tor->downloadedCur -= MIN( tor->downloadedCur, n );
1445                tordbg( t, "we have this block already..." );
1446            }
1447            else
1448            {
1449                tr_cpBlockAdd( &tor->completion, block );
1450                pieceListResortPiece( t, pieceListLookup( t, e->pieceIndex ) );
1451                tr_torrentSetDirty( tor );
1452
1453                if( tr_cpPieceIsComplete( &tor->completion, e->pieceIndex ) )
1454                {
1455                    const tr_piece_index_t p = e->pieceIndex;
1456                    const tr_bool ok = tr_ioTestPiece( tor, p );
1457
1458                    if( !ok )
1459                    {
1460                        tr_torerr( tor, _( "Piece %lu, which was just downloaded, failed its checksum test" ),
1461                                   (unsigned long)p );
1462                    }
1463
1464                    tr_torrentSetHasPiece( tor, p, ok );
1465                    tr_torrentSetPieceChecked( tor, p, TRUE );
1466                    tr_peerMgrSetBlame( tor, p, ok );
1467
1468                    if( !ok )
1469                    {
1470                        gotBadPiece( t, p );
1471                    }
1472                    else
1473                    {
1474                        int i;
1475                        int peerCount;
1476                        tr_peer ** peers;
1477                        tr_file_index_t fileIndex;
1478
1479                        /* only add this to downloadedCur if we got it from a peer --
1480                         * webseeds shouldn't count against our ratio.  As one tracker
1481                         * admin put it, "Those pieces are downloaded directly from the
1482                         * content distributor, not the peers, it is the tracker's job
1483                         * to manage the swarms, not the web server and does not fit
1484                         * into the jurisdiction of the tracker." */
1485                        if( peer != NULL ) {
1486                            const uint32_t n = tr_torPieceCountBytes( tor, p );
1487                            tr_announcerAddBytes( tor, TR_ANN_DOWN, n );
1488                        }
1489
1490                        peerCount = tr_ptrArraySize( &t->peers );
1491                        peers = (tr_peer**) tr_ptrArrayBase( &t->peers );
1492                        for( i=0; i<peerCount; ++i )
1493                            tr_peerMsgsHave( peers[i]->msgs, p );
1494
1495                        for( fileIndex=0; fileIndex<tor->info.fileCount; ++fileIndex ) {
1496                            const tr_file * file = &tor->info.files[fileIndex];
1497                            if( ( file->firstPiece <= p ) && ( p <= file->lastPiece ) ) {
1498                                if( tr_cpFileIsComplete( &tor->completion, fileIndex ) ) {
1499fprintf( stderr, "flushing complete file %d (%s)\n", fileIndex, tor->info.files[fileIndex].name );
1500                                    tr_cacheFlushFile( tor->session->cache, tor, fileIndex );
1501                                    tr_torrentFileCompleted( tor, fileIndex );
1502                                }
1503                            }
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 );
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    static const int CANCEL_HISTORY_MSEC = CANCEL_HISTORY_SEC * 1000;
2315
2316    assert( t->manager );
2317    managerLock( t->manager );
2318
2319    size = tr_ptrArraySize( &t->peers );
2320    peers = (const tr_peer**) tr_ptrArrayBase( &t->peers );
2321    ret = tr_new0( tr_peer_stat, size );
2322    now = tr_date( );
2323
2324    for( i=0; i<size; ++i )
2325    {
2326        char *                   pch;
2327        const tr_peer *          peer = peers[i];
2328        const struct peer_atom * atom = peer->atom;
2329        tr_peer_stat *           stat = ret + i;
2330
2331        tr_ntop( &atom->addr, stat->addr, sizeof( stat->addr ) );
2332        tr_strlcpy( stat->client, ( peer->client ? peer->client : "" ),
2333                   sizeof( stat->client ) );
2334        stat->port                = ntohs( peer->atom->port );
2335        stat->from                = atom->from;
2336        stat->progress            = peer->progress;
2337        stat->isEncrypted         = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
2338        stat->rateToPeer          = tr_peerGetPieceSpeed( peer, now, TR_CLIENT_TO_PEER );
2339        stat->rateToClient        = tr_peerGetPieceSpeed( peer, now, TR_PEER_TO_CLIENT );
2340        stat->peerIsChoked        = peer->peerIsChoked;
2341        stat->peerIsInterested    = peer->peerIsInterested;
2342        stat->clientIsChoked      = peer->clientIsChoked;
2343        stat->clientIsInterested  = peer->clientIsInterested;
2344        stat->isIncoming          = tr_peerIoIsIncoming( peer->io );
2345        stat->isDownloadingFrom   = clientIsDownloadingFrom( tor, peer );
2346        stat->isUploadingTo       = clientIsUploadingTo( peer );
2347        stat->isSeed              = ( atom->uploadOnly == UPLOAD_ONLY_YES ) || ( peer->progress >= 1.0 );
2348
2349        stat->blocksToPeer        = tr_historyGet( peer->blocksSentToPeer,    now, CANCEL_HISTORY_MSEC );
2350        stat->blocksToClient      = tr_historyGet( peer->blocksSentToClient,  now, CANCEL_HISTORY_MSEC );
2351        stat->cancelsToPeer       = tr_historyGet( peer->cancelsSentToPeer,   now, CANCEL_HISTORY_MSEC );
2352        stat->cancelsToClient     = tr_historyGet( peer->cancelsSentToClient, now, CANCEL_HISTORY_MSEC );
2353
2354        stat->pendingReqsToPeer   = peer->pendingReqsToPeer;
2355        stat->pendingReqsToClient = peer->pendingReqsToClient;
2356
2357        pch = stat->flagStr;
2358        if( t->optimistic == peer ) *pch++ = 'O';
2359        if( stat->isDownloadingFrom ) *pch++ = 'D';
2360        else if( stat->clientIsInterested ) *pch++ = 'd';
2361        if( stat->isUploadingTo ) *pch++ = 'U';
2362        else if( stat->peerIsInterested ) *pch++ = 'u';
2363        if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ = 'K';
2364        if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
2365        if( stat->isEncrypted ) *pch++ = 'E';
2366        if( stat->from == TR_PEER_FROM_DHT ) *pch++ = 'H';
2367        if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
2368        if( stat->isIncoming ) *pch++ = 'I';
2369        *pch = '\0';
2370    }
2371
2372    *setmeCount = size;
2373
2374    managerUnlock( t->manager );
2375    return ret;
2376}
2377
2378/**
2379***
2380**/
2381
2382/* do we still want this piece and does the peer have it? */
2383static tr_bool
2384isPieceInteresting( const tr_torrent * tor, const tr_peer * peer, tr_piece_index_t index )
2385{
2386    return ( !tor->info.pieces[index].dnd ) /* we want it */
2387        && ( !tr_cpPieceIsComplete( &tor->completion, index ) )  /* we don't have it */
2388        && ( tr_bitsetHas( &peer->have, index ) ); /* peer has it */
2389}
2390
2391/* does this peer have any pieces that we want? */
2392static tr_bool
2393isPeerInteresting( const tr_torrent * tor, const tr_peer * peer )
2394{
2395    tr_piece_index_t i, n;
2396
2397    if ( tr_torrentIsSeed( tor ) )
2398        return FALSE;
2399
2400    if( !tr_torrentIsPieceTransferAllowed( tor, TR_PEER_TO_CLIENT ) )
2401        return FALSE;
2402
2403    for( i=0, n=tor->info.pieceCount; i<n; ++i )
2404        if( isPieceInteresting( tor, peer, i ) )
2405            return TRUE;
2406
2407    return FALSE;
2408}
2409
2410/* determines who we send "interested" messages to */
2411static void
2412rechokeDownloads( Torrent * t )
2413{
2414    int i;
2415    const uint64_t now = tr_date( );
2416    const int msec = 60 * 1000;
2417    const int MIN_INTERESTING_PEERS = 5;
2418    const int peerCount = tr_ptrArraySize( &t->peers );
2419    int maxPeers;
2420
2421    int badCount         = 0;
2422    int goodCount        = 0;
2423    int untestedCount    = 0;
2424    tr_peer ** bad       = tr_new( tr_peer*, peerCount );
2425    tr_peer ** good      = tr_new( tr_peer*, peerCount );
2426    tr_peer ** untested  = tr_new( tr_peer*, peerCount );
2427
2428    /* decide how many peers to be interested in */
2429    {
2430        int blocks = 0;
2431        int cancels = 0;
2432
2433        /* Count up how many blocks & cancels each peer has.
2434         *
2435         * There are two situations where we send out cancels --
2436         *
2437         * 1. We've got unresponsive peers, which is handled by deciding
2438         *    -which- peers to be interested in.
2439         *
2440         * 2. We've hit our bandwidth cap, which is handled by deciding
2441         *    -how many- peers to be interested in.
2442         *
2443         * We're working on 2. here, so we need to ignore unresponsive
2444         * peers in our calculations lest they confuse Transmission into
2445         * thinking it's hit its bandwidth cap.
2446         */
2447        for( i=0; i<peerCount; ++i )
2448        {
2449            const tr_peer * peer = tr_ptrArrayNth( &t->peers, i );
2450            const int b = tr_historyGet( peer->blocksSentToClient, now, msec );
2451            const int c = tr_historyGet( peer->cancelsSentToPeer, now, msec );
2452
2453            if( b == 0 ) /* ignore unresponsive peers, as described above */
2454                continue;
2455
2456            blocks += b;
2457            cancels += c;
2458        }
2459
2460        if( !t->interestedCount )
2461        {
2462            /* this is the torrent's first time to call this function...
2463             * start off optimistically by allowing interest in many peers */
2464            maxPeers = t->tor->maxConnectedPeers;
2465        }
2466        else if( !blocks )
2467        {
2468            /* we've gotten cancels but zero blocks...
2469             * something is seriously wrong.  throttle back sharply */
2470            maxPeers = t->interestedCount * 0.5;
2471        }
2472        else
2473        {
2474            const double cancelRate = cancels / (double)(cancels + blocks);
2475                 if( cancelRate >= 0.20 ) maxPeers = t->interestedCount * 0.7;
2476            else if( cancelRate >= 0.10 ) maxPeers = t->interestedCount * 0.8;
2477            else if( cancelRate >= 0.05 ) maxPeers = t->interestedCount * 0.9;
2478            else if( cancelRate >= 0.01 ) maxPeers = t->interestedCount;
2479            else                          maxPeers = t->interestedCount + 1;
2480
2481            /* if things are getting worse, don't add more peers */
2482            if( ( t->cancelRate > 0.01 ) && ( cancelRate > t->cancelRate ) )
2483                maxPeers = MIN( maxPeers, t->interestedCount );
2484
2485            t->cancelRate = cancelRate;
2486
2487            tordbg( t, "cancel rate is %.3f -- changing the "
2488                       "number of peers we're interested in from %d to %d",
2489                       cancelRate, t->interestedCount, maxPeers );
2490        }
2491    }
2492
2493    /* don't let the previous paragraph's number tweaking go too far... */
2494    if( maxPeers < MIN_INTERESTING_PEERS )
2495        maxPeers = MIN_INTERESTING_PEERS;
2496    if( maxPeers > t->tor->maxConnectedPeers )
2497        maxPeers = t->tor->maxConnectedPeers;
2498
2499    /* separate the peers into "good" (ones with a low cancel-to-block ratio),
2500     * untested peers, and "bad" (ones with a high cancel-to-block ratio).
2501     * That's the order in which we'll choose who to show interest in */
2502    {
2503        /* Randomize the peer array so the peers in the three groups will be unsorted... */
2504        int n = peerCount;
2505        tr_peer ** peers = tr_memdup( tr_ptrArrayBase( &t->peers ), n * sizeof( tr_peer * ) );
2506
2507        while( n > 0 )
2508        {
2509            const int i = tr_cryptoWeakRandInt( n );
2510            tr_peer * peer = tr_ptrArrayNth( &t->peers, i );
2511
2512            if( !isPeerInteresting( t->tor, peer ) )
2513            {
2514                tr_peerMsgsSetInterested( peer->msgs, FALSE );
2515            }
2516            else
2517            {
2518                const int blocks = tr_historyGet( peer->blocksSentToClient, now, msec );
2519                const int cancels = tr_historyGet( peer->cancelsSentToPeer, now, msec );
2520
2521                if( !blocks && !cancels )
2522                    untested[untestedCount++] = peer;
2523                else if( !cancels )
2524                    good[goodCount++] = peer;
2525                else if( !blocks )
2526                    bad[badCount++] = peer;
2527                else if( ( cancels * 10 ) < blocks )
2528                    good[goodCount++] = peer;
2529                else
2530                    bad[badCount++] = peer;
2531            }
2532
2533            tr_removeElementFromArray( peers, i, sizeof(tr_peer*), n-- );
2534        }
2535
2536        tr_free( peers );
2537    }
2538
2539    t->interestedCount = 0;
2540
2541    /* We've decided (1) how many peers to be interested in,
2542     * and (2) which peers are the best candidates,
2543     * Now it's time to update our `interest' flags. */
2544    for( i=0; i<goodCount; ++i ) {
2545        const tr_bool b = t->interestedCount < maxPeers;
2546        tr_peerMsgsSetInterested( good[i]->msgs, b );
2547        if( b )
2548            ++t->interestedCount;
2549    }
2550    for( i=0; i<untestedCount; ++i ) {
2551        const tr_bool b = t->interestedCount < maxPeers;
2552        tr_peerMsgsSetInterested( untested[i]->msgs, b );
2553        if( b )
2554            ++t->interestedCount;
2555    }
2556    for( i=0; i<badCount; ++i ) {
2557        const tr_bool b = t->interestedCount < maxPeers;
2558        tr_peerMsgsSetInterested( bad[i]->msgs, b );
2559        if( b )
2560            ++t->interestedCount;
2561    }
2562
2563/*fprintf( stderr, "num interested: %d\n", t->interestedCount );*/
2564
2565    /* cleanup */
2566    tr_free( untested );
2567    tr_free( good );
2568    tr_free( bad );
2569}
2570
2571/**
2572***
2573**/
2574
2575struct ChokeData
2576{
2577    tr_bool         doUnchoke;
2578    tr_bool         isInterested;
2579    tr_bool         isChoked;
2580    int             rate;
2581    tr_peer *       peer;
2582};
2583
2584static int
2585compareChoke( const void * va,
2586              const void * vb )
2587{
2588    const struct ChokeData * a = va;
2589    const struct ChokeData * b = vb;
2590
2591    if( a->rate != b->rate ) /* prefer higher overall speeds */
2592        return a->rate > b->rate ? -1 : 1;
2593
2594    if( a->isChoked != b->isChoked ) /* prefer unchoked */
2595        return a->isChoked ? 1 : -1;
2596
2597    return 0;
2598}
2599
2600/* is this a new connection? */
2601static int
2602isNew( const tr_peer * peer )
2603{
2604    return peer && peer->io && tr_peerIoGetAge( peer->io ) < 45;
2605}
2606
2607/* get a rate for deciding which peers to choke and unchoke. */
2608static int
2609getRate( const tr_torrent * tor, struct peer_atom * atom, uint64_t now )
2610{
2611    double KiB_s;
2612
2613    if( tr_torrentIsSeed( tor ) )
2614        KiB_s = tr_peerGetPieceSpeed( atom->peer, now, TR_CLIENT_TO_PEER );
2615
2616    /* downloading a private torrent... take upload speed into account
2617     * because there may only be a small window of opportunity to share */
2618    else if( tr_torrentIsPrivate( tor ) )
2619        KiB_s = tr_peerGetPieceSpeed( atom->peer, now, TR_PEER_TO_CLIENT )
2620              + tr_peerGetPieceSpeed( atom->peer, now, TR_CLIENT_TO_PEER );
2621
2622    /* downloading a public torrent */
2623    else
2624        KiB_s = tr_peerGetPieceSpeed( atom->peer, now, TR_PEER_TO_CLIENT );
2625
2626    /* convert it to bytes per second */
2627    return (int)( KiB_s * 1024 );
2628}
2629
2630static void
2631rechokeUploads( Torrent * t, const uint64_t now )
2632{
2633    int i, size, unchokedInterested;
2634    const int peerCount = tr_ptrArraySize( &t->peers );
2635    tr_peer ** peers = (tr_peer**) tr_ptrArrayBase( &t->peers );
2636    struct ChokeData * choke = tr_new0( struct ChokeData, peerCount );
2637    const tr_session * session = t->manager->session;
2638    const int chokeAll = !tr_torrentIsPieceTransferAllowed( t->tor, TR_CLIENT_TO_PEER );
2639
2640    assert( torrentIsLocked( t ) );
2641
2642    /* sort the peers by preference and rate */
2643    for( i = 0, size = 0; i < peerCount; ++i )
2644    {
2645        tr_peer * peer = peers[i];
2646        struct peer_atom * atom = peer->atom;
2647
2648        if( peer->progress >= 1.0 ) /* choke all seeds */
2649        {
2650            tr_peerMsgsSetChoke( peer->msgs, TRUE );
2651        }
2652        else if( atom->uploadOnly == UPLOAD_ONLY_YES ) /* choke partial seeds */
2653        {
2654            tr_peerMsgsSetChoke( peer->msgs, TRUE );
2655        }
2656        else if( chokeAll ) /* choke everyone if we're not uploading */
2657        {
2658            tr_peerMsgsSetChoke( peer->msgs, TRUE );
2659        }
2660        else
2661        {
2662            struct ChokeData * n = &choke[size++];
2663            n->peer         = peer;
2664            n->isInterested = peer->peerIsInterested;
2665            n->isChoked     = peer->peerIsChoked;
2666            n->rate         = getRate( t->tor, atom, now );
2667        }
2668    }
2669
2670    qsort( choke, size, sizeof( struct ChokeData ), compareChoke );
2671
2672    /**
2673     * Reciprocation and number of uploads capping is managed by unchoking
2674     * the N peers which have the best upload rate and are interested.
2675     * This maximizes the client's download rate. These N peers are
2676     * referred to as downloaders, because they are interested in downloading
2677     * from the client.
2678     *
2679     * Peers which have a better upload rate (as compared to the downloaders)
2680     * but aren't interested get unchoked. If they become interested, the
2681     * downloader with the worst upload rate gets choked. If a client has
2682     * a complete file, it uses its upload rate rather than its download
2683     * rate to decide which peers to unchoke.
2684     */
2685    unchokedInterested = 0;
2686    for( i=0; i<size && unchokedInterested<session->uploadSlotsPerTorrent; ++i ) {
2687        choke[i].doUnchoke = 1;
2688        if( choke[i].isInterested )
2689            ++unchokedInterested;
2690    }
2691
2692    /* optimistic unchoke */
2693    if( i < size )
2694    {
2695        int n;
2696        struct ChokeData * c;
2697        tr_ptrArray randPool = TR_PTR_ARRAY_INIT;
2698
2699        for( ; i<size; ++i )
2700        {
2701            if( choke[i].isInterested )
2702            {
2703                const tr_peer * peer = choke[i].peer;
2704                int x = 1, y;
2705                if( isNew( peer ) ) x *= 3;
2706                for( y=0; y<x; ++y )
2707                    tr_ptrArrayAppend( &randPool, &choke[i] );
2708            }
2709        }
2710
2711        if(( n = tr_ptrArraySize( &randPool )))
2712        {
2713            c = tr_ptrArrayNth( &randPool, tr_cryptoWeakRandInt( n ));
2714            c->doUnchoke = 1;
2715            t->optimistic = c->peer;
2716        }
2717
2718        tr_ptrArrayDestruct( &randPool, NULL );
2719    }
2720
2721    for( i=0; i<size; ++i )
2722        tr_peerMsgsSetChoke( choke[i].peer->msgs, !choke[i].doUnchoke );
2723
2724    /* cleanup */
2725    tr_free( choke );
2726}
2727
2728static void
2729rechokePulse( int foo UNUSED, short bar UNUSED, void * vmgr )
2730{
2731    uint64_t now;
2732    tr_torrent * tor = NULL;
2733    tr_peerMgr * mgr = vmgr;
2734    managerLock( mgr );
2735
2736    now = tr_date( );
2737    while(( tor = tr_torrentNext( mgr->session, tor ))) {
2738        if( tor->isRunning ) {
2739            rechokeUploads( tor->torrentPeers, now );
2740            if( !tr_torrentIsSeed( tor ) )
2741                rechokeDownloads( tor->torrentPeers );
2742        }
2743    }
2744
2745    tr_timerAddMsec( mgr->rechokeTimer, RECHOKE_PERIOD_MSEC );
2746    managerUnlock( mgr );
2747}
2748
2749/***
2750****
2751****  Life and Death
2752****
2753***/
2754
2755typedef enum
2756{
2757    TR_CAN_KEEP,
2758    TR_CAN_CLOSE,
2759    TR_MUST_CLOSE,
2760}
2761tr_close_type_t;
2762
2763static tr_close_type_t
2764shouldPeerBeClosed( const Torrent    * t,
2765                    const tr_peer    * peer,
2766                    int                peerCount,
2767                    const time_t       now )
2768{
2769    const tr_torrent *       tor = t->tor;
2770    const struct peer_atom * atom = peer->atom;
2771
2772    /* if it's marked for purging, close it */
2773    if( peer->doPurge )
2774    {
2775        tordbg( t, "purging peer %s because its doPurge flag is set",
2776                tr_atomAddrStr( atom ) );
2777        return TR_MUST_CLOSE;
2778    }
2779
2780    /* if we're seeding and the peer has everything we have,
2781     * and enough time has passed for a pex exchange, then disconnect */
2782    if( tr_torrentIsSeed( tor ) )
2783    {
2784        tr_bool peerHasEverything;
2785
2786        if( atom->seedProbability != -1 )
2787        {
2788            peerHasEverything = atomIsSeed( atom );
2789        }
2790        else
2791        {
2792            tr_bitfield * tmp = tr_bitfieldDup( tr_cpPieceBitfield( &tor->completion ) );
2793            tr_bitsetDifference( tmp, &peer->have );
2794            peerHasEverything = tr_bitfieldCountTrueBits( tmp ) == 0;
2795            tr_bitfieldFree( tmp );
2796        }
2797
2798        if( peerHasEverything && ( !tr_torrentAllowsPex(tor) || (now-atom->time>=30 )))
2799        {
2800            tordbg( t, "purging peer %s because we're both seeds",
2801                    tr_atomAddrStr( atom ) );
2802            return TR_MUST_CLOSE;
2803        }
2804    }
2805
2806    /* disconnect if it's been too long since piece data has been transferred.
2807     * this is on a sliding scale based on number of available peers... */
2808    {
2809        const int relaxStrictnessIfFewerThanN = (int)( ( getMaxPeerCount( tor ) * 0.9 ) + 0.5 );
2810        /* if we have >= relaxIfFewerThan, strictness is 100%.
2811         * if we have zero connections, strictness is 0% */
2812        const float strictness = peerCount >= relaxStrictnessIfFewerThanN
2813                               ? 1.0
2814                               : peerCount / (float)relaxStrictnessIfFewerThanN;
2815        const int lo = MIN_UPLOAD_IDLE_SECS;
2816        const int hi = MAX_UPLOAD_IDLE_SECS;
2817        const int limit = hi - ( ( hi - lo ) * strictness );
2818        const int idleTime = now - MAX( atom->time, atom->piece_data_time );
2819/*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 );*/
2820        if( idleTime > limit ) {
2821            tordbg( t, "purging peer %s because it's been %d secs since we shared anything",
2822                       tr_atomAddrStr( atom ), idleTime );
2823            return TR_CAN_CLOSE;
2824        }
2825    }
2826
2827    return TR_CAN_KEEP;
2828}
2829
2830static void sortPeersByLivelinessReverse( tr_peer ** peers, void ** clientData, int n, uint64_t now );
2831
2832static tr_peer **
2833getPeersToClose( Torrent * t, tr_close_type_t closeType, const time_t now, int * setmeSize )
2834{
2835    int i, peerCount, outsize;
2836    tr_peer ** peers = (tr_peer**) tr_ptrArrayPeek( &t->peers, &peerCount );
2837    struct tr_peer ** ret = tr_new( tr_peer *, peerCount );
2838
2839    assert( torrentIsLocked( t ) );
2840
2841    for( i = outsize = 0; i < peerCount; ++i )
2842        if( shouldPeerBeClosed( t, peers[i], peerCount, now ) == closeType )
2843            ret[outsize++] = peers[i];
2844
2845    sortPeersByLivelinessReverse ( ret, NULL, outsize, tr_date( ) );
2846
2847    *setmeSize = outsize;
2848    return ret;
2849}
2850
2851static int
2852getReconnectIntervalSecs( const struct peer_atom * atom, const time_t now )
2853{
2854    int sec;
2855
2856    /* if we were recently connected to this peer and transferring piece
2857     * data, try to reconnect to them sooner rather that later -- we don't
2858     * want network troubles to get in the way of a good peer. */
2859    if( ( now - atom->piece_data_time ) <= ( MINIMUM_RECONNECT_INTERVAL_SECS * 2 ) )
2860        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2861
2862    /* don't allow reconnects more often than our minimum */
2863    else if( ( now - atom->time ) < MINIMUM_RECONNECT_INTERVAL_SECS )
2864        sec = MINIMUM_RECONNECT_INTERVAL_SECS;
2865
2866    /* otherwise, the interval depends on how many times we've tried
2867     * and failed to connect to the peer */
2868    else switch( atom->numFails ) {
2869        case 0: sec = 0; break;
2870        case 1: sec = 5; break;
2871        case 2: sec = 2 * 60; break;
2872        case 3: sec = 15 * 60; break;
2873        case 4: sec = 30 * 60; break;
2874        case 5: sec = 60 * 60; break;
2875        default: sec = 120 * 60; break;
2876    }
2877
2878    /* penalize peers that were unreachable the last time we tried */
2879    if( atom->myflags & MYFLAG_UNREACHABLE )
2880        sec += sec;
2881
2882    dbgmsg( "reconnect interval for %s is %d seconds", tr_atomAddrStr( atom ), sec );
2883    return sec;
2884}
2885
2886static void
2887closePeer( Torrent * t, tr_peer * peer )
2888{
2889    struct peer_atom * atom;
2890
2891    assert( t != NULL );
2892    assert( peer != NULL );
2893
2894    atom = peer->atom;
2895
2896    /* if we transferred piece data, then they might be good peers,
2897       so reset their `numFails' weight to zero.  otherwise we connected
2898       to them fruitlessly, so mark it as another fail */
2899    if( atom->piece_data_time ) {
2900        tordbg( t, "resetting atom %s numFails to 0", tr_atomAddrStr(atom) );
2901        atom->numFails = 0;
2902    } else {
2903        ++atom->numFails;
2904        tordbg( t, "incremented atom %s numFails to %d", tr_atomAddrStr(atom), (int)atom->numFails );
2905    }
2906
2907    tordbg( t, "removing bad peer %s", tr_peerIoGetAddrStr( peer->io ) );
2908    removePeer( t, peer );
2909}
2910
2911static void
2912closeBadPeers( Torrent * t )
2913{
2914    const time_t  now = tr_time( );
2915
2916    if( !t->isRunning )
2917    {
2918        removeAllPeers( t );
2919    }
2920    else
2921    {
2922        int i;
2923        int mustCloseCount;
2924        struct tr_peer ** mustClose;
2925
2926        /* disconnect the really bad peers */
2927        mustClose = getPeersToClose( t, TR_MUST_CLOSE, now, &mustCloseCount );
2928        for( i=0; i<mustCloseCount; ++i )
2929            closePeer( t, mustClose[i] );
2930        tr_free( mustClose );
2931    }
2932}
2933
2934struct peer_liveliness
2935{
2936    tr_peer * peer;
2937    void * clientData;
2938    time_t pieceDataTime;
2939    time_t time;
2940    int speed;
2941    tr_bool doPurge;
2942};
2943
2944static int
2945comparePeerLiveliness( const void * va, const void * vb )
2946{
2947    const struct peer_liveliness * a = va;
2948    const struct peer_liveliness * b = vb;
2949
2950    if( a->doPurge != b->doPurge )
2951        return a->doPurge ? 1 : -1;
2952
2953    if( a->speed != b->speed ) /* faster goes first */
2954        return a->speed > b->speed ? -1 : 1;
2955
2956    /* the one to give us data more recently goes first */
2957    if( a->pieceDataTime != b->pieceDataTime )
2958        return a->pieceDataTime > b->pieceDataTime ? -1 : 1;
2959
2960    /* the one we connected to most recently goes first */
2961    if( a->time != b->time )
2962        return a->time > b->time ? -1 : 1;
2963
2964    return 0;
2965}
2966
2967static int
2968comparePeerLivelinessReverse( const void * va, const void * vb )
2969{
2970    return -comparePeerLiveliness (va, vb);
2971}
2972
2973static void
2974sortPeersByLivelinessImpl( tr_peer  ** peers,
2975                           void     ** clientData,
2976                           int         n,
2977                           uint64_t    now,
2978                           int (*compare) ( const void *va, const void *vb ) )
2979{
2980    int i;
2981    struct peer_liveliness *lives, *l;
2982
2983    /* build a sortable array of peer + extra info */
2984    lives = l = tr_new0( struct peer_liveliness, n );
2985    for( i=0; i<n; ++i, ++l )
2986    {
2987        tr_peer * p = peers[i];
2988        l->peer = p;
2989        l->doPurge = p->doPurge;
2990        l->pieceDataTime = p->atom->piece_data_time;
2991        l->time = p->atom->time;
2992        l->speed = 1024.0 * (   tr_peerGetPieceSpeed( p, now, TR_UP )
2993                              + tr_peerGetPieceSpeed( p, now, TR_DOWN ) );
2994        if( clientData )
2995            l->clientData = clientData[i];
2996    }
2997
2998    /* sort 'em */
2999    assert( n == ( l - lives ) );
3000    qsort( lives, n, sizeof( struct peer_liveliness ), compare );
3001
3002    /* build the peer array */
3003    for( i=0, l=lives; i<n; ++i, ++l ) {
3004        peers[i] = l->peer;
3005        if( clientData )
3006            clientData[i] = l->clientData;
3007    }
3008    assert( n == ( l - lives ) );
3009
3010    /* cleanup */
3011    tr_free( lives );
3012}
3013
3014static void
3015sortPeersByLiveliness( tr_peer ** peers, void ** clientData, int n, uint64_t now )
3016{
3017    sortPeersByLivelinessImpl( peers, clientData, n, now, comparePeerLiveliness );
3018}
3019
3020static void
3021sortPeersByLivelinessReverse( tr_peer ** peers, void ** clientData, int n, uint64_t now )
3022{
3023    sortPeersByLivelinessImpl( peers, clientData, n, now, comparePeerLivelinessReverse );
3024}
3025
3026
3027static void
3028enforceTorrentPeerLimit( Torrent * t, uint64_t now )
3029{
3030    int n = tr_ptrArraySize( &t->peers );
3031    const int max = tr_torrentGetPeerLimit( t->tor );
3032    if( n > max )
3033    {
3034        void * base = tr_ptrArrayBase( &t->peers );
3035        tr_peer ** peers = tr_memdup( base, n*sizeof( tr_peer* ) );
3036        sortPeersByLiveliness( peers, NULL, n, now );
3037        while( n > max )
3038            closePeer( t, peers[--n] );
3039        tr_free( peers );
3040    }
3041}
3042
3043static void
3044enforceSessionPeerLimit( tr_session * session, uint64_t now )
3045{
3046    int n = 0;
3047    tr_torrent * tor = NULL;
3048    const int max = tr_sessionGetPeerLimit( session );
3049
3050    /* count the total number of peers */
3051    while(( tor = tr_torrentNext( session, tor )))
3052        n += tr_ptrArraySize( &tor->torrentPeers->peers );
3053
3054    /* if there are too many, prune out the worst */
3055    if( n > max )
3056    {
3057        tr_peer ** peers = tr_new( tr_peer*, n );
3058        Torrent ** torrents = tr_new( Torrent*, n );
3059
3060        /* populate the peer array */
3061        n = 0;
3062        tor = NULL;
3063        while(( tor = tr_torrentNext( session, tor ))) {
3064            int i;
3065            Torrent * t = tor->torrentPeers;
3066            const int tn = tr_ptrArraySize( &t->peers );
3067            for( i=0; i<tn; ++i, ++n ) {
3068                peers[n] = tr_ptrArrayNth( &t->peers, i );
3069                torrents[n] = t;
3070            }
3071        }
3072
3073        /* sort 'em */
3074        sortPeersByLiveliness( peers, (void**)torrents, n, now );
3075
3076        /* cull out the crappiest */
3077        while( n-- > max )
3078            closePeer( torrents[n], peers[n] );
3079
3080        /* cleanup */
3081        tr_free( torrents );
3082        tr_free( peers );
3083    }
3084}
3085
3086static void makeNewPeerConnections( tr_peerMgr * mgr, const int max );
3087
3088static void
3089reconnectPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
3090{
3091    tr_torrent * tor;
3092    tr_peerMgr * mgr = vmgr;
3093    const uint64_t now = tr_date( );
3094
3095    /**
3096    ***  enforce the per-session and per-torrent peer limits
3097    **/
3098
3099    /* if we're over the per-torrent peer limits, cull some peers */
3100    tor = NULL;
3101    while(( tor = tr_torrentNext( mgr->session, tor )))
3102        if( tor->isRunning )
3103            enforceTorrentPeerLimit( tor->torrentPeers, now );
3104
3105    /* if we're over the per-session peer limits, cull some peers */
3106    enforceSessionPeerLimit( mgr->session, now );
3107
3108    /* remove crappy peers */
3109    tor = NULL;
3110    while(( tor = tr_torrentNext( mgr->session, tor )))
3111        closeBadPeers( tor->torrentPeers );
3112
3113    /* try to make new peer connections */
3114    makeNewPeerConnections( mgr, MAX_CONNECTIONS_PER_PULSE );
3115}
3116
3117/****
3118*****
3119*****  BANDWIDTH ALLOCATION
3120*****
3121****/
3122
3123static void
3124pumpAllPeers( tr_peerMgr * mgr )
3125{
3126    tr_torrent * tor = NULL;
3127
3128    while(( tor = tr_torrentNext( mgr->session, tor )))
3129    {
3130        int j;
3131        Torrent * t = tor->torrentPeers;
3132
3133        for( j=0; j<tr_ptrArraySize( &t->peers ); ++j )
3134        {
3135            tr_peer * peer = tr_ptrArrayNth( &t->peers, j );
3136            tr_peerMsgsPulse( peer->msgs );
3137        }
3138    }
3139}
3140
3141static void
3142bandwidthPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
3143{
3144    tr_torrent * tor;
3145    tr_peerMgr * mgr = vmgr;
3146    managerLock( mgr );
3147
3148    /* FIXME: this next line probably isn't necessary... */
3149    pumpAllPeers( mgr );
3150
3151    /* allocate bandwidth to the peers */
3152    tr_bandwidthAllocate( mgr->session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC );
3153    tr_bandwidthAllocate( mgr->session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC );
3154
3155    /* possibly stop torrents that have seeded enough */
3156    tor = NULL;
3157    while(( tor = tr_torrentNext( mgr->session, tor )))
3158        tr_torrentCheckSeedRatio( tor );
3159
3160    /* run the completeness check for any torrents that need it */
3161    tor = NULL;
3162    while(( tor = tr_torrentNext( mgr->session, tor ))) {
3163        if( tor->torrentPeers->needsCompletenessCheck ) {
3164            tor->torrentPeers->needsCompletenessCheck  = FALSE;
3165            tr_torrentRecheckCompleteness( tor );
3166        }
3167    }
3168
3169    /* possibly stop torrents that have an error */
3170    tor = NULL;
3171    while(( tor = tr_torrentNext( mgr->session, tor )))
3172        if( tor->isRunning && ( tor->error == TR_STAT_LOCAL_ERROR ))
3173            tr_torrentStop( tor );
3174
3175    reconnectPulse( 0, 0, mgr );
3176
3177    tr_timerAddMsec( mgr->bandwidthTimer, BANDWIDTH_PERIOD_MSEC );
3178    managerUnlock( mgr );
3179}
3180
3181/***
3182****
3183***/
3184
3185static int
3186compareAtomPtrsByAddress( const void * va, const void *vb )
3187{
3188    const struct peer_atom * a = * (const struct peer_atom**) va;
3189    const struct peer_atom * b = * (const struct peer_atom**) vb;
3190
3191    assert( tr_isAtom( a ) );
3192    assert( tr_isAtom( b ) );
3193
3194    return tr_compareAddresses( &a->addr, &b->addr );
3195}
3196
3197/* best come first, worst go last */
3198static int
3199compareAtomPtrsByShelfDate( const void * va, const void *vb )
3200{
3201    time_t atime;
3202    time_t btime;
3203    const struct peer_atom * a = * (const struct peer_atom**) va;
3204    const struct peer_atom * b = * (const struct peer_atom**) vb;
3205    const int data_time_cutoff_secs = 60 * 60;
3206    const time_t tr_now = tr_time( );
3207
3208    assert( tr_isAtom( a ) );
3209    assert( tr_isAtom( b ) );
3210
3211    /* primary key: the last piece data time *if* it was within the last hour */
3212    atime = a->piece_data_time; if( atime + data_time_cutoff_secs < tr_now ) atime = 0;
3213    btime = b->piece_data_time; if( btime + data_time_cutoff_secs < tr_now ) btime = 0;
3214    if( atime != btime )
3215        return atime > btime ? -1 : 1;
3216
3217    /* secondary key: shelf date. */
3218    if( a->shelf_date != b->shelf_date )
3219        return a->shelf_date > b->shelf_date ? -1 : 1;
3220
3221    return 0;
3222}
3223
3224static int
3225getMaxAtomCount( const tr_torrent * tor )
3226{
3227    /* FIXME: this curve should be smoother... */
3228    const int n = tor->maxConnectedPeers;
3229    if( n >= 200 ) return n * 1.5;
3230    if( n >= 100 ) return n * 2;
3231    if( n >=  50 ) return n * 3;
3232    if( n >=  20 ) return n * 5;
3233    return n * 10;
3234}
3235
3236static void
3237atomPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
3238{
3239    tr_torrent * tor = NULL;
3240    tr_peerMgr * mgr = vmgr;
3241    managerLock( mgr );
3242
3243    while(( tor = tr_torrentNext( mgr->session, tor )))
3244    {
3245        int atomCount;
3246        Torrent * t = tor->torrentPeers;
3247        const int maxAtomCount = getMaxAtomCount( tor );
3248        struct peer_atom ** atoms = (struct peer_atom**) tr_ptrArrayPeek( &t->pool, &atomCount );
3249
3250        if( atomCount > maxAtomCount ) /* we've got too many atoms... time to prune */
3251        {
3252            int i;
3253            int keepCount = 0;
3254            int testCount = 0;
3255            struct peer_atom ** keep = tr_new( struct peer_atom*, atomCount );
3256            struct peer_atom ** test = tr_new( struct peer_atom*, atomCount );
3257
3258            /* keep the ones that are in use */
3259            for( i=0; i<atomCount; ++i ) {
3260                struct peer_atom * atom = atoms[i];
3261                if( peerIsInUse( t, atom ) )
3262                    keep[keepCount++] = atom;
3263                else
3264                    test[testCount++] = atom;
3265            }
3266
3267            /* if there's room, keep the best of what's left */
3268            i = 0;
3269            if( keepCount < maxAtomCount ) {
3270                qsort( test, testCount, sizeof( struct peer_atom * ), compareAtomPtrsByShelfDate );
3271                while( i<testCount && keepCount<maxAtomCount )
3272                    keep[keepCount++] = test[i++];
3273            }
3274
3275            /* free the culled atoms */
3276            while( i<testCount )
3277                tr_free( test[i++] );
3278
3279            /* rebuild Torrent.pool with what's left */
3280            tr_ptrArrayDestruct( &t->pool, NULL );
3281            t->pool = TR_PTR_ARRAY_INIT;
3282            qsort( keep, keepCount, sizeof( struct peer_atom * ), compareAtomPtrsByAddress );
3283            for( i=0; i<keepCount; ++i )
3284                tr_ptrArrayAppend( &t->pool, keep[i] );
3285
3286            tordbg( t, "max atom count is %d... pruned from %d to %d\n", maxAtomCount, atomCount, keepCount );
3287
3288            /* cleanup */
3289            tr_free( test );
3290            tr_free( keep );
3291        }
3292    }
3293
3294    tr_timerAddMsec( mgr->atomTimer, ATOM_PERIOD_MSEC );
3295    managerUnlock( mgr );
3296}
3297
3298/***
3299****
3300****
3301****
3302***/
3303
3304static inline tr_bool
3305isBandwidthMaxedOut( const tr_bandwidth * b,
3306                     const uint64_t now_msec, tr_direction dir )
3307{
3308    if( !tr_bandwidthIsLimited( b, dir ) )
3309        return FALSE;
3310    else {
3311        const double got = tr_bandwidthGetPieceSpeed( b, now_msec, dir );
3312        const double want = tr_bandwidthGetDesiredSpeed( b, dir );
3313        return got >= want;
3314    }
3315}
3316
3317/* is this atom someone that we'd want to initiate a connection to? */
3318static tr_bool
3319isPeerCandidate( const tr_torrent * tor, struct peer_atom * atom, const time_t now )
3320{
3321    /* not if they're banned... */
3322    if( atom->myflags & MYFLAG_BANNED )
3323        return FALSE;
3324
3325    /* not if we're both seeds */
3326    if( tr_torrentIsSeed( tor ) )
3327        if( atomIsSeed( atom ) || ( atom->uploadOnly == UPLOAD_ONLY_YES ) )
3328            return FALSE;
3329 
3330    /* not if we just tried them already */
3331    if( ( now - atom->time ) < getReconnectIntervalSecs( atom, now ) )
3332        return FALSE;
3333
3334    /* not if they're blocklisted */
3335    if( isAtomBlocklisted( tor->session, atom ) )
3336        return FALSE;
3337
3338    /* not if we've already got a connection to them...  */
3339    if( peerIsInUse( tor->torrentPeers, atom ) )
3340        return FALSE;
3341
3342    return TRUE;
3343}
3344
3345struct peer_candidate
3346{
3347    uint64_t score;
3348    tr_torrent * tor;
3349    struct peer_atom * atom;
3350};
3351
3352static tr_bool
3353torrentWasRecentlyStarted( const tr_torrent * tor )
3354{
3355    return difftime( tr_time( ), tor->startDate ) < 120;
3356}
3357
3358static inline uint64_t
3359addValToKey( uint64_t value, int width, uint64_t addme )
3360{
3361    value = (value << (uint64_t)width);
3362    value |= addme;
3363    return value;
3364}
3365
3366/* smaller value is better */
3367static uint64_t
3368getPeerCandidateScore( const tr_torrent * tor, const struct peer_atom * atom, uint8_t salt  )
3369{
3370    uint64_t i;
3371    uint64_t score = 0;
3372    const tr_bool failed = atom->lastConnectionAt < atom->lastConnectionAttemptAt;
3373
3374    /* prefer peers we've connected to, or never tried, over peers we failed to connect to. */
3375    i = failed ? 1 : 0;
3376    score = addValToKey( score, 1, i );
3377
3378    /* prefer the one we attempted least recently (to cycle through all peers) */
3379    i = atom->lastConnectionAttemptAt;
3380    score = addValToKey( score, 32, i );
3381
3382    /* prefer peers belonging to a torrent of a higher priority */
3383    switch( tr_torrentGetPriority( tor ) ) {
3384        case TR_PRI_HIGH:    i = 0; break;
3385        case TR_PRI_NORMAL:  i = 1; break;
3386        case TR_PRI_LOW:     i = 2; break;
3387    }
3388    score = addValToKey( score, 4, i );
3389
3390    /* prefer recently-started torrents */
3391    i = torrentWasRecentlyStarted( tor ) ? 0 : 1;
3392    score = addValToKey( score, 1, i );
3393
3394    /* prefer peers that we might have a chance of uploading to...
3395       so lower seed probability is better */
3396    if( atom->seedProbability == 100 ) i = 101;
3397    else if( atom->seedProbability == -1 ) i = 100;
3398    else i = atom->seedProbability;
3399    score = addValToKey( score, 8, i );
3400
3401    /* Prefer peers that we got from more trusted sources.
3402     * lower `from' values indicate more trusted sources */
3403    score = addValToKey( score, 4, atom->from );
3404
3405    /* salt */
3406    score = addValToKey( score, 8, salt );
3407
3408    return score;
3409}
3410
3411/* sort an array of peer candidates */
3412static int
3413comparePeerCandidates( const void * va, const void * vb )
3414{
3415    const struct peer_candidate * a = va;
3416    const struct peer_candidate * b = vb;
3417
3418    if( a->score < b->score ) return -1;
3419    if( a->score > b->score ) return 1;
3420
3421    return 0;
3422}
3423
3424/** @return an array of all the atoms we might want to connect to */
3425static struct peer_candidate*
3426getPeerCandidates( tr_session * session, int * candidateCount )
3427{
3428    int n;
3429    tr_torrent * tor;
3430    struct peer_candidate * candidates;
3431    struct peer_candidate * walk;
3432    const time_t now = tr_time( );
3433    const uint64_t now_msec = tr_date( );
3434    /* leave 5% of connection slots for incoming connections -- ticket #2609 */
3435    const int maxCandidates = tr_sessionGetPeerLimit( session ) * 0.95;
3436
3437    /* don't start any new handshakes if we're full up */
3438    n = 0;
3439    tor= NULL;
3440    while(( tor = tr_torrentNext( session, tor )))
3441        n += tr_ptrArraySize( &tor->torrentPeers->peers );
3442    if( maxCandidates <= n ) {
3443        *candidateCount = 0;
3444        return NULL;
3445    }
3446
3447    /* allocate an array of candidates */
3448    n = 0;
3449    tor= NULL;
3450    while(( tor = tr_torrentNext( session, tor )))
3451        n += tr_ptrArraySize( &tor->torrentPeers->pool );
3452    walk = candidates = tr_new( struct peer_candidate, n );
3453
3454    /* populate the candidate array */
3455    tor = NULL;
3456    while(( tor = tr_torrentNext( session, tor )))
3457    {
3458        int i, nAtoms;
3459        struct peer_atom ** atoms;
3460
3461        if( !tor->torrentPeers->isRunning )
3462            continue;
3463
3464        /* if we've already got enough peers in this torrent... */
3465        if( tr_torrentGetPeerLimit( tor ) <= tr_ptrArraySize( &tor->torrentPeers->peers ) )
3466            continue;
3467
3468        /* if we've already got enough speed in this torrent... */
3469        if( tr_torrentIsSeed( tor ) && isBandwidthMaxedOut( tor->bandwidth, now_msec, TR_UP ) )
3470            continue;
3471
3472        atoms = (struct peer_atom**) tr_ptrArrayPeek( &tor->torrentPeers->pool, &nAtoms );
3473        for( i=0; i<nAtoms; ++i )
3474        {
3475            struct peer_atom * atom = atoms[i];
3476
3477            if( isPeerCandidate( tor, atom, now ) )
3478            {
3479                const uint8_t salt = tr_cryptoWeakRandInt( 1024 );
3480                walk->tor = tor;
3481                walk->atom = atom;
3482                walk->score = getPeerCandidateScore( tor, atom, salt );
3483                ++walk;
3484            }
3485        }
3486    }
3487
3488    *candidateCount = walk - candidates;
3489    if( *candidateCount > 1 )
3490        qsort( candidates, *candidateCount, sizeof( struct peer_candidate ), comparePeerCandidates );
3491    return candidates;
3492}
3493
3494static void
3495initiateConnection( tr_peerMgr * mgr, Torrent * t, struct peer_atom * atom )
3496{
3497    tr_peerIo * io;
3498    const time_t now = tr_time( );
3499
3500    tordbg( t, "Starting an OUTGOING connection with %s", tr_atomAddrStr( atom ) );
3501
3502    io = tr_peerIoNewOutgoing( mgr->session,
3503                               mgr->session->bandwidth,
3504                               &atom->addr,
3505                               atom->port,
3506                               t->tor->info.hash,
3507                               t->tor->completeness == TR_SEED );
3508
3509    if( io == NULL )
3510    {
3511        tordbg( t, "peerIo not created; marking peer %s as unreachable",
3512                tr_atomAddrStr( atom ) );
3513        atom->myflags |= MYFLAG_UNREACHABLE;
3514        atom->numFails++;
3515    }
3516    else
3517    {
3518        tr_handshake * handshake = tr_handshakeNew( io,
3519                                                    mgr->session->encryptionMode,
3520                                                    myHandshakeDoneCB,
3521                                                    mgr );
3522
3523        assert( tr_peerIoGetTorrentHash( io ) );
3524
3525        tr_peerIoUnref( io ); /* balanced by the initial ref
3526                                 in tr_peerIoNewOutgoing() */
3527
3528        tr_ptrArrayInsertSorted( &t->outgoingHandshakes, handshake,
3529                                 handshakeCompare );
3530    }
3531
3532    atom->lastConnectionAttemptAt = now;
3533    atom->time = now;
3534}
3535
3536static void
3537initiateCandidateConnection( tr_peerMgr * mgr, struct peer_candidate * c )
3538{
3539#if 0
3540    fprintf( stderr, "Starting an OUTGOING connection with %s - [%s] seedProbability==%d; %s, %s\n",
3541             tr_atomAddrStr( c->atom ),
3542             tr_torrentName( c->tor ),
3543             (int)c->atom->seedProbability,
3544             tr_torrentIsPrivate( c->tor ) ? "private" : "public",
3545             tr_torrentIsSeed( c->tor ) ? "seed" : "downloader" );
3546#endif
3547
3548    initiateConnection( mgr, c->tor->torrentPeers, c->atom );
3549}
3550
3551static void
3552makeNewPeerConnections( struct tr_peerMgr * mgr, const int max )
3553{
3554    int i, n;
3555    struct peer_candidate * candidates;
3556
3557    candidates = getPeerCandidates( mgr->session, &n );
3558
3559    for( i=0; i<n && i<max; ++i )
3560        initiateCandidateConnection( mgr, &candidates[i] );
3561
3562    tr_free( candidates );
3563}
Note: See TracBrowser for help on using the repository browser.