source: branches/encryption/libtransmission/peer-mgr.c @ 2989

Last change on this file since 2989 was 2989, checked in by charles, 15 years ago

update the request block array after the recheck finishes, so that we don't re-ask for blocks we've got

  • Property svn:keywords set to Date Rev Author Id
File size: 24.8 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: peer-mgr.c 2989 2007-09-09 00:29:59Z charles $
11 */
12
13#include <assert.h>
14#include <string.h> /* memcpy, memcmp */
15#include <stdlib.h> /* qsort */
16#include <stdio.h> /* printf */
17
18#include "transmission.h"
19#include "handshake.h"
20#include "completion.h"
21#include "net.h"
22#include "peer-io.h"
23#include "peer-mgr.h"
24#include "peer-mgr-private.h"
25#include "peer-msgs.h"
26#include "ptrarray.h"
27#include "timer.h"
28#include "utils.h"
29
30#define MINUTES_TO_MSEC(N) ((N) * 60 * 1000)
31
32/* how frequently to change which peers are choked */
33#define RECHOKE_PERIOD_SECONDS (MINUTES_TO_MSEC(10))
34
35/* how many downloaders to unchoke per-torrent.
36 * http://wiki.theory.org/BitTorrentSpecification#Choking_and_Optimistic_Unchoking */
37#define NUM_DOWNLOADERS_TO_UNCHOKE 4
38
39/* across all torrents, how many peers maximum do we want connected? */
40#define MAX_CONNECTED_PEERS 80
41
42/**
43***
44**/
45
46struct tr_block
47{
48    unsigned int have          : 1;
49    unsigned int dnd           : 1;
50    unsigned int low_priority  : 1;
51    unsigned int high_priority : 1;
52    uint8_t requestCount;
53    uint8_t scarcity;
54    uint32_t block;
55};
56
57#define MAX_SCARCITY UINT8_MAX
58#define MAX_REQ_COUNT UINT8_MAX
59
60static void
61incrementReqCount( struct tr_block * block )
62{
63    assert( block != NULL );
64
65    if( block->requestCount < MAX_REQ_COUNT )
66        block->requestCount++;
67}
68
69static void
70incrementScarcity( struct tr_block * block )
71{
72    assert( block != NULL );
73
74    if( block->scarcity < MAX_SCARCITY )
75        block->scarcity++;
76}
77
78static int
79compareBlockByIndex( const void * va, const void * vb )
80{
81    const struct tr_block * a = (const struct tr_block *) va;
82    const struct tr_block * b = (const struct tr_block *) vb;
83    return tr_compareUint32( a->block, b->block );
84}
85
86static int
87compareBlockByInterest( const void * va, const void * vb )
88{
89    const struct tr_block * a = (const struct tr_block *) va;
90    const struct tr_block * b = (const struct tr_block *) vb;
91    int i;
92
93    if( a->dnd != b->dnd )
94        return a->dnd ? 1 : -1;
95
96    if( a->have != b->have )
97        return a->have ? 1 : -1;
98
99    if(( i = tr_compareUint8( a->requestCount, b->requestCount )))
100        return i;
101
102    if( a->high_priority != b->high_priority )
103        return a->high_priority ? -1 : 1;
104
105    if( a->low_priority != b->low_priority )
106        return a->low_priority ? 1 : -1;
107
108    if(( i = tr_compareUint16( a->scarcity, b->scarcity )))
109        return i;
110
111    if(( i = tr_compareUint32( a->block, b->block )))
112        return i;
113
114    return 0;
115}
116
117/**
118***
119**/
120
121typedef struct
122{
123    uint8_t hash[SHA_DIGEST_LENGTH];
124    tr_ptrArray * peers; /* tr_peer */
125    tr_timer_tag choke_tag;
126    tr_timer_tag refill_tag;
127    tr_torrent * tor;
128
129    struct tr_block * blocks;
130    uint32_t blockCount;
131
132    struct tr_peerMgr * manager;
133}
134Torrent;
135
136struct tr_peerMgr
137{
138    tr_handle * handle;
139    tr_ptrArray * torrents; /* Torrent */
140    int connectionCount;
141};
142
143/**
144***
145**/
146
147static int
148torrentCompare( const void * va, const void * vb )
149{
150    const Torrent * a = (const Torrent*) va;
151    const Torrent * b = (const Torrent*) vb;
152    return memcmp( a->hash, b->hash, SHA_DIGEST_LENGTH );
153}
154
155static int
156torrentCompareToHash( const void * va, const void * vb )
157{
158    const Torrent * a = (const Torrent*) va;
159    const uint8_t * b_hash = (const uint8_t*) vb;
160    return memcmp( a->hash, b_hash, SHA_DIGEST_LENGTH );
161}
162
163static Torrent*
164getExistingTorrent( tr_peerMgr * manager, const uint8_t * hash )
165{
166    return (Torrent*) tr_ptrArrayFindSorted( manager->torrents,
167                                             hash,
168                                             torrentCompareToHash );
169}
170
171static int chokePulse( void * vtorrent );
172
173static int
174peerCompare( const void * va, const void * vb )
175{
176    const tr_peer * a = (const tr_peer *) va;
177    const tr_peer * b = (const tr_peer *) vb;
178    return memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) );
179}
180
181static int
182peerCompareToAddr( const void * va, const void * vb )
183{
184    const tr_peer * a = (const tr_peer *) va;
185    const struct in_addr * b = (const struct in_addr *) vb;
186    return memcmp( &a->in_addr, b, sizeof(struct in_addr) );
187}
188
189static tr_peer*
190getExistingPeer( Torrent * torrent, const struct in_addr * in_addr )
191{
192    return (tr_peer*) tr_ptrArrayFindSorted( torrent->peers,
193                                             in_addr,
194                                             peerCompareToAddr );
195}
196
197static tr_peer*
198getPeer( Torrent * torrent, const struct in_addr * in_addr )
199{
200    tr_peer * peer = getExistingPeer( torrent, in_addr );
201    if( peer == NULL )
202    {
203        peer = tr_new0( tr_peer, 1 );
204        memcpy( &peer->in_addr, in_addr, sizeof(struct in_addr) );
205        tr_ptrArrayInsertSorted( torrent->peers, peer, peerCompare );
206fprintf( stderr, "getPeer: torrent %p now has %d peers\n", torrent, tr_ptrArraySize(torrent->peers) );
207    }
208    return peer;
209}
210
211static void
212freePeer( tr_peer * peer )
213{
214    tr_peerMsgsFree( peer->msgs );
215    tr_bitfieldFree( peer->have );
216    tr_bitfieldFree( peer->blame );
217    tr_bitfieldFree( peer->banned );
218    tr_peerIoFree( peer->io );
219    tr_free( peer->client );
220    tr_free( peer );
221}
222
223static void
224freeTorrent( tr_peerMgr * manager, Torrent * t )
225{
226    int i, size;
227    tr_peer ** peers;
228
229    assert( manager != NULL );
230    assert( t != NULL );
231    assert( t->peers != NULL );
232
233    peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &size );
234    tr_timerFree( &t->choke_tag );
235    tr_timerFree( &t->refill_tag );
236    for( i=0; i<size; ++i )
237        freePeer( peers[i] );
238    tr_ptrArrayFree( t->peers );
239    tr_ptrArrayRemoveSorted( manager->torrents, t, torrentCompare );
240    tr_free( t->blocks );
241    tr_free( t );
242}
243
244/**
245***
246**/
247
248tr_peerMgr*
249tr_peerMgrNew( tr_handle * handle )
250{
251    tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
252    m->handle = handle;
253    m->torrents = tr_ptrArrayNew( );
254    return m;
255}
256
257void
258tr_peerMgrFree( tr_peerMgr * manager )
259{
260    while( !tr_ptrArrayEmpty( manager->torrents ) )
261        freeTorrent( manager, (Torrent*)tr_ptrArrayNth( manager->torrents,0) );
262    tr_ptrArrayFree( manager->torrents );
263    tr_free( manager );
264}
265
266/**
267***
268**/
269
270static tr_peer**
271getConnectedPeers( Torrent * t, int * setmeCount )
272{
273    int i, peerCount, connectionCount;
274    tr_peer **peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
275    tr_peer **ret = tr_new( tr_peer*, peerCount );
276
277    for( i=connectionCount=0; i<peerCount; ++i )
278        if( peers[i]->msgs != NULL )
279            ret[connectionCount++] = peers[i];
280
281    *setmeCount = connectionCount;
282    return ret;
283}
284
285static int
286refillPulse( void * vtorrent )
287{
288    uint32_t i;
289    int size;
290    Torrent * t = (Torrent *) vtorrent;
291    tr_peer ** peers = getConnectedPeers( t, &size );
292fprintf( stderr, "in refill pulse for [%s]... sorting blocks by interest...", t->tor->info.name );
293
294    /* sort the blocks by interest */
295    qsort( t->blocks, t->blockCount, sizeof(struct tr_block), compareBlockByInterest );
296fprintf( stderr, " .done.\n" );
297
298    /* walk through all the most interesting blocks */
299    for( i=0; i<t->blockCount; ++i )
300    {
301        const uint32_t b = t->blocks[i].block;
302        const uint32_t index = tr_torBlockPiece( t->tor, b );
303        const uint32_t begin = ( b * t->tor->blockSize )-( index * t->tor->info.pieceSize );
304        const uint32_t length = tr_torBlockCountBytes( t->tor, (int)b );
305        int j;
306
307        if( t->blocks[i].have || t->blocks[i].dnd )
308            continue;
309
310        if( !size ) { /* all peers full */
311            fprintf( stderr, "all peers full...\n" );
312            break;
313        }
314
315        /* find a peer who can ask for this block */
316        for( j=0; j<size; )
317        {
318            const int val = tr_peerMsgsAddRequest( peers[j]->msgs, index, begin, length );
319//fprintf( stderr, " block %"PRIu64", peer %"PRIu64, (uint64_t)i,  (uint64_t)j );
320            if( val == TR_ADDREQ_FULL ) {
321fprintf( stderr, "peer %d of %d is full\n", (int)j, size );
322                peers[j] = peers[--size];
323            }
324            else if( val == TR_ADDREQ_MISSING ) {
325//fprintf( stderr, "peer doesn't have it\n" );
326                ++j;
327            }
328            else if( val == TR_ADDREQ_OK ) {
329fprintf( stderr, "peer %d took the request for block %d\n", j, i );
330                incrementReqCount( &t->blocks[i] );
331                j = size;
332            }
333        }
334    }
335
336    /* put the blocks back by index */
337    qsort( t->blocks, t->blockCount, sizeof(struct tr_block), compareBlockByIndex );
338
339    /* cleanup */
340    tr_free( peers );
341
342    /* let the timer expire */
343    t->refill_tag = NULL;
344    return FALSE;
345}
346
347static void
348ensureRefillTag( Torrent * t )
349{
350    if( t->refill_tag == NULL )
351        t->refill_tag = tr_timerNew( t->manager->handle,
352                                     refillPulse, t, NULL, 5000 );
353}
354
355static void
356msgsCallbackFunc( void * source UNUSED, void * vevent, void * vt )
357{
358    Torrent * t = (Torrent *) vt;
359    const tr_peermsgs_event * e = (const tr_peermsgs_event *) vevent;
360
361    switch( e->eventType )
362    {
363        case TR_PEERMSG_GOT_BITFIELD: {
364            const uint32_t begin = 0;
365            const uint32_t end = begin + t->blockCount;
366            uint32_t i;
367            for( i=begin; i<end; ++i ) {
368                if( !tr_bitfieldHas( e->bitfield, i ) )
369                    continue;
370                assert( t->blocks[i].block == i );
371                incrementScarcity( &t->blocks[i] );
372            }
373            break;
374        }
375
376        case TR_PEERMSG_GOT_HAVE: {
377            const uint32_t begin = tr_torPieceFirstBlock( t->tor, e->pieceIndex );
378            const uint32_t end = begin + tr_torPieceCountBlocks( t->tor, (int)e->pieceIndex );
379            uint32_t i;
380            for( i=begin; i<end; ++i ) {
381                assert( t->blocks[i].block == i );
382                incrementScarcity( &t->blocks[i] );
383            }
384            break;
385        }
386
387        case TR_PEERMSG_GOT_BLOCK: {
388            uint32_t i = e->blockIndex;
389            assert( t->blocks[i].block == i );
390            t->blocks[i].have = 1;
391            break;
392        }
393
394        case TR_PEERMSG_GOT_PEX:
395            /* FIXME */
396            break;
397
398        case TR_PEERMSG_GOT_ERROR:
399            /* FIXME */
400            break;
401
402        case TR_PEERMSG_BLOCKS_RUNNING_LOW:
403            ensureRefillTag( t );
404            break;
405
406        default:
407            assert(0);
408    }
409}
410
411static void
412myHandshakeDoneCB( tr_peerIo * io, int isConnected, void * vmanager )
413{
414    int ok = isConnected;
415    uint16_t port;
416    const struct in_addr * in_addr;
417    tr_peerMgr * manager = (tr_peerMgr*) vmanager;
418    const uint8_t * hash = NULL;
419    Torrent * t;
420
421    assert( io != NULL );
422
423    in_addr = tr_peerIoGetAddress( io, &port );
424
425    if( !tr_peerIoHasTorrentHash( io ) ) /* incoming connection gone wrong? */
426    {
427        tr_peerIoFree( io );
428        --manager->connectionCount;
429        return;
430    }
431
432    hash = tr_peerIoGetTorrentHash( io );
433    t = getExistingTorrent( manager, hash );
434    if( t == NULL )
435    {
436        tr_peerIoFree( io );
437        --manager->connectionCount;
438        return;
439    }
440
441    fprintf( stderr, "peer-mgr: torrent [%s] finished a handshake; isConnected is %d\n", t->tor->info.name, isConnected );
442
443    /* if we couldn't connect or were snubbed,
444     * the peer's probably not worth remembering. */
445    if( !ok ) {
446        tr_peer * peer = getExistingPeer( t, in_addr );
447        fprintf( stderr, "peer-mgr: torrent [%s] got a bad one, and you know what? fuck them.\n", t->tor->info.name );
448        if( peer ) {
449            tr_ptrArrayRemoveSorted( t->peers, peer, peerCompare );
450            freePeer( peer );
451        } else  {
452            tr_peerIoFree( io );
453        }
454        --manager->connectionCount;
455        return;
456    }
457
458#if 0
459    /* ONLY DO THIS TEST FOR INCOMING CONNECTIONS */
460    /* check for duplicates */
461    if( getExistingPeer( t, in_addr ) ) {
462        tr_dbg( "dropping a duplicate connection... dropping." );
463        tr_peerIoFree( io );
464        return;
465    }
466#endif
467
468    if( 1 ) {
469        tr_peer * peer = getPeer( t, in_addr );
470        peer->port = port;
471        peer->io = io;
472        peer->msgs = tr_peerMsgsNew( t->tor, peer );
473        peer->msgsTag = tr_peerMsgsSubscribe( peer->msgs, msgsCallbackFunc, t );
474        chokePulse( t );
475    }
476}
477
478void
479tr_peerMgrAddIncoming( tr_peerMgr      * manager,
480                       struct in_addr  * addr,
481                       int               socket )
482{
483    ++manager->connectionCount;
484
485fprintf( stderr, "peer-mgr: new INCOMING CONNECTION...\n" );
486    tr_handshakeAdd( tr_peerIoNewIncoming( manager->handle, addr, socket ),
487                     HANDSHAKE_ENCRYPTION_PREFERRED,
488                     myHandshakeDoneCB,
489                     manager );
490}
491
492static void
493maybeConnect( tr_peerMgr * manager, Torrent * t, tr_peer * peer )
494{
495    if( tr_peerMgrIsAcceptingConnections( manager ) )
496    {
497        fprintf( stderr, "peer-mgr: torrent [%s] is handshaking with a new peer %08x:%04x\n",
498                 t->tor->info.name,
499                 (uint32_t) peer->in_addr.s_addr, peer->port );
500
501        ++manager->connectionCount;
502
503        peer->io = tr_peerIoNewOutgoing( manager->handle,
504                                         &peer->in_addr,
505                                         peer->port,
506                                         t->hash );
507
508        tr_handshakeAdd( peer->io, HANDSHAKE_ENCRYPTION_PREFERRED,
509                         myHandshakeDoneCB, manager );
510    }
511}
512
513void
514tr_peerMgrAddPex( tr_peerMgr     * manager,
515                  const uint8_t  * torrentHash,
516                  int              from,
517                  const tr_pex   * pex,
518                  int              pexCount )
519{
520    int i;
521    const tr_pex * walk = pex;
522    Torrent * t = getExistingTorrent( manager, torrentHash );
523    for( i=0; i<pexCount; ++i )
524    {
525        tr_peer * peer = getPeer( t, &walk->in_addr );
526        peer->port = walk->port;
527        peer->from = from;
528        maybeConnect( manager, t, peer );
529    }
530}
531
532void
533tr_peerMgrAddPeers( tr_peerMgr    * manager,
534                    const uint8_t * torrentHash,
535                    int             from,
536                    const uint8_t * peerCompact,
537                    int             peerCount )
538{
539    int i;
540    const uint8_t * walk = peerCompact;
541    Torrent * t = getExistingTorrent( manager, torrentHash );
542    for( i=0; i<peerCount; ++i )
543    {
544        tr_peer * peer;
545        struct in_addr addr;
546        uint16_t port;
547        memcpy( &addr, walk, 4 ); walk += 4;
548        memcpy( &port, walk, 2 ); walk += 2;
549        peer = getPeer( t, &addr );
550        peer->port = port;
551        peer->from = from;
552        maybeConnect( manager, t, peer );
553    }
554}
555
556/**
557***
558**/
559
560int
561tr_peerMgrIsAcceptingConnections( const tr_peerMgr * manager )
562{
563    return manager->connectionCount < MAX_CONNECTED_PEERS;
564}
565
566void
567tr_peerMgrSetBlame( tr_peerMgr     * manager UNUSED,
568                    const uint8_t  * torrentHash UNUSED,
569                    int              pieceIndex UNUSED,
570                    int              success UNUSED )
571{
572    assert( 0 );
573}
574
575int
576tr_pexCompare( const void * va, const void * vb )
577{
578    const tr_pex * a = (const tr_pex *) va;
579    const tr_pex * b = (const tr_pex *) vb;
580    int i = memcmp( &a->in_addr, &b->in_addr, sizeof(struct in_addr) );
581    if( i ) return i;
582    if( a->port < b->port ) return -1;
583    if( a->port > b->port ) return 1;
584    return 0;
585}
586
587int tr_pexCompare( const void * a, const void * b );
588
589
590int
591tr_peerMgrGetPeers( tr_peerMgr      * manager,
592                    const uint8_t   * torrentHash,
593                    tr_pex         ** setme_pex )
594{
595    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
596    int i, peerCount;
597    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
598    tr_pex * pex = tr_new( tr_pex, peerCount );
599    tr_pex * walk = pex;
600
601    for( i=0; i<peerCount; ++i, ++walk )
602    {
603        walk->in_addr = peers[i]->in_addr;
604        walk->port = peers[i]->port;
605        walk->flags = '\0'; /* FIXME */
606    }
607
608    assert( ( walk - pex ) == peerCount );
609    qsort( pex, peerCount, sizeof(tr_pex), tr_pexCompare );
610    *setme_pex = pex;
611    return peerCount;
612}
613
614void
615tr_peerMgrStartTorrent( tr_peerMgr     * manager UNUSED,
616                        const uint8_t  * torrentHash UNUSED)
617{
618    //fprintf( stderr, "FIXME\n" );
619}
620
621void
622tr_peerMgrStopTorrent( tr_peerMgr     * manager UNUSED,
623                       const uint8_t  * torrentHash UNUSED )
624{
625    //fprintf( stderr, "FIXME\n" );
626}
627
628void
629tr_peerMgrUpdateCompletion( tr_peerMgr     * manager,
630                            const uint8_t  * torrentHash )
631{
632    uint32_t i;
633    Torrent * t = getExistingTorrent( manager, torrentHash );
634
635    for( i=0; i<t->blockCount; ++i ) {
636        assert( t->blocks[i].block == i );
637        t->blocks[i].have = tr_cpBlockIsComplete( t->tor->completion, i ) ? 1 : 0;
638    }
639}
640
641void
642tr_peerMgrAddTorrent( tr_peerMgr * manager,
643                      tr_torrent * tor )
644{
645    Torrent * t;
646    uint32_t i;
647
648    assert( tor != NULL );
649    assert( getExistingTorrent( manager, tor->info.hash ) == NULL );
650
651    t = tr_new0( Torrent, 1 );
652    t->manager = manager;
653    t->tor = tor;
654    t->peers = tr_ptrArrayNew( );
655    t->choke_tag = tr_timerNew( manager->handle,
656                                chokePulse, t, NULL, 
657                                RECHOKE_PERIOD_SECONDS );
658
659    t->blockCount = tor->blockCount;
660    t->blocks = tr_new( struct tr_block, t->blockCount );
661    for( i=0; i<t->blockCount; ++i ) {
662        const int index = tr_torBlockPiece( tor, i );
663        t->blocks[i].have = tr_cpBlockIsComplete( t->tor->completion, i ) ? 1 : 0;
664if( tr_cpBlockIsComplete( t->tor->completion, i ) ) fprintf( stderr, "have block %d\n", (int)i );
665        t->blocks[i].dnd = tor->info.pieces[index].dnd;
666        t->blocks[i].low_priority = tor->info.pieces[index].priority == TR_PRI_LOW;
667        t->blocks[i].high_priority = tor->info.pieces[index].priority == TR_PRI_HIGH;
668        t->blocks[i].requestCount = 0;
669        t->blocks[i].scarcity = 0;
670        t->blocks[i].block = i;
671    }
672
673    memcpy( t->hash, tor->info.hash, SHA_DIGEST_LENGTH );
674    tr_ptrArrayInsertSorted( manager->torrents, t, torrentCompare );
675}
676
677void
678tr_peerMgrRemoveTorrent( tr_peerMgr     * manager,
679                         const uint8_t  * torrentHash )
680{
681    Torrent * t = getExistingTorrent( manager, torrentHash );
682    if( t != NULL ) {
683        tr_peerMgrStopTorrent( manager, torrentHash );
684        freeTorrent( manager, t );
685    }
686}
687
688void
689tr_peerMgrTorrentAvailability( const tr_peerMgr * manager,
690                               const uint8_t    * torrentHash,
691                               int8_t           * tab,
692                               int                tabCount )
693{
694    int i;
695    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
696    const tr_torrent * tor = t->tor;
697    const float interval = tor->info.pieceCount / (float)tabCount;
698
699    for( i=0; i<tabCount; ++i )
700    {
701        const int piece = i * interval;
702
703        if( tor == NULL )
704            tab[i] = 0;
705        else if( tr_cpPieceIsComplete( tor->completion, piece ) )
706            tab[i] = -1;
707        else {
708            int j, peerCount;
709            const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &peerCount );
710            for( j=0; j<peerCount; ++j )
711                if( tr_bitfieldHas( peers[j]->have, i ) )
712                    ++tab[i];
713        }
714    }
715}
716
717
718void
719tr_peerMgrTorrentStats( const tr_peerMgr * manager,
720                        const uint8_t    * torrentHash,
721                        int              * setmePeersTotal,
722                        int              * setmePeersConnected,
723                        int              * setmePeersSendingToUs,
724                        int              * setmePeersGettingFromUs,
725                        int              * setmePeersFrom )
726{
727    int i, size;
728    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
729    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
730
731    *setmePeersTotal          = size;
732    *setmePeersConnected      = 0;
733    *setmePeersSendingToUs    = 0;
734    *setmePeersGettingFromUs  = 0;
735
736    for( i=0; i<size; ++i )
737    {
738        const tr_peer * peer = peers[i];
739
740        if( peer->io == NULL ) /* not connected */
741            continue;
742
743        ++*setmePeersConnected;
744
745        ++setmePeersFrom[peer->from];
746
747        if( tr_peerIoGetRateToPeer( peer->io ) > 0.01 )
748            ++*setmePeersGettingFromUs;
749
750        if( tr_peerIoGetRateToClient( peer->io ) > 0.01 )
751            ++*setmePeersSendingToUs;
752    }
753}
754
755struct tr_peer_stat *
756tr_peerMgrPeerStats( const tr_peerMgr  * manager,
757                     const uint8_t     * torrentHash,
758                     int               * setmeCount UNUSED )
759{
760    int i, size;
761    const Torrent * t = getExistingTorrent( (tr_peerMgr*)manager, torrentHash );
762    const tr_peer ** peers = (const tr_peer **) tr_ptrArrayPeek( t->peers, &size );
763    tr_peer_stat * ret;
764
765    ret = tr_new0( tr_peer_stat, size );
766
767    for( i=0; i<size; ++i )
768    {
769        const tr_peer * peer = peers[i];
770        const int live = peer->io != NULL;
771        tr_peer_stat * stat = ret + i;
772
773        tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) );
774        stat->port             = peer->port;
775        stat->from             = peer->from;
776        stat->client           = peer->client;
777        stat->progress         = peer->progress;
778        stat->isConnected      = live;
779        stat->uploadToRate     = tr_peerIoGetRateToPeer( peer->io );
780        stat->downloadFromRate = tr_peerIoGetRateToClient( peer->io );
781        stat->isDownloading    = stat->uploadToRate > 0.01;
782        stat->isUploading      = stat->downloadFromRate > 0.01;
783    }
784
785    *setmeCount = size;
786    return ret;
787}
788
789void
790tr_peerMgrDisablePex( tr_peerMgr    * manager,
791                      const uint8_t * torrentHash,
792                      int             disable)
793{
794    Torrent * t = getExistingTorrent( manager, torrentHash );
795    tr_torrent * tor = t->tor;
796
797    if( ( tor->pexDisabled != disable ) && ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
798    {
799        int i, size;
800        tr_peer ** peers = (tr_peer **) tr_ptrArrayPeek( t->peers, &size );
801        for( i=0; i<size; ++i ) {
802            peers[i]->pexEnabled = disable ? 0 : 1;
803            peers[i]->lastPexTime = 0;
804        }
805
806        tor->pexDisabled = disable;
807    }
808}
809
810/**
811***
812**/
813
814typedef struct
815{
816    tr_peer * peer;
817    float rate;
818    int isInterested;
819}
820ChokeData;
821
822static int
823compareChokeByRate( const void * va, const void * vb )
824{
825    const ChokeData * a = ( const ChokeData * ) va;
826    const ChokeData * b = ( const ChokeData * ) vb;
827    if( a->rate > b->rate ) return -1;
828    if( a->rate < b->rate ) return 1;
829    return 0;
830}
831
832static int
833compareChokeByDownloader( const void * va, const void * vb )
834{
835    const ChokeData * a = ( const ChokeData * ) va;
836    const ChokeData * b = ( const ChokeData * ) vb;
837
838    /* primary key: interest */
839    if(  a->isInterested && !b->isInterested ) return -1;
840    if( !a->isInterested &&  b->isInterested ) return 1;
841
842    /* second key: rate */
843    return compareChokeByRate( va, vb );
844}
845
846static int
847chokePulse( void * vtorrent )
848{
849    Torrent * t = (Torrent *) vtorrent;
850    int i, size, unchoked;
851    const int done = tr_cpGetStatus( t->tor->completion ) != TR_CP_INCOMPLETE;
852    float bestDownloaderRate;
853    ChokeData * data;
854    tr_peer ** peers = getConnectedPeers( t, &size );
855
856fprintf( stderr, "rechoking torrent %p, with %d peers\n", t, size );
857
858    if( size < 1 )
859        return TRUE;
860
861    data = tr_new( ChokeData, size );
862    for( i=0; i<size; ++i ) {
863        data[i].peer = peers[i];
864        data[i].isInterested = peers[i]->peerIsInterested;
865        data[i].rate = done ? tr_peerIoGetRateToPeer( peers[i]->io )
866                            : tr_peerIoGetRateToClient( peers[i]->io );
867    }
868
869    /* find the best downloaders and unchoke them */
870    qsort( data, size, sizeof(ChokeData), compareChokeByDownloader );
871    bestDownloaderRate = data[0].rate;
872    for( i=unchoked=0; i<size && unchoked<NUM_DOWNLOADERS_TO_UNCHOKE; ++i ) {
873        tr_peerMsgsSetChoke( data[i].peer->msgs, FALSE );
874        ++unchoked;
875    }
876    memmove( data, data+i, sizeof(ChokeData)*(size-i) );
877    size -= i;
878
879    /* of those remaining, unchoke those that are faster than the downloaders */
880    qsort( data, size, sizeof(ChokeData), compareChokeByRate );
881    for( i=0; i<size && data[i].rate >= bestDownloaderRate; ++i )
882        tr_peerMsgsSetChoke( data[i].peer->msgs, FALSE );
883    memmove( data, data+i, sizeof(ChokeData)*(size-i) );
884    size -= i;
885
886    /* of those remaining, optimistically unchoke one; choke the rest */
887    if( size > 0 ) {
888        const int optimistic = tr_rand( size );
889        for( i=0; i<size; ++i )
890            tr_peerMsgsSetChoke( data[i].peer->msgs, i!=optimistic );
891    }
892
893    /* cleanup */
894    tr_free( data );
895    tr_free( peers );
896    return TRUE;
897}
Note: See TracBrowser for help on using the repository browser.