235 lines
6.4 KiB
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;
|
|
}
|
|
|