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

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

lots of thread work

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