transmission/libtransmission/ratecontrol.c

235 lines
6.4 KiB
C

/******************************************************************************
* $Id$
*
* Copyright (c) 2006 Transmission authors and contributors
*
* 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"
#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
/* 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
{
uint64_t date;
int size;
}
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_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()
{
tr_ratecontrol_t * r;
r = calloc( sizeof( tr_ratecontrol_t ), 1 );
r->limit = -1;
tr_lockInit( &r->lock );
return r;
}
int tr_rcCanGlobalTransfer( tr_handle_t * h, int isUpload )
{
tr_torrent_t * tor;
tr_ratecontrol_t * r;
float rate = 0;
int limit = isUpload ? h->uploadLimit : h->downloadLimit;
if( limit <= 0 )
{
return limit < 0;
}
tr_sharedLock( h->shared );
for( tor = h->torrentList; tor; tor = tor->next )
{
if( isUpload ? tor->customUploadLimit : tor->customDownloadLimit )
{
continue;
}
r = isUpload ? tor->upload : tor->download;
tr_lockLock( &r->lock );
rate += rateForInterval( r, SHORT_INTERVAL );
tr_lockUnlock( &r->lock );
if( rate >= (float)limit )
{
tr_sharedUnlock( h->shared );
return 0;
}
}
tr_sharedUnlock( h->shared );
return 1;
}
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 )
{
tr_rcReset( r );
tr_lockClose( &r->lock );
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 )
{
tr_transfer_t * t = NULL;
uint64_t now, start;
int i, total;
now = tr_date();
start = now - interval;
/* Browse the history back in time */
total = 0;
for( i = r->transferStop; i != r->transferStart; i-- )
{
t = &r->transfers[i];
if( t->date < start )
break;
total += t->size;
if( !i )
i = HISTORY_SIZE; /* Loop */
}
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;
}
return ( 1000.0f / 1024.0f ) * total / interval;
}