source: trunk/libtransmission/torrent.c @ 2177

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

adding experimental implementation of Tamilmani's `Swift' tit-for-tat algorithm for testing. To tweak or disable, change the values around line 50 of libtransmission/peer.c

  • Property svn:keywords set to Date Rev Author Id
File size: 31.4 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2177 2007-06-21 14:47:26Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26#include "shared.h"
27#define INTERVAL_MSEC 100
28
29/***********************************************************************
30 * Local prototypes
31 **********************************************************************/
32static tr_torrent_t * torrentRealInit( tr_handle_t *, tr_torrent_t * tor,
33                                       uint8_t *, int flags, int * error );
34static void torrentReallyStop( tr_torrent_t * );
35
36static void ioInitAdd ( tr_torrent_t * );
37static int ioInitRemove ( tr_torrent_t * );
38static void downloadLoop( void * );
39
40void tr_setUseCustomUpload( tr_torrent_t * tor, int limit )
41{
42    tor->customUploadLimit = limit;
43}
44
45void tr_setUseCustomDownload( tr_torrent_t * tor, int limit )
46{
47    tor->customDownloadLimit = limit;
48}
49
50void tr_setUploadLimit( tr_torrent_t * tor, int limit )
51{
52    tr_rcSetLimit( tor->upload, limit );
53}
54
55void tr_setDownloadLimit( tr_torrent_t * tor, int limit )
56{
57    tr_rcSetLimit( tor->download, limit );
58}
59
60tr_torrent_t *
61tr_torrentInit( tr_handle_t * h, const char * path,
62                uint8_t * hash, int flags, int * error )
63{
64    tr_torrent_t * tor;
65
66    tor  = calloc( 1, sizeof *tor );
67    if( NULL == tor )
68    {
69        *error = TR_EOTHER;
70        return NULL;
71    }
72
73    /* Parse torrent file */
74    if( tr_metainfoParseFile( &tor->info, h->tag, path,
75                              TR_FLAG_SAVE & flags ) )
76    {
77        *error = TR_EINVALID;
78        free( tor );
79        return NULL;
80    }
81
82    return torrentRealInit( h, tor, hash, flags, error );
83}
84
85tr_torrent_t *
86tr_torrentInitData( tr_handle_t * h, uint8_t * data, size_t size,
87                    uint8_t * hash, int flags, int * error )
88{
89    tr_torrent_t * tor;
90
91    tor  = calloc( 1, sizeof *tor );
92    if( NULL == tor )
93    {
94        *error = TR_EOTHER;
95        return NULL;
96    }
97
98    /* Parse torrent file */
99    if( tr_metainfoParseData( &tor->info, h->tag, data, size,
100                              TR_FLAG_SAVE & flags ) )
101    {
102        *error = TR_EINVALID;
103        free( tor );
104        return NULL;
105    }
106
107    return torrentRealInit( h, tor, hash, flags, error );
108}
109
110tr_torrent_t *
111tr_torrentInitSaved( tr_handle_t * h, const char * hashStr,
112                     int flags, int * error )
113{
114    tr_torrent_t * tor;
115
116    tor  = calloc( 1, sizeof *tor );
117    if( NULL == tor )
118    {
119        *error = TR_EOTHER;
120        return NULL;
121    }
122
123    /* Parse torrent file */
124    if( tr_metainfoParseHash( &tor->info, h->tag, hashStr ) )
125    {
126        *error = TR_EINVALID;
127        free( tor );
128        return NULL;
129    }
130
131    return torrentRealInit( h, tor, NULL, ( TR_FLAG_SAVE | flags ), error );
132}
133
134/***********************************************************************
135 * tr_torrentInit
136 ***********************************************************************
137 * Allocates a tr_torrent_t structure, then relies on tr_metainfoParse
138 * to fill it.
139 **********************************************************************/
140static tr_torrent_t * torrentRealInit( tr_handle_t * h, tr_torrent_t * tor,
141                                       uint8_t * hash, int flags, int * error )
142{
143    tr_torrent_t  * tor_tmp;
144    tr_info_t     * inf;
145    int             i;
146   
147    inf         = &tor->info;
148    inf->flags |= flags;
149
150    tr_sharedLock( h->shared );
151
152    /* Make sure this torrent is not already open */
153    for( tor_tmp = h->torrentList; tor_tmp; tor_tmp = tor_tmp->next )
154    {
155        if( !memcmp( tor->info.hash, tor_tmp->info.hash,
156                     SHA_DIGEST_LENGTH ) )
157        {
158            if( NULL != hash )
159            {
160                memcpy( hash, tor->info.hash, SHA_DIGEST_LENGTH );
161            }
162            *error = TR_EDUPLICATE;
163            tr_metainfoFree( &tor->info );
164            free( tor );
165            tr_sharedUnlock( h->shared );
166            return NULL;
167        }
168    }
169
170    tor->handle   = h;
171    tor->status   = TR_STATUS_PAUSE;
172    tor->id       = h->id;
173    tor->key      = h->key;
174    tor->azId     = h->azId;
175    tor->hasChangedState = -1;
176
177    /* Escaped info hash for HTTP queries */
178    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
179    {
180        snprintf( &tor->escapedHashString[3*i],
181                  sizeof( tor->escapedHashString ) - 3 * i,
182                  "%%%02x", inf->hash[i] );
183    }
184
185    tor->pexDisabled = 0;
186
187    /* Block size: usually 16 ko, or less if we have to */
188    tor->blockSize  = MIN( inf->pieceSize, 1 << 14 );
189    tor->blockCount = ( inf->totalSize + tor->blockSize - 1 ) /
190                        tor->blockSize;
191    tor->completion = tr_cpInit( tor );
192
193    tor->thread = THREAD_EMPTY;
194    tr_lockInit( &tor->lock );
195    tr_condInit( &tor->cond );
196
197    tor->upload         = tr_rcInit();
198    tor->download       = tr_rcInit();
199    tor->swarmspeed     = tr_rcInit();
200 
201    /* We have a new torrent */
202    tor->publicPort = tr_sharedGetPublicPort( h->shared );
203    tor->prev       = NULL;
204    tor->next       = h->torrentList;
205    if( tor->next )
206    {
207        tor->next->prev = tor;
208    }
209    h->torrentList = tor;
210    (h->torrentCount)++;
211
212    tr_sharedUnlock( h->shared );
213
214    if( !h->isPortSet )
215    {
216        tr_setBindPort( h, TR_DEFAULT_PORT );
217    }
218
219    tr_torrentInitFilePieces( tor );
220
221    return tor;
222}
223
224tr_info_t * tr_torrentInfo( tr_torrent_t * tor )
225{
226    return &tor->info;
227}
228
229/***********************************************************************
230 * tr_torrentScrape     
231 **********************************************************************/
232int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
233{
234    return tr_trackerScrape( tor, s, l, d );
235}
236
237void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
238{
239    tor->destination = strdup( path );
240    if ( !tor->ioLoaded )
241    {
242        tr_ioLoadResume( tor );
243    }
244}
245
246char * tr_torrentGetFolder( tr_torrent_t * tor )
247{
248    return tor->destination;
249}
250
251int tr_torrentDuplicateDownload( tr_torrent_t * tor )
252{
253    tr_torrent_t * current;
254   
255    /* Check if a torrent with the same name and destination is already active */
256    for( current = tor->handle->torrentList; current; current = current->next )
257    {
258        if( current != tor && current->status != TR_STATUS_PAUSE
259            && !strcmp( tor->destination, current->destination )
260            && !strcmp( tor->info.name, current->info.name ) )
261        {
262            return 1;
263        }
264    }
265    return 0;
266}
267
268void tr_torrentStart( tr_torrent_t * tor )
269{
270    /* Join the thread first */
271    torrentReallyStop( tor );
272   
273    /* Don't start if a torrent with the same name and destination is already active */
274    if( tr_torrentDuplicateDownload( tor ) )
275    {
276        tor->error = TR_ERROR_IO_DUP_DOWNLOAD;
277        snprintf( tor->errorString, sizeof( tor->errorString ),
278                    "%s", tr_errorString( TR_ERROR_IO_DUP_DOWNLOAD ) );
279        return;
280    }
281
282    tr_lockLock( &tor->lock );
283
284    tor->downloadedPrev += tor->downloadedCur;
285    tor->downloadedCur   = 0;
286    tor->uploadedPrev   += tor->uploadedCur;
287    tor->uploadedCur     = 0;
288
289    tor->status  = TR_STATUS_CHECK_WAIT;
290    tor->error   = TR_OK;
291    tor->tracker = tr_trackerInit( tor );
292
293    tor->startDate = tr_date();
294    tor->die = 0;
295    tor->thread = THREAD_EMPTY;
296
297    tr_lockUnlock( &tor->lock );
298
299    ioInitAdd ( tor );
300}
301
302static void torrentStop( tr_torrent_t * tor )
303{
304    tr_trackerStopped( tor->tracker );
305    tr_rcReset( tor->download );
306    tr_rcReset( tor->upload );
307    tr_rcReset( tor->swarmspeed );
308    tor->status = TR_STATUS_STOPPING;
309    tor->stopDate = tr_date();
310}
311
312void tr_torrentStop( tr_torrent_t * tor )
313{
314    tr_lockLock( &tor->lock );
315    torrentStop( tor );
316
317    /* Don't return until the files are closed, so the UI can trash
318     * them if requested */
319    if ( ioInitRemove( tor ) ) /* torrent never got started */
320        tor->status = TR_STATUS_STOPPED;
321    else
322        tr_condWait( &tor->cond, &tor->lock );
323
324    tr_lockUnlock( &tor->lock );
325}
326
327/***********************************************************************
328 * torrentReallyStop
329 ***********************************************************************
330 * Joins the download thread and frees/closes everything related to it.
331 **********************************************************************/
332static void torrentReallyStop( tr_torrent_t * tor )
333{
334    int i;
335
336    tor->die = 1;
337    tr_threadJoin( &tor->thread );
338
339    if( tor->tracker )
340    {
341       tr_trackerClose( tor->tracker );
342       tor->tracker = NULL;
343    }
344
345    tr_lockLock( &tor->lock );
346    for( i = 0; i < tor->peerCount; i++ )
347    {
348        tr_peerDestroy( tor->peers[i] );
349    }
350    tor->peerCount = 0;
351    tr_lockUnlock( &tor->lock );
352}
353
354void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
355{
356    int ii;
357
358    if( TR_FLAG_PRIVATE & tor->info.flags )
359    {
360        return;
361    }
362
363    tr_lockLock( &tor->lock );
364
365    if( tor->pexDisabled == disable )
366    {
367        tr_lockUnlock( &tor->lock );
368        return;
369    }
370
371    tor->pexDisabled = disable;
372    for( ii = 0; ii < tor->peerCount; ii++ )
373    {
374        tr_peerSetPrivate( tor->peers[ii], disable );
375    }
376
377    tr_lockUnlock( &tor->lock );
378}
379
380static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
381{
382    if( tor->hasChangedState == status )
383    {
384        tor->hasChangedState = -1;
385        return 1;
386    }
387    return 0;
388}
389
390int tr_getIncomplete( tr_torrent_t * tor )
391{
392    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
393}
394int tr_getDone( tr_torrent_t * tor )
395{
396    return tr_didStateChangeTo( tor, TR_CP_DONE );
397}
398int tr_getComplete( tr_torrent_t * tor )
399{
400    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
401}
402
403void tr_manualUpdate( tr_torrent_t * tor )
404{
405    int peerCount, new;
406    uint8_t * peerCompact;
407
408    if( !( tor->status & TR_STATUS_ACTIVE ) )
409        return;
410   
411    tr_lockLock( &tor->lock );
412    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
413    new = 0;
414    if( peerCount > 0 )
415    {
416        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
417                              peerCompact, peerCount );
418        free( peerCompact );
419    }
420    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
421    tr_lockUnlock( &tor->lock );
422}
423
424tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
425{
426    tr_stat_t * s;
427    tr_peer_t * peer;
428    tr_info_t * inf = &tor->info;
429    tr_tracker_t * tc;
430    int i;
431
432    tor->statCur = ( tor->statCur + 1 ) % 2;
433    s = &tor->stats[tor->statCur];
434
435    if( ( tor->status & TR_STATUS_STOPPED ) ||
436        ( ( tor->status & TR_STATUS_STOPPING ) &&
437          tr_date() > tor->stopDate + 60000 ) )
438    {
439        torrentReallyStop( tor );
440        tor->status = TR_STATUS_PAUSE;
441    }
442
443    tr_lockLock( &tor->lock );
444
445    s->status = tor->status;
446    s->error  = tor->error;
447    memcpy( s->errorString, tor->errorString,
448            sizeof( s->errorString ) );
449
450    tc = tor->tracker;
451    s->cannotConnect = tr_trackerCannotConnect( tc );
452    s->tracker = ( tc ? tr_trackerGet( tc ) : &inf->trackerList[0].list[0] );
453
454    s->peersTotal       = 0;
455    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
456    s->peersUploading   = 0;
457    s->peersDownloading = 0;
458   
459    for( i = 0; i < tor->peerCount; i++ )
460    {
461        peer = tor->peers[i];
462   
463        if( tr_peerIsConnected( peer ) )
464        {
465            (s->peersTotal)++;
466            (s->peersFrom[ tr_peerIsFrom( peer ) ])++;
467            if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
468            {
469                (s->peersUploading)++;
470            }
471            if( !tr_peerAmChoking( peer ) )
472            {
473                (s->peersDownloading)++;
474            }
475        }
476    }
477
478    s->percentDone = tr_cpPercentDone( tor->completion );
479    s->percentComplete = tr_cpPercentComplete( tor->completion );
480    s->cpStatus = tr_cpGetStatus( tor->completion );
481    s->left     = tr_cpLeftUntilDone( tor->completion );
482    if( tor->status & TR_STATUS_DOWNLOAD )
483    {
484        s->rateDownload = tr_rcRate( tor->download );
485    }
486    else
487    {
488        /* tr_rcRate() doesn't make the difference between 'piece'
489           messages and other messages, which causes a non-zero
490           download rate even tough we are not downloading. So we
491           force it to zero not to confuse the user. */
492        s->rateDownload = 0.0;
493    }
494    s->rateUpload = tr_rcRate( tor->upload );
495   
496    s->seeders  = tr_trackerSeeders( tc );
497    s->leechers = tr_trackerLeechers( tc );
498    s->completedFromTracker = tr_trackerDownloaded( tc );
499
500    s->swarmspeed = tr_rcRate( tor->swarmspeed );
501   
502    s->startDate = tor->startDate;
503    s->activityDate = tor->activityDate;
504
505    if( s->rateDownload < 0.1 )
506    {
507        s->eta = -1;
508    }
509    else
510    {
511        s->eta = (float) s->left / s->rateDownload / 1024.0;
512    }
513
514    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
515    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
516    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
517   
518    if( s->downloaded == 0 && s->percentDone == 0.0 )
519    {
520        s->ratio = TR_RATIO_NA;
521    }
522    else
523    {
524        s->ratio = (float)s->uploaded
525                 / (float)MAX(s->downloaded, s->downloadedValid);
526    }
527   
528    tr_lockUnlock( &tor->lock );
529
530    return s;
531}
532
533tr_peer_stat_t * tr_torrentPeers( tr_torrent_t * tor, int * peerCount )
534{
535    tr_peer_stat_t * peers;
536
537    tr_lockLock( &tor->lock );
538
539    *peerCount = tor->peerCount;
540   
541    peers = (tr_peer_stat_t *) calloc( tor->peerCount, sizeof( tr_peer_stat_t ) );
542    if (peers != NULL)
543    {
544        tr_peer_t * peer;
545        struct in_addr * addr;
546        int i;
547        for( i = 0; i < tor->peerCount; i++ )
548        {
549            peer = tor->peers[i];
550           
551            addr = tr_peerAddress( peer );
552            if( NULL != addr )
553            {
554                tr_netNtop( addr, peers[i].addr,
555                           sizeof( peers[i].addr ) );
556            }
557           
558            peers[i].client        = tr_peerClient( peer );
559            peers[i].isConnected   = tr_peerIsConnected( peer );
560            peers[i].from          = tr_peerIsFrom( peer );
561            peers[i].progress      = tr_peerProgress( peer );
562            peers[i].port          = tr_peerPort( peer );
563           
564            if( ( peers[i].isDownloading = !tr_peerAmChoking( peer ) ) )
565            {
566                peers[i].uploadToRate = tr_peerUploadRate( peer );
567            }
568            if( ( peers[i].isUploading = ( tr_peerAmInterested( peer ) &&
569                                           !tr_peerIsChoking( peer ) ) ) )
570            {
571                peers[i].downloadFromRate = tr_peerDownloadRate( peer );
572            }
573        }
574    }
575   
576    tr_lockUnlock( &tor->lock );
577   
578    return peers;
579}
580
581void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
582{
583    if (peers == NULL)
584        return;
585
586    free( peers );
587}
588
589void tr_torrentAvailability( tr_torrent_t * tor, int8_t * tab, int size )
590{
591    int i, j, piece;
592    float interval;
593
594    tr_lockLock( &tor->lock );
595    interval = (float)tor->info.pieceCount / (float)size;
596    for( i = 0; i < size; i++ )
597    {
598        piece = i * interval;
599
600        if( tr_cpPieceIsComplete( tor->completion, piece ) )
601        {
602            tab[i] = -1;
603            continue;
604        }
605
606        tab[i] = 0;
607        for( j = 0; j < tor->peerCount; j++ )
608        {
609            if( tr_peerHasPiece( tor->peers[j], piece ) )
610            {
611                (tab[i])++;
612            }
613        }
614    }
615    tr_lockUnlock( &tor->lock );
616}
617
618uint64_t
619tr_torrentFileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
620{
621    const tr_file_t * file     =  &tor->info.files[fileIndex];
622    const uint64_t firstBlock       =  file->offset / tor->blockSize;
623    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
624    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
625    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
626    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
627    uint64_t haveBytes = 0;
628
629    assert( tor != NULL );
630    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
631    assert( file->offset + file->length <= tor->info.totalSize );
632    assert( (int)firstBlock < tor->blockCount );
633    assert( (int)lastBlock < tor->blockCount );
634    assert( firstBlock <= lastBlock );
635    assert( tr_blockPiece( firstBlock ) == file->firstPiece );
636    assert( tr_blockPiece( lastBlock ) == file->lastPiece );
637
638    if( firstBlock == lastBlock )
639    {
640        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
641            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
642    }
643    else
644    {
645        uint64_t i;
646
647        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
648            haveBytes += tor->blockSize - firstBlockOffset;
649
650        for( i=firstBlock+1; i<lastBlock; ++i )
651            if( tr_cpBlockIsComplete( tor->completion, i ) )
652               haveBytes += tor->blockSize;
653
654        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
655            haveBytes += lastBlockOffset + 1;
656    }
657
658    return haveBytes;
659}
660
661float
662tr_torrentFileCompletion ( const tr_torrent_t * tor, int fileIndex )
663{
664    const uint64_t c = tr_torrentFileBytesCompleted ( tor, fileIndex );
665    uint64_t length = tor->info.files[fileIndex].length;
666   
667    if( !length )
668        return 1.0;
669    return (double)c / length;
670}
671
672float*
673tr_torrentCompletion( tr_torrent_t * tor )
674{
675    int i;
676    float * f;
677
678    tr_lockLock( &tor->lock );
679    f = calloc ( tor->info.fileCount, sizeof( float ) );
680    for( i=0; i<tor->info.fileCount; ++i )
681       f[i] = tr_torrentFileCompletion ( tor, i );
682    tr_lockUnlock( &tor->lock );
683
684    return f;
685}
686
687void tr_torrentAmountFinished( tr_torrent_t * tor, float * tab, int size )
688{
689    int i, piece;
690    float interval;
691
692    tr_lockLock( &tor->lock );
693    interval = (float)tor->info.pieceCount / (float)size;
694    for( i = 0; i < size; i++ )
695    {
696        piece = i * interval;
697        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
698    }
699    tr_lockUnlock( &tor->lock );
700}
701
702void tr_torrentRemoveSaved( tr_torrent_t * tor )
703{
704    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
705}
706
707void tr_torrentRemoveFastResume( tr_torrent_t * tor )
708{
709    tr_ioRemoveResume( tor );
710}
711
712/***********************************************************************
713 * tr_torrentClose
714 ***********************************************************************
715 * Frees memory allocated by tr_torrentInit.
716 **********************************************************************/
717void tr_torrentClose( tr_torrent_t * tor )
718{
719    tr_handle_t * h = tor->handle;
720    tr_info_t * inf = &tor->info;
721
722    /* Join the thread first */
723    torrentReallyStop( tor );
724
725    tr_sharedLock( h->shared );
726
727    h->torrentCount--;
728
729    tr_lockClose( &tor->lock );
730    tr_condClose( &tor->cond );
731    tr_cpClose( tor->completion );
732
733    tr_rcClose( tor->upload );
734    tr_rcClose( tor->download );
735    tr_rcClose( tor->swarmspeed );
736
737    if( tor->destination )
738    {
739        free( tor->destination );
740    }
741
742    tr_metainfoFree( inf );
743
744    if( tor->prev )
745    {
746        tor->prev->next = tor->next;
747    }
748    else
749    {
750        h->torrentList = tor->next;
751    }
752    if( tor->next )
753    {
754        tor->next->prev = tor->prev;
755    }
756    free( tor );
757
758    tr_sharedUnlock( h->shared );
759}
760
761int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
762{
763    int i;
764    tr_peer_t * otherPeer;
765
766    if( tor->peerCount >= TR_MAX_PEER_COUNT )
767    {
768        tr_peerDestroy(  peer );
769        return 0;
770    }
771
772    /* Don't accept two connections from the same IP */
773    for( i = 0; i < tor->peerCount; i++ )
774    {
775        otherPeer = tor->peers[i];
776        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
777        {
778            tr_peerDestroy(  peer );
779            return 0;
780        }
781    }
782
783    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
784                       tor->pexDisabled );
785    tr_peerSetTorrent( peer, tor );
786    tor->peers[tor->peerCount++] = peer;
787
788    return 1;
789}
790
791int tr_torrentAddCompact( tr_torrent_t * tor, int from,
792                           uint8_t * buf, int count )
793{
794    struct in_addr addr;
795    in_port_t port;
796    int i, added;
797    tr_peer_t * peer;
798
799    added = 0;
800    for( i = 0; i < count; i++ )
801    {
802        memcpy( &addr, buf, 4 ); buf += 4;
803        memcpy( &port, buf, 2 ); buf += 2;
804
805        peer = tr_peerInit( addr, port, -1, from );
806        added += tr_torrentAttachPeer( tor, peer );
807    }
808
809    return added;
810}
811
812/***********************************************************************
813 * Push a torrent's call to tr_ioInit through a queue in a worker
814 * thread so that only one torrent can be in checkFiles() at a time.
815 **********************************************************************/
816
817struct tr_io_init_list_t
818{
819    tr_torrent_t              * tor;
820    struct tr_io_init_list_t  * next;
821};
822
823static struct tr_io_init_list_t * ioInitQueue = NULL;
824
825static int ioInitWorkerRunning = 0;
826
827static tr_thread_t ioInitThread;
828
829static tr_lock_t* getIOLock( tr_handle_t * h )
830{
831    static tr_lock_t * lock = NULL;
832
833    tr_sharedLock( h->shared );
834    if( lock == NULL )
835    {
836        lock = calloc( 1, sizeof( tr_lock_t ) );
837        tr_lockInit( lock );
838    }
839    tr_sharedUnlock( h->shared );
840
841    return lock;
842}
843
844static void ioInitWorker( void * user_data )
845{
846    tr_handle_t * h = (tr_handle_t*) user_data;
847
848    for (;;)
849    {
850        char name[32];
851
852        /* find the next torrent to process */
853        tr_torrent_t * tor = NULL;
854        tr_lock_t * lock = getIOLock( h );
855        tr_lockLock( lock );
856        if( ioInitQueue != NULL )
857        {
858            struct tr_io_init_list_t * node = ioInitQueue;
859            ioInitQueue = node->next;
860            tor = node->tor;
861            free( node );
862        }
863        tr_lockUnlock( lock );
864
865        /* if no torrents, this worker thread is done */
866        if( tor == NULL )
867        {
868          break;
869        }
870
871        /* check this torrent's files */
872        tor->status = TR_STATUS_CHECK;
873       
874        tr_dbg( "torrent %s checking files", tor->info.name );
875        tr_lockLock( &tor->lock );
876        tr_cpReset( tor->completion );
877        tor->io = tr_ioInit( tor );
878        tr_lockUnlock( &tor->lock );
879
880        snprintf( name, sizeof( name ), "torrent %p", tor );
881        tr_threadCreate( &tor->thread, downloadLoop, tor, name );
882    }
883
884    ioInitWorkerRunning = 0;
885}
886
887/* add tor to the queue of torrents waiting for an tr_ioInit */
888static void ioInitAdd( tr_torrent_t * tor )
889{
890    tr_lock_t * lock = getIOLock( tor->handle );
891    struct tr_io_init_list_t * node;
892    tr_lockLock( lock );
893
894    /* enqueue this torrent to have its io initialized */
895    node = malloc( sizeof( struct tr_io_init_list_t ) );
896    node->tor = tor;
897    node->next = NULL;
898    if( ioInitQueue == NULL )
899    {
900        ioInitQueue = node;
901    }
902    else
903    {
904        struct tr_io_init_list_t * l = ioInitQueue;
905        while( l->next != NULL )
906        {
907            l = l->next;
908        }
909        l->next = node;
910    }
911
912    /* ensure there's a worker thread to process the queue */
913    if( !ioInitWorkerRunning )
914    {
915        ioInitWorkerRunning = 1;
916        tr_threadCreate( &ioInitThread, ioInitWorker, tor->handle, "ioInit" );
917    }
918   
919    tr_lockUnlock( lock );
920}
921
922/* remove tor from the queue of torrents waiting for an tr_ioInit.
923   return nonzero if tor was found and removed */
924static int ioInitRemove( tr_torrent_t * tor )
925{
926    tr_lock_t * lock = getIOLock( tor->handle );
927    struct tr_io_init_list_t *node, *prev;
928    tr_lockLock( lock );
929
930    /* find tor's node */
931    for( prev = NULL, node = ioInitQueue;
932         node != NULL && node->tor != tor;
933         prev=node, node=node->next );
934
935    if( node != NULL )
936    {
937        if( prev == NULL )
938        {
939            ioInitQueue = node->next;
940        }
941        else
942        {
943            prev->next = node->next;
944        }
945    }
946
947    tr_lockUnlock( lock );
948    return node != NULL;
949}
950
951/***********************************************************************
952 * downloadLoop
953 **********************************************************************/
954static void downloadLoop( void * _tor )
955{
956    tr_torrent_t * tor = _tor;
957    int            i, ret;
958    int            peerCount, used;
959    cp_status_t    cpState, cpPrevState;
960    uint8_t      * peerCompact;
961    tr_peer_t    * peer;
962
963    tr_lockLock( &tor->lock );
964
965    cpState = cpPrevState = tr_cpGetStatus( tor->completion );
966    switch( cpState ) {
967        case TR_CP_COMPLETE:   tor->status = TR_STATUS_SEED; break;
968        case TR_CP_DONE:       tor->status = TR_STATUS_DONE; break;
969        case TR_CP_INCOMPLETE: tor->status = TR_STATUS_DOWNLOAD; break;
970    }
971
972    while( !tor->die )
973    {
974        tr_lockUnlock( &tor->lock );
975        tr_wait( INTERVAL_MSEC );
976        tr_lockLock( &tor->lock );
977
978        cpState = tr_cpGetStatus( tor->completion );
979
980        if( cpState != cpPrevState )
981        {
982            switch( cpState ) {
983                case TR_CP_COMPLETE:   tor->status = TR_STATUS_SEED; break;
984                case TR_CP_DONE:       tor->status = TR_STATUS_DONE; break;
985                case TR_CP_INCOMPLETE: tor->status = TR_STATUS_DOWNLOAD; break;
986            }
987
988            tor->hasChangedState = cpState;
989
990            if( cpState == TR_CP_COMPLETE )
991                tr_trackerCompleted( tor->tracker );
992
993            tr_ioSync( tor->io );
994
995            cpPrevState = cpState;
996        }
997
998        /* Try to get new peers or to send a message to the tracker */
999        tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1000        if( peerCount > 0 )
1001        {
1002            used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
1003                                         peerCompact, peerCount );
1004            free( peerCompact );
1005            tr_dbg( "got %i peers from announce, used %i", peerCount, used );
1006        }
1007        if( tor->status & TR_STATUS_STOPPED )
1008        {
1009            break;
1010        }
1011
1012        /* Stopping: make sure all files are closed and stop talking
1013           to peers */
1014        if( tor->status & TR_STATUS_STOPPING )
1015        {
1016            if( tor->io )
1017            {
1018                tr_ioClose( tor->io ); tor->io = NULL;
1019                tr_condSignal( &tor->cond );
1020            }
1021            continue;
1022        }
1023
1024        /* Shuffle peers */
1025        if( tor->peerCount > 1 )
1026        {
1027            peer = tor->peers[0];
1028            memmove( &tor->peers[0], &tor->peers[1],
1029                    ( tor->peerCount - 1 ) * sizeof( void * ) );
1030            tor->peers[tor->peerCount - 1] = peer;
1031        }
1032
1033        /* Receive/send messages */
1034        for( i = 0; i < tor->peerCount; )
1035        {
1036            peer = tor->peers[i];
1037
1038            ret = tr_peerPulse( peer );
1039            if( ret & TR_ERROR_IO_MASK )
1040            {
1041                tr_err( "Fatal error, stopping download (%d)", ret );
1042                torrentStop( tor );
1043                tor->error = ret;
1044                snprintf( tor->errorString, sizeof( tor->errorString ),
1045                          "%s", tr_errorString( ret ) );
1046                break;
1047            }
1048            if( ret )
1049            {
1050                tr_peerDestroy( peer );
1051                tor->peerCount--;
1052                memmove( &tor->peers[i], &tor->peers[i+1],
1053                         ( tor->peerCount - i ) * sizeof( void * ) );
1054                continue;
1055            }
1056            i++;
1057        }
1058    }
1059
1060    tr_lockUnlock( &tor->lock );
1061
1062    if( tor->io )
1063    {
1064        tr_ioClose( tor->io ); tor->io = NULL;
1065        tr_condSignal( &tor->cond );
1066    }
1067
1068    tor->status = TR_STATUS_STOPPED;
1069}
1070
1071/***
1072****
1073****  File prioritization
1074****
1075***/
1076
1077static int
1078getBytePiece( const tr_info_t * info, uint64_t byteOffset )
1079{
1080    assert( info != NULL );
1081    assert( info->pieceSize != 0 );
1082
1083    return byteOffset / info->pieceSize;
1084}
1085
1086static void
1087initFilePieces ( tr_info_t * info, int fileIndex )
1088{
1089    tr_file_t * file = &info->files[fileIndex];
1090    uint64_t firstByte, lastByte;
1091
1092    assert( info != NULL );
1093    assert( 0<=fileIndex && fileIndex<info->fileCount );
1094
1095    file = &info->files[fileIndex];
1096    firstByte = file->offset;
1097    lastByte = firstByte + (file->length ? file->length-1 : 0);
1098    file->firstPiece = getBytePiece( info, firstByte );
1099    file->lastPiece = getBytePiece( info, lastByte );
1100    tr_dbg( "file #%d is in pieces [%d...%d] (%s)", fileIndex, file->firstPiece, file->lastPiece, file->name );
1101}
1102
1103static tr_priority_t
1104calculatePiecePriority ( const tr_torrent_t * tor,
1105                         int                  piece )
1106{
1107    int i;
1108    tr_priority_t priority = TR_PRI_DND;
1109
1110    for( i=0; i<tor->info.fileCount; ++i )
1111    {
1112        const tr_file_t * file = &tor->info.files[i];
1113        if ( file->firstPiece <= piece
1114          && file->lastPiece  >= piece
1115          && file->priority   >  priority)
1116              priority = file->priority;
1117    }
1118
1119    return priority;
1120}
1121
1122void
1123tr_torrentInitFilePieces( tr_torrent_t * tor )
1124{
1125    int i;
1126    uint64_t offset = 0;
1127
1128    assert( tor != NULL );
1129
1130    for( i=0; i<tor->info.fileCount; ++i ) {
1131      tor->info.files[i].offset = offset;
1132      offset += tor->info.files[i].length;
1133      initFilePieces( &tor->info, i );
1134    }
1135
1136    for( i=0; i<tor->info.pieceCount; ++i )
1137      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1138}
1139
1140void
1141tr_torrentSetFilePriority( tr_torrent_t   * tor,
1142                           int              fileIndex,
1143                           tr_priority_t    priority )
1144{
1145    int i;
1146    tr_file_t * file;
1147
1148    assert( tor != NULL );
1149    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1150    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL
1151         || priority==TR_PRI_HIGH || priority==TR_PRI_DND );
1152
1153    file = &tor->info.files[fileIndex];
1154    file->priority = priority;
1155    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1156      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1157
1158    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1159             fileIndex, file->firstPiece, file->lastPiece,
1160             priority, tor->info.files[fileIndex].name );
1161}
1162
1163tr_priority_t
1164tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1165{
1166    assert( tor != NULL );
1167    assert( 0<=file && file<tor->info.fileCount );
1168
1169    return tor->info.files[file].priority;
1170}
1171
1172
1173void
1174tr_torrentSetFilePriorities( tr_torrent_t         * tor,
1175                             const tr_priority_t  * filePriorities )
1176{
1177    int i;
1178    for( i=0; i<tor->info.pieceCount; ++i )
1179      tr_torrentSetFilePriority( tor, i, filePriorities[i] );
1180}
1181
1182tr_priority_t*
1183tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1184{
1185    int i;
1186    tr_priority_t * p = malloc( tor->info.fileCount * sizeof(tr_priority_t) );
1187    for( i=0; i<tor->info.fileCount; ++i )
1188        p[i] = tor->info.files[i].priority;
1189    return p;
1190}
Note: See TracBrowser for help on using the repository browser.