diff --git a/libtransmission/Jamfile b/libtransmission/Jamfile index bed221edd..a694cbff9 100644 --- a/libtransmission/Jamfile +++ b/libtransmission/Jamfile @@ -2,8 +2,8 @@ SubDir TOP libtransmission ; LIBTRANSMISSION_SRC = transmission.c bencode.c net.c tracker.c peer.c inout.c - metainfo.c sha1.c utils.c upload.c fdlimit.c clients.c - completion.c platform.c ; + metainfo.c sha1.c utils.c fdlimit.c clients.c completion.c + platform.c ratecontrol.c ; Library libtransmission.a : $(LIBTRANSMISSION_SRC) ; ObjectDefines $(LIBTRANSMISSION_SRC) : __TRANSMISSION__ ; diff --git a/libtransmission/internal.h b/libtransmission/internal.h index 1ac0510ce..47102de36 100644 --- a/libtransmission/internal.h +++ b/libtransmission/internal.h @@ -93,15 +93,18 @@ typedef struct tr_completion_s tr_completion_t; #include "peer.h" #include "net.h" #include "inout.h" -#include "upload.h" +#include "ratecontrol.h" #include "clients.h" struct tr_torrent_s { tr_info_t info; - tr_upload_t * upload; - tr_fd_t * fdlimit; + tr_ratecontrol_t * globalUpload; + tr_ratecontrol_t * globalDownload; + tr_ratecontrol_t * upload; + tr_ratecontrol_t * download; + tr_fd_t * fdlimit; int status; int finished; @@ -156,7 +159,8 @@ struct tr_handle_s int torrentCount; tr_torrent_t * torrents[TR_MAX_TORRENT_COUNT]; - tr_upload_t * upload; + tr_ratecontrol_t * upload; + tr_ratecontrol_t * download; tr_fd_t * fdlimit; int bindPort; diff --git a/libtransmission/peer.c b/libtransmission/peer.c index be1e3ba52..27f72f555 100644 --- a/libtransmission/peer.c +++ b/libtransmission/peer.c @@ -197,7 +197,7 @@ void tr_peerRem( tr_torrent_t * tor, int i ) } if( !peer->amChoking ) { - tr_uploadChoked( tor->upload ); + //tr_uploadChoked( tor->upload ); } tr_peerDestroy( tor->fdlimit, peer ); tor->peerCount--; @@ -242,6 +242,8 @@ int tr_peerRead( tr_torrent_t * tor, tr_peer_t * peer ) peer->pos += ret; if( NULL != tor ) { + tr_rcTransferred( tor->download, ret ); + tr_rcTransferred( tor->globalDownload, ret ); if( parseBuf( tor, peer, ret ) ) { return 1; @@ -358,7 +360,7 @@ writeBegin: /* Send pieces if we can */ while( ( p = blockPending( tor, peer, &size ) ) ) { - if( !tr_uploadCanUpload( tor->upload ) ) + if( !tr_rcCanTransfer( tor->globalUpload ) ) { break; } @@ -374,7 +376,8 @@ writeBegin: } blockSent( peer, ret ); - tr_uploadUploaded( tor->upload, ret ); + tr_rcTransferred( tor->upload, ret ); + tr_rcTransferred( tor->globalUpload, ret ); tor->uploaded[9] += ret; peer->outTotal += ret; diff --git a/libtransmission/peerutils.h b/libtransmission/peerutils.h index 6d699e587..80de118a9 100644 --- a/libtransmission/peerutils.h +++ b/libtransmission/peerutils.h @@ -165,13 +165,13 @@ static int checkPeer( tr_torrent_t * tor, int i ) { /* He doesn't need us */ sendChoke( peer, 1 ); - tr_uploadChoked( tor->upload ); + //tr_uploadChoked( tor->upload ); } - if( peer->amChoking && peer->peerInterested && - !peer->outSlow && tr_uploadCanUnchoke( tor->upload ) ) + if( peer->amChoking && peer->peerInterested /* && + !peer->outSlow && tr_uploadCanUnchoke( tor->upload ) */ ) { sendChoke( peer, 0 ); - tr_uploadUnchoked( tor->upload ); + //tr_uploadUnchoked( tor->upload ); } } diff --git a/libtransmission/ratecontrol.c b/libtransmission/ratecontrol.c new file mode 100644 index 000000000..7ef0717ce --- /dev/null +++ b/libtransmission/ratecontrol.c @@ -0,0 +1,147 @@ +/****************************************************************************** + * Copyright (c) 2005 Eric Petit + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "transmission.h" + +#define MAX_HISTORY 30 + +typedef struct tr_transfer_s tr_transfer_t; +struct tr_ratecontrol_s +{ + tr_lock_t lock; + int limit; + tr_transfer_t * first; + tr_transfer_t * last; +}; +struct tr_transfer_s +{ + uint64_t date; + int size; + tr_transfer_t * next; + tr_transfer_t * prev; +}; + +/*********************************************************************** + * rateForInterval + *********************************************************************** + * Returns the transfer rate on the last 'interval' milliseconds + **********************************************************************/ +static inline float rateForInterval( tr_ratecontrol_t * r, int interval ) +{ + tr_transfer_t * t; + uint64_t start = tr_date() - interval; + int total = 0; + + for( t = r->first; t && t->date > start; t = t->next ) + { + total += t->size; + } + + return ( 1000.0 / 1024.0 ) * total / interval; +} + +static inline void cleanOldTransfers( tr_ratecontrol_t * r ) +{ + tr_transfer_t * t, * prev; + uint64_t old = tr_date() - MAX_HISTORY * 1000; + + for( t = r->last; t && t->date < old; ) + { + prev = t->prev; + prev->next = NULL; + free( t ); + t = prev; + } +} + +tr_ratecontrol_t * tr_rcInit() +{ + tr_ratecontrol_t * r; + + r = calloc( sizeof( tr_ratecontrol_t ), 1 ); + r->limit = -1; + tr_lockInit( &r->lock ); + + return r; +} + +void tr_rcSetLimit( tr_ratecontrol_t * r, int limit ) +{ + tr_lockLock( &r->lock ); + r->limit = limit; + tr_lockUnlock( &r->lock ); +} + +int tr_rcCanTransfer( tr_ratecontrol_t * r ) +{ + int ret; + + tr_lockLock( &r->lock ); + ret = ( r->limit < -1 ) || ( rateForInterval( r, 1000 ) < r->limit ); + tr_lockUnlock( &r->lock ); + + return ret; +} + +void tr_rcTransferred( tr_ratecontrol_t * r, int size ) +{ + tr_transfer_t * t; + + tr_lockLock( &r->lock ); + t = malloc( sizeof( tr_transfer_t ) ); + + if( r->first ) + r->first->prev = t; + t->next = r->first; + t->prev = NULL; + r->first = t; + + t->date = tr_date(); + t->size = size; + + cleanOldTransfers( r ); + tr_lockUnlock( &r->lock ); +} + +float tr_rcRate( tr_ratecontrol_t * r ) +{ + float ret; + + tr_lockLock( &r->lock ); + ret = rateForInterval( r, MAX_HISTORY * 1000 ); + tr_lockUnlock( &r->lock ); + + return ret; +} + +void tr_rcClose( tr_ratecontrol_t * r ) +{ + tr_transfer_t * t, * next; + for( t = r->first; t; ) + { + next = t->next; + free( t ); + t = next; + } + tr_lockClose( &r->lock ); + free( r ); +} diff --git a/libtransmission/upload.h b/libtransmission/ratecontrol.h similarity index 74% rename from libtransmission/upload.h rename to libtransmission/ratecontrol.h index 21c10b905..308e0b721 100644 --- a/libtransmission/upload.h +++ b/libtransmission/ratecontrol.h @@ -20,13 +20,11 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -typedef struct tr_upload_s tr_upload_t; +typedef struct tr_ratecontrol_s tr_ratecontrol_t; -tr_upload_t * tr_uploadInit(); -void tr_uploadSetLimit( tr_upload_t *, int ); -int tr_uploadCanUnchoke( tr_upload_t * ); -void tr_uploadChoked( tr_upload_t * ); -void tr_uploadUnchoked( tr_upload_t * ); -int tr_uploadCanUpload( tr_upload_t * ); -void tr_uploadUploaded( tr_upload_t *, int ); -void tr_uploadClose( tr_upload_t * ); +tr_ratecontrol_t * tr_rcInit(); +void tr_rcSetLimit( tr_ratecontrol_t *, int ); +int tr_rcCanTransfer( tr_ratecontrol_t * ); +void tr_rcTransferred( tr_ratecontrol_t *, int ); +float tr_rcRate( tr_ratecontrol_t * ); +void tr_rcClose( tr_ratecontrol_t * ); diff --git a/libtransmission/transmission.c b/libtransmission/transmission.c index 063bd2af6..52805af56 100644 --- a/libtransmission/transmission.c +++ b/libtransmission/transmission.c @@ -27,8 +27,6 @@ **********************************************************************/ static void torrentReallyStop( tr_handle_t * h, int t ); static void downloadLoop( void * ); -static float rateDownload( tr_torrent_t * ); -static float rateUpload( tr_torrent_t * ); static void acceptLoop( void * ); static void acceptStop( tr_handle_t * h ); @@ -65,8 +63,9 @@ tr_handle_t * tr_init() signal( SIGPIPE, SIG_IGN ); /* Initialize rate and file descripts controls */ - h->upload = tr_uploadInit(); - h->fdlimit = tr_fdInit(); + h->upload = tr_rcInit(); + h->download = tr_rcInit(); + h->fdlimit = tr_fdInit(); h->bindPort = TR_DEFAULT_PORT; h->bindSocket = -1; @@ -145,7 +144,7 @@ void tr_setBindPort( tr_handle_t * h, int port ) **********************************************************************/ void tr_setUploadLimit( tr_handle_t * h, int limit ) { - tr_uploadSetLimit( h->upload, limit ); + tr_rcSetLimit( h->upload, limit ); } /*********************************************************************** @@ -155,20 +154,8 @@ void tr_setUploadLimit( tr_handle_t * h, int limit ) **********************************************************************/ void tr_torrentRates( tr_handle_t * h, float * dl, float * ul ) { - int i; - tr_torrent_t * tor; - - *dl = 0.0; - *ul = 0.0; - - for( i = 0; i < h->torrentCount; i++ ) - { - tor = h->torrents[i]; - tr_lockLock( &tor->lock ); - *dl += rateDownload( tor ); - *ul += rateUpload( tor ); - tr_lockUnlock( &tor->lock ); - } + *dl = tr_rcRate( h->download ); + *ul = tr_rcRate( h->upload ); } /*********************************************************************** @@ -248,8 +235,11 @@ int tr_torrentInit( tr_handle_t * h, const char * path ) tr_lockInit( &tor->lock ); - tor->upload = h->upload; - tor->fdlimit = h->fdlimit; + tor->globalUpload = h->upload; + tor->globalDownload = h->download; + tor->fdlimit = h->fdlimit; + tor->upload = tr_rcInit(); + tor->download = tr_rcInit(); /* We have a new torrent */ tr_lockLock( &h->acceptLock ); @@ -419,8 +409,8 @@ int tr_torrentStat( tr_handle_t * h, tr_stat_t ** stat ) } s[i].progress = tr_cpCompletionAsFloat( tor->completion ); - s[i].rateDownload = rateDownload( tor ); - s[i].rateUpload = rateUpload( tor ); + s[i].rateDownload = tr_rcRate( tor->download ); + s[i].rateUpload = tr_rcRate( tor->upload ); s[i].seeders = tr_trackerSeeders(tor); s[i].leechers = tr_trackerLeechers(tor); @@ -496,6 +486,9 @@ void tr_torrentClose( tr_handle_t * h, int t ) tr_lockClose( &tor->lock ); tr_cpClose( tor->completion ); + tr_rcClose( tor->upload ); + tr_rcClose( tor->download ); + if( tor->destination ) { free( tor->destination ); @@ -514,7 +507,7 @@ void tr_close( tr_handle_t * h ) { acceptStop( h ); tr_fdClose( h->fdlimit ); - tr_uploadClose( h->upload ); + tr_rcClose( h->upload ); free( h ); } @@ -585,38 +578,6 @@ static void downloadLoop( void * _tor ) tr_dbg( "Thread exited" ); } -/*********************************************************************** - * rateDownload, rateUpload - **********************************************************************/ -static float rateGeneric( uint64_t * dates, uint64_t * counts ) -{ - float ret; - int i; - - ret = 0.0; - for( i = 0; i < 9; i++ ) - { - if( dates[i+1] == dates[i] ) - { - continue; - } - ret += (float) ( i + 1 ) * 1000.0 / 1024.0 * - (float) ( counts[i+1] - counts[i] ) / - (float) ( dates[i+1] - dates[i] ); - } - ret *= 1000.0 / 1024.0 / 45.0; - - return ret; -} -static float rateDownload( tr_torrent_t * tor ) -{ - return rateGeneric( tor->dates, tor->downloaded ); -} -static float rateUpload( tr_torrent_t * tor ) -{ - return rateGeneric( tor->dates, tor->uploaded ); -} - /*********************************************************************** * acceptLoop **********************************************************************/ diff --git a/libtransmission/upload.c b/libtransmission/upload.c deleted file mode 100644 index cb3c8db6f..000000000 --- a/libtransmission/upload.c +++ /dev/null @@ -1,136 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2005 Eric Petit - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - *****************************************************************************/ - -#include "transmission.h" - -#define FOO 10 - -struct tr_upload_s -{ - tr_lock_t lock; - int limit; /* Max upload rate in KB/s */ - int count; /* Number of peers currently unchoked */ - uint64_t dates[FOO]; /* The last times we uploaded something */ - int sizes[FOO]; /* How many bytes we uploaded */ -}; - -tr_upload_t * tr_uploadInit() -{ - tr_upload_t * u; - - u = calloc( sizeof( tr_upload_t ), 1 ); - tr_lockInit( &u->lock ); - - return u; -} - -void tr_uploadSetLimit( tr_upload_t * u, int limit ) -{ - tr_lockLock( &u->lock ); - u->limit = limit; - tr_lockUnlock( &u->lock ); -} - -int tr_uploadCanUnchoke( tr_upload_t * u ) -{ - int ret; - - tr_lockLock( &u->lock ); - if( u->limit < 0 ) - { - /* Infinite number of slots */ - ret = 1; - } - else - { - /* One slot per 2 KB/s */ - ret = ( u->count < ( u->limit + 1 ) / 2 ); - } - tr_lockUnlock( &u->lock ); - - return ret; -} - -void tr_uploadChoked( tr_upload_t * u ) -{ - tr_lockLock( &u->lock ); - (u->count)--; - tr_lockUnlock( &u->lock ); -} - -void tr_uploadUnchoked( tr_upload_t * u ) -{ - tr_lockLock( &u->lock ); - (u->count)++; - tr_lockUnlock( &u->lock ); -} - -int tr_uploadCanUpload( tr_upload_t * u ) -{ - int ret, i, size; - uint64_t now; - - tr_lockLock( &u->lock ); - if( u->limit < 0 ) - { - /* No limit */ - ret = 1; - } - else - { - ret = 0; - size = 0; - now = tr_date(); - - /* Check the last FOO times we sent something and decide if - we must wait */ - for( i = 0; i < FOO; i++ ) - { - size += u->sizes[i]; - if( (uint64_t) size * 1000 < - ( now - u->dates[i] ) * u->limit * 1024 ) - { - ret = 1; - break; - } - } - } - tr_lockUnlock( &u->lock ); - - return ret; -} - -void tr_uploadUploaded( tr_upload_t * u, int size ) -{ - tr_lockLock( &u->lock ); - memmove( &u->dates[1], &u->dates[0], (FOO-1) * sizeof( uint64_t ) ); - memmove( &u->sizes[1], &u->sizes[0], (FOO-1) * sizeof( int ) ); - u->dates[0] = tr_date(); - u->sizes[0] = size; - tr_lockUnlock( &u->lock ); -} - -void tr_uploadClose( tr_upload_t * u ) -{ - tr_lockClose( &u->lock ); - free( u ); -}