From 26660b0594e39a608825e9c52630c4d52009fa56 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Sat, 7 Jul 2007 04:13:13 +0000 Subject: [PATCH] fix broken rate control. --- libtransmission/peer.c | 24 +--- libtransmission/ratecontrol.c | 261 +++++++++++++++------------------- libtransmission/ratecontrol.h | 6 +- 3 files changed, 121 insertions(+), 170 deletions(-) diff --git a/libtransmission/peer.c b/libtransmission/peer.c index 75da17f74..94badf71a 100644 --- a/libtransmission/peer.c +++ b/libtransmission/peer.c @@ -377,16 +377,11 @@ int tr_peerRead( tr_peer_t * peer ) peer->size *= 2; peer->buf = realloc( peer->buf, peer->size ); } -#if 0 - /* Never read more than 1K each time, otherwise the rate - control is no use */ + + /* Read in smallish chunks, otherwise we might read more + * than the download cap is supposed to allow us */ ret = tr_netRecv( peer->socket, &peer->buf[peer->pos], MIN( 1024, peer->size - peer->pos ) ); -#else - /* Hm, it doesn't *seem* to break rate control... */ - ret = tr_netRecv( peer->socket, &peer->buf[peer->pos], - peer->size - peer->pos ); -#endif if( ret & TR_NET_CLOSE ) { @@ -568,26 +563,19 @@ writeBegin: while( ( p = blockPending( tor, peer, &size ) ) ) { if( SWIFT_ENABLED && !isSeeding && (peer->credit<0) ) - { break; - } if( tor->customUploadLimit ? !tr_rcCanTransfer( tor->upload ) : !tr_rcCanTransfer( tor->handle->upload ) ) - { break; - } ret = tr_netSend( peer->socket, p, size ); if( ret & TR_NET_CLOSE ) - { return TR_ERROR; - } - else if( ret & TR_NET_BLOCK ) - { + + if( ret & TR_NET_BLOCK ) break; - } blockSent( peer, ret ); @@ -601,9 +589,7 @@ writeBegin: peer->outDate = date; if( !tr_peerAmChoking( peer ) ) - { tor->activityDate = date; - } /* In case this block is done, you may have messages pending. Send them before we start the next block */ diff --git a/libtransmission/ratecontrol.c b/libtransmission/ratecontrol.c index 448eb46d6..24279d42f 100644 --- a/libtransmission/ratecontrol.c +++ b/libtransmission/ratecontrol.c @@ -25,23 +25,12 @@ #include "transmission.h" #include "shared.h" -/* Maximum number of packets we keep track of. Since most packets are - * 1 KB, it means we remember the last 2 MB transferred */ -#define HISTORY_SIZE 2048 +#define GRANULARITY_MSEC 200 +#define SHORT_INTERVAL_MSEC 1000 +#define LONG_INTERVAL_MSEC 10000 +#define HISTORY_SIZE (LONG_INTERVAL_MSEC / GRANULARITY_MSEC) -/* How far back we go to calculate rates to be displayed in the - * interface */ -#define LONG_INTERVAL 30000 /* 30 secs */ - -/* How far back we go to calculate pseudo-instantaneous transfer rates, - * for the actual rate control */ -#define SHORT_INTERVAL 1000 /* 1 sec */ - - -/*********************************************************************** - * Structures - **********************************************************************/ -typedef struct tr_transfer_s +typedef struct { uint64_t date; int size; @@ -50,154 +39,130 @@ tr_transfer_t; struct tr_ratecontrol_s { - tr_lock_t lock; - int limit; - - /* Circular history: it's empty if transferStop == transferStart, - * full if ( transferStop + 1 ) % HISTORY_SIZE == transferStart */ + tr_rwlock_t lock; + int limit; + int newest; tr_transfer_t transfers[HISTORY_SIZE]; - int transferStart; - int transferStop; }; - -/*********************************************************************** - * Local prototypes - **********************************************************************/ -static float rateForInterval( tr_ratecontrol_t * r, int interval ); - - -/*********************************************************************** - * Exported functions - **********************************************************************/ - -tr_ratecontrol_t * tr_rcInit() +/* return the xfer rate over the last `interval' seconds in KiB/sec */ +static float +rateForInterval( const tr_ratecontrol_t * r, int interval_msec ) { - tr_ratecontrol_t * r; + uint64_t bytes = 0; + const uint64_t now = tr_date (); + int i = r->newest; + int real_interval_msec = 0; + for( ;; ) + { + if( r->transfers[i].date + interval_msec < now ) + break; - r = calloc( 1, sizeof( tr_ratecontrol_t ) ); + bytes += r->transfers[i].size; + real_interval_msec = now - r->transfers[i].date; + + if( --i == -1 ) i = HISTORY_SIZE - 1; /* circular history */ + if( i == r->newest ) break; /* we've come all the way around */ + } + + return !bytes || !real_interval_msec + ? 0.0 + : (bytes/1024.0) * (1000.0/real_interval_msec); +} + +/*** +**** +***/ + +tr_ratecontrol_t* +tr_rcInit( void ) +{ + tr_ratecontrol_t * r = tr_new0( tr_ratecontrol_t, 1 ); r->limit = -1; - tr_lockInit( &r->lock ); - + tr_rwInit( &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 <= 0 ) ? ( r->limit < 0 ) : - ( rateForInterval( r, SHORT_INTERVAL ) < r->limit ); - tr_lockUnlock( &r->lock ); - - return ret; -} - -void tr_rcTransferred( tr_ratecontrol_t * r, int size ) -{ - tr_transfer_t * t; - - if( size < 100 ) - { - /* Don't count small messages */ - return; - } - - tr_lockLock( &r->lock ); - - r->transferStop = ( r->transferStop + 1 ) % HISTORY_SIZE; - if( r->transferStop == r->transferStart ) - /* History is full, forget about the first (oldest) item */ - r->transferStart = ( r->transferStart + 1 ) % HISTORY_SIZE; - - t = &r->transfers[r->transferStop]; - t->date = tr_date(); - t->size = size; - - tr_lockUnlock( &r->lock ); -} - -float tr_rcRate( tr_ratecontrol_t * r ) -{ - float ret; - - tr_lockLock( &r->lock ); - ret = rateForInterval( r, LONG_INTERVAL ); - tr_lockUnlock( &r->lock ); - - return ret; -} - -void tr_rcReset( tr_ratecontrol_t * r ) -{ - tr_lockLock( &r->lock ); - r->transferStart = 0; - r->transferStop = 0; - tr_lockUnlock( &r->lock ); -} - -void tr_rcClose( tr_ratecontrol_t * r ) +void +tr_rcClose( tr_ratecontrol_t * r ) { tr_rcReset( r ); - tr_lockClose( &r->lock ); - free( r ); + tr_rwClose( &r->lock ); + tr_free( r ); } +/*** +**** +***/ -/*********************************************************************** - * Local functions - **********************************************************************/ - -/*********************************************************************** - * rateForInterval - *********************************************************************** - * Returns the transfer rate in KB/s on the last 'interval' - * milliseconds - **********************************************************************/ -static float rateForInterval( tr_ratecontrol_t * r, int interval ) +int +tr_rcCanTransfer( const tr_ratecontrol_t * r ) { - tr_transfer_t * t = NULL; - uint64_t now, then, start; - float total = 0; - int i; + int ret; + tr_rwReaderLock( (tr_rwlock_t*)&r->lock ); - now = then = tr_date(); - start = now - interval; + if( r->limit < 0 ) /* unbounded */ + ret = TRUE; + else if( !r->limit ) /* off */ + ret = FALSE; + else + ret = rateForInterval( r, SHORT_INTERVAL_MSEC ) < r->limit; - /* Browse the history back in time */ - for( i = r->transferStop; i != r->transferStart; i-- ) - { - t = &r->transfers[i]; - then = t->date; - if( then < start ) - break; - - total += t->size; - - if( !i ) - i = HISTORY_SIZE; /* Loop */ - } -#if 0 - if( ( r->transferStop + 1 ) % HISTORY_SIZE == r->transferStart - && i == r->transferStart ) - { - /* High bandwidth -> the history isn't big enough to remember - * everything transferred since 'interval' ms ago. Correct the - * interval so that we return the correct rate */ - interval = now - t->date; - } -#endif - - if( now == then ) - return 0.0; - return ( 1000.0f / 1024.0f ) * total / (now - then); + tr_rwReaderUnlock( (tr_rwlock_t*)&r->lock ); + return ret; } +float +tr_rcRate( const tr_ratecontrol_t * r ) +{ + float ret; + tr_rwReaderLock( (tr_rwlock_t*)&r->lock ); + + ret = rateForInterval( r, LONG_INTERVAL_MSEC ); + + tr_rwReaderUnlock( (tr_rwlock_t*)&r->lock ); + return ret; +} + +/*** +**** +***/ + +void +tr_rcTransferred( tr_ratecontrol_t * r, int size ) +{ + uint64_t now; + + if( size < 100 ) /* don't count small messages */ + return; + + tr_rwWriterLock( &r->lock ); + + now = tr_date (); + if( r->transfers[r->newest].date + GRANULARITY_MSEC >= now ) + r->transfers[r->newest].size += size; + else { + if( ++r->newest == HISTORY_SIZE ) r->newest = 0; + r->transfers[r->newest].date = now; + r->transfers[r->newest].size = size; + } + + tr_rwWriterUnlock( &r->lock ); +} + +void +tr_rcReset( tr_ratecontrol_t * r ) +{ + tr_rwWriterLock( &r->lock ); + r->newest = 0; + memset( r->transfers, 0, sizeof(tr_transfer_t) * HISTORY_SIZE ); + tr_rwWriterUnlock( &r->lock ); +} + +void +tr_rcSetLimit( tr_ratecontrol_t * r, int limit ) +{ + tr_rwWriterLock( &r->lock ); + r->limit = limit; + tr_rwWriterUnlock( &r->lock ); +} diff --git a/libtransmission/ratecontrol.h b/libtransmission/ratecontrol.h index f2077eea7..92c2a86e7 100644 --- a/libtransmission/ratecontrol.h +++ b/libtransmission/ratecontrol.h @@ -24,10 +24,10 @@ typedef struct tr_ratecontrol_s tr_ratecontrol_t; -tr_ratecontrol_t * tr_rcInit(); +tr_ratecontrol_t * tr_rcInit( void ); void tr_rcSetLimit( tr_ratecontrol_t *, int ); -int tr_rcCanTransfer( tr_ratecontrol_t * ); +int tr_rcCanTransfer( const tr_ratecontrol_t * ); void tr_rcTransferred( tr_ratecontrol_t *, int ); -float tr_rcRate( tr_ratecontrol_t * ); +float tr_rcRate( const tr_ratecontrol_t * ); void tr_rcReset( tr_ratecontrol_t * ); void tr_rcClose( tr_ratecontrol_t * );