st->percentDone * 100.0,
st->peersSendingToUs,
st->peersConnected,
- st->rateDownload,
+ st->pieceDownloadSpeed,
st->peersGettingFromUs,
- st->rateUpload,
+ st->pieceUploadSpeed,
ratioStr );
}
else if( st->activity & TR_STATUS_SEED )
buf, buflen,
"Seeding, uploading to %d of %d peer(s), %.0f KB/s [%s]",
st->peersGettingFromUs, st->peersConnected,
- st->rateUpload, ratioStr );
+ st->pieceUploadSpeed, ratioStr );
}
else *buf = '\0';
}
const int haveUp = torStat->peersGettingFromUs > 0;
if( haveDown )
- tr_strlspeed( downStr, torStat->rateDownload, sizeof( downStr ) );
+ tr_strlspeed( downStr, torStat->pieceDownloadSpeed, sizeof( downStr ) );
if( haveUp )
- tr_strlspeed( upStr, torStat->rateUpload, sizeof( upStr ) );
+ tr_strlspeed( upStr, torStat->pieceUploadSpeed, sizeof( upStr ) );
if( haveDown && haveUp )
/* Translators: do not translate the "speed|" disambiguation prefix.
sa = tr_torrentStatCached( ta );
sb = tr_torrentStatCached( tb );
- if( ( i = compareDouble( sa->rateUpload + sa->rateDownload,
- sb->rateUpload + sb->rateDownload ) ) )
+ if( ( i = compareDouble( sa->pieceUploadSpeed + sa->pieceDownloadSpeed,
+ sb->pieceUploadSpeed + sb->pieceDownloadSpeed ) ) )
return i;
if( sa->uploadedEver != sb->uploadedEver )
if( !isDisposed( core ) )
{
- tr_sessionGetSpeed( core->priv->session,
- &setme->clientDownloadSpeed,
- &setme->clientUploadSpeed );
+ tr_session * session = core->priv->session;
- gtk_tree_model_foreach( core->priv->model,
- statsForeach,
- setme );
+ setme->clientDownloadSpeed = tr_sessionGetPieceSpeed( session, TR_DOWN );
+
+ setme->clientUploadSpeed = tr_sessionGetPieceSpeed( session, TR_UP );
+
+ gtk_tree_model_foreach( core->priv->model, statsForeach, setme );
}
}
static void
updateSpeeds( PrivateData * p )
{
- char buf[128];
- float u, d;
tr_session * session = tr_core_session( p->core );
- tr_sessionGetSpeed( session, &d, &u );
- tr_strlspeed( buf, d, sizeof( buf ) );
- gtk_label_set_text( GTK_LABEL( p->dl_lb ), buf );
- tr_strlspeed( buf, u, sizeof( buf ) );
- gtk_label_set_text( GTK_LABEL( p->ul_lb ), buf );
+ if( session != NULL )
+ {
+ char buf[128];
+ double d;
+
+ d = tr_sessionGetPieceSpeed( session, TR_DOWN );
+ tr_strlspeed( buf, d, sizeof( buf ) );
+ gtk_label_set_text( GTK_LABEL( p->dl_lb ), buf );
+
+ d = tr_sessionGetPieceSpeed( session, TR_UP );
+ tr_strlspeed( buf, d, sizeof( buf ) );
+ gtk_label_set_text( GTK_LABEL( p->ul_lb ), buf );
+ }
}
void
#include "crypto.h"
#include "net.h"
#include "peer-io.h"
+#include "ratecontrol.h"
#include "trevent.h"
#include "utils.h"
struct tr_bandwidth * b = &io->bandwidth[TR_UP];
b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
b->bytesUsed += n;
+ tr_rcTransferred( io->session->rawSpeed[TR_UP], n );
dbgmsg( io,
"wrote %zu bytes to peer... upload bytesLeft is now %zu",
n,
struct tr_bandwidth * b = io->bandwidth + TR_DOWN;
b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
b->bytesUsed += n;
+ tr_rcTransferred( io->session->rawSpeed[TR_DOWN], n );
dbgmsg( io,
"%zu new input bytes. bytesUsed is %zu, bytesLeft is %zu",
n, b->bytesUsed,
/* how frequently to decide which peers live and die */
RECONNECT_PERIOD_MSEC = ( 2 * 1000 ),
+
+ /* how frequently to reallocate bandwidth */
+ BANDWIDTH_PERIOD_MSEC = 250,
/* max # of peers to ask fer per torrent per reconnect pulse */
MAX_RECONNECTIONS_PER_PULSE = 2,
struct tr_peerMgr
{
- uint8_t bandwidthPulseNumber;
- tr_session * session;
- tr_ptrArray * torrents; /* Torrent */
- tr_ptrArray * incomingHandshakes; /* tr_handshake */
- tr_timer * bandwidthTimer;
- double globalPoolHistory[2][BANDWIDTH_PULSE_HISTORY];
+ tr_session * session;
+ tr_ptrArray * torrents; /* Torrent */
+ tr_ptrArray * incomingHandshakes; /* tr_handshake */
+ tr_timer * bandwidthTimer;
+ tr_ratecontrol * globalPoolRawSpeed[2];
};
#define tordbg( t, ... ) \
static int bandwidthPulse( void * vmgr );
+
tr_peerMgr*
tr_peerMgrNew( tr_session * session )
{
m->session = session;
m->torrents = tr_ptrArrayNew( );
m->incomingHandshakes = tr_ptrArrayNew( );
- m->bandwidthPulseNumber = -1;
- m->bandwidthTimer = tr_timerNew( session, bandwidthPulse,
- m, 1000 / BANDWIDTH_PULSES_PER_SECOND );
+ m->globalPoolRawSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
+ m->globalPoolRawSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
+ m->bandwidthTimer = tr_timerNew( session, bandwidthPulse, m, BANDWIDTH_PERIOD_MSEC );
return m;
}
managerLock( manager );
tr_timerFree( &manager->bandwidthTimer );
+ tr_rcClose( manager->globalPoolRawSpeed[TR_CLIENT_TO_PEER] );
+ tr_rcClose( manager->globalPoolRawSpeed[TR_PEER_TO_CLIENT] );
/* free the handshakes. Abort invokes handshakeDoneCB(), which removes
* the item from manager->handshakes, so this is a little roundabout... */
****/
static double
-allocateHowMuch( double desiredAvgKB,
- const double * history )
-{
- const double baseline = desiredAvgKB * 1024.0 /
- BANDWIDTH_PULSES_PER_SECOND;
- const double min = baseline * 0.85;
- const double max = baseline * 1.15;
- int i;
- double usedBytes;
- double n;
- double clamped;
-
- for( usedBytes = i = 0; i < BANDWIDTH_PULSE_HISTORY; ++i )
- usedBytes += history[i];
-
- n = ( desiredAvgKB * 1024.0 )
- * ( BANDWIDTH_PULSE_HISTORY + 1.0 )
- / BANDWIDTH_PULSES_PER_SECOND
- - usedBytes;
+allocateHowMuch( double desired_average_kb_per_sec,
+ const tr_ratecontrol * ratecontrol )
+{
+ const int pulses_per_history = TR_RATECONTROL_HISTORY_MSEC / BANDWIDTH_PERIOD_MSEC;
+ const double seconds_per_pulse = BANDWIDTH_PERIOD_MSEC / 1000.0;
+ const double baseline_bytes_per_pulse = desired_average_kb_per_sec * 1024.0 * seconds_per_pulse;
+ const double min = baseline_bytes_per_pulse * 0.85;
+ const double max = baseline_bytes_per_pulse * 1.15;
+ const double current_bytes_per_pulse = tr_rcRate( ratecontrol ) * 1024.0 * seconds_per_pulse;
+ const double next_pulse_bytes = baseline_bytes_per_pulse * ( pulses_per_history + 1 )
+ - ( current_bytes_per_pulse * pulses_per_history );
+ double clamped;
/* clamp the return value to lessen oscillation */
- clamped = n;
+ clamped = next_pulse_bytes;
clamped = MAX( clamped, min );
clamped = MIN( clamped, max );
-/*fprintf( stderr, "desiredAvgKB is %.2f, rate is %.2f, allocating %.2f
- (%.2f)\n", desiredAvgKB,
- ((usedBytes*BANDWIDTH_PULSES_PER_SECOND)/BANDWIDTH_PULSE_HISTORY)/1024.0,
- clamped/1024.0, n/1024.0 );*/
+
+fprintf( stderr, "desiredAvgKB is %.2f, rate is %.2f, allocating %.2f (%.2f)\n",
+ desired_average_kb_per_sec,
+ tr_rcRate( ratecontrol ),
+ clamped/1024.0,
+ next_pulse_bytes/1024.0 );
+
return clamped;
}
* @param desiredAvgKB overall bandwidth goal for this set of peers
*/
static void
-setPeerBandwidth( tr_ptrArray * peerArray,
- const tr_direction direction,
- const double * history,
- double desiredAvgKB )
+setPeerBandwidth( tr_ptrArray * peerArray,
+ const tr_direction direction,
+ const tr_ratecontrol * ratecontrol,
+ double desiredAvgKB )
{
const int peerCount = tr_ptrArraySize( peerArray );
- const double bytes = allocateHowMuch( desiredAvgKB, history );
+ const double bytes = allocateHowMuch( desiredAvgKB, ratecontrol );
const double welfareBytes = MIN( 2048, bytes * 0.2 );
const double meritBytes = MAX( 0, bytes - welfareBytes );
tr_peer ** peers = (tr_peer**) tr_ptrArrayBase( peerArray );
tr_direction direction )
{
tr_session * session = mgr->session;
- const int pulseNumber = mgr->bandwidthPulseNumber;
const int torrentCount = tr_ptrArraySize( mgr->torrents );
Torrent ** torrents = (Torrent **) tr_ptrArrayBase( mgr->torrents );
tr_ptrArray * globalPool = tr_ptrArrayNew( );
/* before allocating bandwidth, pump the connected peers */
pumpAllPeers( mgr );
- for( i = 0; i < torrentCount; ++i )
+ for( i=0; i<torrentCount; ++i )
{
Torrent * t = torrents[i];
- const size_t used = countPeerBandwidth( t->peers, direction );
+ size_t used;
tr_speedlimit speedMode;
+ /* no point in allocating bandwidth for stopped torrents */
+ if( tr_torrentGetActivity( t->tor ) == TR_STATUS_STOPPED )
+ continue;
+
+ used = countPeerBandwidth( t->peers, direction );
countHandshakeBandwidth( t->outgoingHandshakes, direction );
/* remember this torrent's bytes used */
- t->tor->rateHistory[direction][pulseNumber] = used;
+ tr_rcTransferred( t->tor->rawSpeed[direction], used );
/* add this torrent's bandwidth use to allBytesUsed */
allBytesUsed += used;
case TR_SPEEDLIMIT_SINGLE:
setPeerBandwidth( t->peers, direction,
- t->tor->rateHistory[direction],
- tr_torrentGetSpeedLimit( t->tor,
- direction ) );
+ t->tor->rawSpeed[direction],
+ tr_torrentGetSpeedLimit( t->tor, direction ) );
break;
case TR_SPEEDLIMIT_GLOBAL:
allBytesUsed += i;
poolBytesUsed += i;
- mgr->globalPoolHistory[direction][pulseNumber] = poolBytesUsed;
+ tr_rcTransferred( mgr->globalPoolRawSpeed[direction], poolBytesUsed );
/* handle the global pool's connections */
if( !tr_sessionIsSpeedLimitEnabled( session, direction ) )
givePeersUnlimitedBandwidth( globalPool, direction );
else
setPeerBandwidth( globalPool, direction,
- mgr->globalPoolHistory[direction],
- tr_sessionGetSpeedLimit( session, direction ) );
+ mgr->globalPoolRawSpeed[direction],
+ tr_sessionGetSpeedLimit( session, direction ) );
/* now that we've allocated bandwidth, pump all the connected peers */
pumpAllPeers( mgr );
managerLock( mgr );
- /* keep track of how far we are into the cycle */
- if( ++mgr->bandwidthPulseNumber == BANDWIDTH_PULSE_HISTORY )
- mgr->bandwidthPulseNumber = 0;
-
/* allocate the upload and download bandwidth */
for( i = 0; i < 2; ++i )
allocateBandwidth( mgr, i );
#include "ratecontrol.h"
#include "utils.h"
-#define INTERVAL_MSEC 1000
-#define GRANULARITY_MSEC 200
-#define HISTORY_SIZE ( INTERVAL_MSEC / GRANULARITY_MSEC )
+enum
+{
+ INTERVAL_MSEC = TR_RATECONTROL_HISTORY_MSEC,
+
+ GRANULARITY_MSEC = 250,
+
+ HISTORY_SIZE = ( INTERVAL_MSEC / GRANULARITY_MSEC )
+};
struct tr_transfer
{
#ifndef _TR_RATECONTROL_H_
#define _TR_RATECONTROL_H_
+enum
+{
+ TR_RATECONTROL_HISTORY_MSEC = 2000
+};
+
typedef struct tr_ratecontrol tr_ratecontrol;
tr_bencListAddInt( p, inf->files[i].priority );
}
else if( !strcmp( key, "rateDownload" ) )
- tr_bencDictAddInt( d, key, (int)( st->rateDownload * 1024 ) );
+ tr_bencDictAddInt( d, key, (int)( st->pieceDownloadSpeed * 1024 ) );
else if( !strcmp( key, "rateUpload" ) )
- tr_bencDictAddInt( d, key, (int)( st->rateUpload * 1024 ) );
+ tr_bencDictAddInt( d, key, (int)( st->pieceUploadSpeed * 1024 ) );
else if( !strcmp( key, "recheckProgress" ) )
tr_bencDictAddDouble( d, key, st->recheckProgress );
else if( !strcmp( key, "scrapeResponse" ) )
{
tr_benc * d = tr_bencDictAddDict( args_out, "session-stats", 10 );
tr_torrent * tor = NULL;
- float up, down;
int running = 0;
int total = 0;
- tr_sessionGetSpeed( h, &down, &up );
while( ( tor = tr_torrentNext( h, tor ) ) )
{
++total;
}
tr_bencDictAddInt( d, "activeTorrentCount", running );
- tr_bencDictAddInt( d, "downloadSpeed", (int)( down * 1024 ) );
+ tr_bencDictAddInt( d, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( h, TR_DOWN ) * 1024 ) );
tr_bencDictAddInt( d, "pausedTorrentCount", total - running );
tr_bencDictAddInt( d, "torrentCount", total );
- tr_bencDictAddInt( d, "uploadSpeed", (int)( up * 1024 ) );
+ tr_bencDictAddInt( d, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( h, TR_UP ) * 1024 ) );
return NULL;
}
h->proxyPassword = tr_strdup( proxyPassword );
h->pieceSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
h->pieceSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
+ h->rawSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
+ h->rawSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
if( configDir == NULL )
configDir = tr_getDefaultConfigDir( );
****
***/
-void
-tr_sessionGetSpeed( const tr_handle * session,
- float * toClient,
- float * toPeer )
+double
+tr_sessionGetPieceSpeed( const tr_session * session, tr_direction dir )
+{
+ assert( dir==TR_UP || dir==TR_DOWN );
+
+ return session ? tr_rcRate( session->pieceSpeed[dir] ) : 0.0;
+}
+
+double
+tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir )
{
- if( session && toClient )
- *toClient = tr_rcRate( session->pieceSpeed[TR_PEER_TO_CLIENT] );
+ assert( dir==TR_UP || dir==TR_DOWN );
- if( session && toPeer )
- *toPeer = tr_rcRate( session->pieceSpeed[TR_CLIENT_TO_PEER] );
+ return session ? tr_rcRate( session->rawSpeed[dir] ) : 0.0;
}
int
/* free the session memory */
tr_rcClose( session->pieceSpeed[TR_PEER_TO_CLIENT] );
tr_rcClose( session->pieceSpeed[TR_CLIENT_TO_PEER] );
+ tr_rcClose( session->rawSpeed[TR_PEER_TO_CLIENT] );
+ tr_rcClose( session->rawSpeed[TR_CLIENT_TO_PEER] );
tr_lockFree( session->lock );
for( i = 0; i < session->metainfoLookupCount; ++i )
tr_free( session->metainfoLookup[i].filename );
#endif
#endif
-enum
-{
- /* How frequently to reallocate peer bandwidth. */
- BANDWIDTH_PULSES_PER_SECOND = 4,
-
- /* HOw many pulses to remember for averaging the current speed */
- BANDWIDTH_PULSE_HISTORY = ( BANDWIDTH_PULSES_PER_SECOND * 2 )
-};
-
typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t;
/* the rate at which pieces are being transferred between client and peer.
* protocol overhead is NOT included; this is only the piece data */
struct tr_ratecontrol * pieceSpeed[2];
+
+ /* the rate at which bytes are being transferred between client and peer. */
+ struct tr_ratecontrol * rawSpeed[2];
};
const char * tr_sessionFindTorrentFile( const tr_session * session,
randomizeTiers( info );
+ tor->rawSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
+ tor->rawSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
tor->pieceSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
tor->pieceSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
return tor ? &tor->info : NULL;
}
-static double
-tr_torrentGetRate( const tr_torrent * tor,
- tr_direction direction )
-{
- assert( tor != NULL );
- assert( direction == TR_UP || direction == TR_DOWN );
-
- return tr_rcRate( tor->pieceSpeed[direction] );
-}
-
const tr_stat *
tr_torrentStatCached( tr_torrent * tor )
{
&s->peersGettingFromUs,
s->peersFrom );
- s->rateDownload = tr_torrentGetRate( tor, TR_PEER_TO_CLIENT );
-
- s->rateUpload = tr_torrentGetRate( tor, TR_CLIENT_TO_PEER );
+ s->rawUploadSpeed = tr_rcRate( tor->rawSpeed[TR_UP] );
+ s->rawDownloadSpeed = tr_rcRate( tor->rawSpeed[TR_DOWN] );
+ s->pieceUploadSpeed = tr_rcRate( tor->pieceSpeed[TR_UP] );
+ s->pieceDownloadSpeed = tr_rcRate( tor->pieceSpeed[TR_DOWN] );
usableSeeds += tor->info.webseedCount;
if( s->leftUntilDone > s->desiredAvailable )
s->eta = TR_ETA_NOT_AVAIL;
- else if( s->rateDownload < 0.1 )
+ else if( s->pieceDownloadSpeed < 0.1 )
s->eta = TR_ETA_UNKNOWN;
else
- s->eta = s->leftUntilDone / s->rateDownload / 1024.0;
+ s->eta = s->leftUntilDone / s->pieceDownloadSpeed / 1024.0;
s->ratio = tr_getRatio(
s->uploadedEver,
tr_rcClose( tor->pieceSpeed[TR_PEER_TO_CLIENT] );
tr_rcClose( tor->pieceSpeed[TR_CLIENT_TO_PEER] );
+ tr_rcClose( tor->rawSpeed[TR_PEER_TO_CLIENT] );
+ tr_rcClose( tor->rawSpeed[TR_CLIENT_TO_PEER] );
tr_metainfoFree( inf );
tr_free( tor );
int uniqueId;
- /* this is the count of raw bytes transferred between the
- * client and its peers over the past HISTORY time slices.
- * this count is used for bandwidth allocation, and includes
- * piece data, protocol overhead, and estimated tcp header overhead. */
- double rateHistory[2][BANDWIDTH_PULSE_HISTORY];
-
/* the rate at which pieces are being transferred between client and
* its peers. protocol overhead is NOT included; only the piece data */
struct tr_ratecontrol * pieceSpeed[2];
+
+ /* the rate at which bytes are being sent between client and peers */
+ struct tr_ratecontrol * rawSpeed[2];
};
#endif
tr_direction direction,
int isEnabled );
-void tr_sessionGetSpeed( const tr_session * session,
- float * overall_down_KiBs,
- float * overall_up_KiBs );
+double tr_sessionGetRawSpeed( const tr_session * session,
+ tr_direction direection );
+
+double tr_sessionGetPieceSpeed( const tr_session * session,
+ tr_direction direection );
int tr_sessionIsSpeedLimitEnabled( const tr_session * session,
tr_direction direction );
}
tr_torrent_activity;
+tr_torrent_activity tr_torrentGetActivity( tr_torrent * );
+
#define TR_STATUS_IS_ACTIVE( s ) ( ( s ) != TR_STATUS_STOPPED )
typedef enum
}
tr_lockfile_state_t;
-tr_torrent_activity tr_torrentGetActivity( tr_torrent * );
-
enum
{
TR_PEER_FROM_INCOMING = 0, /* connections made to the listening port */
@see tr_stat.leftUntilDone */
float percentDone;
- /** Download speed in KiB/s */
- double rateDownload;
+ /** Speed all data being sent for this torrent. (KiB/s)
+ This includes piece data, protocol messages, and TCP overhead */
+ double rawUploadSpeed;
+
+ /** Speed all data being received for this torrent. (KiB/s)
+ This includes piece data, protocol messages, and TCP overhead */
+ double rawDownloadSpeed;
+
+ /** Speed all piece being sent for this torrent. (KiB/s)
+ This ONLY counts piece data. */
+ double pieceUploadSpeed;
- /** Upload speed in KiB/s */
- double rateUpload;
+ /** Speed all piece being received for this torrent. (KiB/s)
+ This ONLY counts piece data. */
+ double pieceDownloadSpeed;
#define TR_ETA_NOT_AVAIL -1
#define TR_ETA_UNKNOWN -2
{
if ([NSApp isOnLeopardOrBetter])
{
- float downloadRate = 0.0, uploadRate = 0.0;
BOOL badgeDownload = [[NSUserDefaults standardUserDefaults] boolForKey: @"BadgeDownloadRate"],
badgeUpload = [[NSUserDefaults standardUserDefaults] boolForKey: @"BadgeUploadRate"];
- if (badgeDownload || badgeUpload)
- tr_sessionGetSpeed(fLib, badgeDownload ? &downloadRate : NULL, badgeUpload ? &uploadRate : NULL);
+ float downloadRate = badgeDownload ? tr_sessionGetPieceSpeed( fLib, TR_DOWN ) : 0.0f;
+ float uploadRate = badgeUpload ? tr_sessionGetPieceSpeed( fLib, TR_UP ) : 0.0f;
//only update if the badged values change
if ([(BadgeView *)[[NSApp dockTile] contentView] setRatesWithDownload: downloadRate upload: uploadRate])
//set upload and download rate badges
NSString * downloadRateString = nil, * uploadRateString = nil;
- float downloadRate, uploadRate;
- tr_sessionGetSpeed(fLib, &downloadRate, &uploadRate);
+ float downloadRate = tr_sessionGetPieceSpeed( fLib, TR_DOWN );
+ float uploadRate = tr_sessionGetPieceSpeed( fLib, TR_UP );
if (checkDownload && downloadRate >= 0.1)
downloadRateString = [NSString stringForSpeedAbbrev: downloadRate];
if (![fStatusBar isHidden])
{
//set rates
- float downloadRate, uploadRate;
- tr_sessionGetSpeed(fLib, &downloadRate, &uploadRate);
+ float downloadRate = tr_sessionGetPieceSpeed( fLib, TR_DOWN );
+ float uploadRate = tr_sessionGetPieceSpeed( fLib, TR_UP );
[fTotalDLField setStringValue: [NSString stringForSpeed: downloadRate]];
[fTotalULField setStringValue: [NSString stringForSpeed: uploadRate]];
- (CGFloat) downloadRate
{
- return fStat->rateDownload;
+ return fStat->pieceDownloadSpeed;
}
- (CGFloat) uploadRate
{
- return fStat->rateUpload;
+ return fStat->pieceUploadSpeed;
}
- (CGFloat) totalRate