source: branches/1.4x/libtransmission/bandwidth.c @ 7222

Last change on this file since 7222 was 7222, checked in by charles, 12 years ago

(libT) possible fix for #1545: "1.41b1: speed temporarily jumps to 2x the upload limit"

File size: 9.4 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#include <assert.h>
14#include <limits.h>
15
16#include "event.h"
17
18#include "transmission.h"
19#include "bandwidth.h"
20#include "iobuf.h"
21#include "ptrarray.h"
22#include "utils.h"
23
24/***
25****
26***/
27
28enum
29{
30    HISTORY_MSEC = 2000,
31    INTERVAL_MSEC = HISTORY_MSEC,
32    GRANULARITY_MSEC = 40,
33    HISTORY_SIZE = ( INTERVAL_MSEC / GRANULARITY_MSEC ),
34    MAGIC_NUMBER = 43143
35};
36
37struct bratecontrol
38{
39    int newest;
40    struct { uint64_t date, size; } transfers[HISTORY_SIZE];
41};
42
43static float
44getSpeed( const struct bratecontrol * r, int interval_msec )
45{
46    uint64_t       bytes = 0;
47    const uint64_t cutoff = tr_date ( ) - interval_msec;
48    int            i = r->newest;
49
50    for( ;; )
51    {
52        if( r->transfers[i].date <= cutoff )
53            break;
54
55        bytes += r->transfers[i].size;
56
57        if( --i == -1 ) i = HISTORY_SIZE - 1; /* circular history */
58        if( i == r->newest ) break; /* we've come all the way around */
59    }
60
61    return ( bytes / 1024.0 ) * ( 1000.0 / interval_msec );
62}
63
64static void
65bytesUsed( struct bratecontrol * r, size_t size )
66{
67    const uint64_t now = tr_date ( );
68
69    if( r->transfers[r->newest].date + GRANULARITY_MSEC >= now )
70        r->transfers[r->newest].size += size;
71    else
72    {
73        if( ++r->newest == HISTORY_SIZE ) r->newest = 0;
74        r->transfers[r->newest].date = now;
75        r->transfers[r->newest].size = size;
76    }
77}
78
79/******
80*******
81*******
82******/
83
84struct tr_band
85{
86    tr_bool isLimited;
87    tr_bool honorParentLimits;
88    size_t bytesLeft;
89    double desiredSpeed;
90    struct bratecontrol raw;
91    struct bratecontrol piece;
92};
93
94struct tr_bandwidth
95{
96    struct tr_band band[2];
97    struct tr_bandwidth * parent;
98    int magicNumber;
99    tr_session * session;
100    tr_ptrArray * children; /* struct tr_bandwidth */
101    tr_ptrArray * iobufs; /* struct tr_iobuf */
102};
103
104/***
105****
106***/
107
108static int
109comparePointers( const void * a, const void * b )
110{
111    return a - b;
112}
113
114static int
115isBandwidth( const tr_bandwidth * b )
116{
117    return ( b != NULL ) && ( b->magicNumber == MAGIC_NUMBER );
118}
119
120static int
121isDirection( const tr_direction dir )
122{
123    return ( dir == TR_UP ) || ( dir == TR_DOWN );
124}
125
126/***
127****
128***/
129
130tr_bandwidth*
131tr_bandwidthNew( tr_session * session, tr_bandwidth * parent )
132{
133    tr_bandwidth * b = tr_new0( tr_bandwidth, 1 );
134    b->session = session;
135    b->children = tr_ptrArrayNew( );
136    b->iobufs = tr_ptrArrayNew( );
137    b->magicNumber = MAGIC_NUMBER;
138    b->band[TR_UP].honorParentLimits = 1;
139    b->band[TR_DOWN].honorParentLimits = 1;
140    tr_bandwidthSetParent( b, parent );
141    return b;
142}
143
144void
145tr_bandwidthFree( tr_bandwidth * b )
146{
147    assert( isBandwidth( b ) );
148
149    tr_bandwidthSetParent( b, NULL );
150    tr_ptrArrayFree( b->iobufs, NULL );
151    tr_ptrArrayFree( b->children, NULL );
152    b->magicNumber = 0xDEAD;
153    tr_free( b );
154}
155
156/***
157****
158***/
159
160void
161tr_bandwidthSetParent( tr_bandwidth  * b,
162                       tr_bandwidth  * parent )
163{
164    assert( isBandwidth( b ) );
165    assert( b != parent );
166
167    if( b->parent )
168    {
169        assert( isBandwidth( b->parent ) );
170
171        tr_ptrArrayRemoveSorted( b->parent->children, b, comparePointers );
172        b->parent= NULL;
173    }
174
175    if( parent )
176    {
177        assert( isBandwidth( parent ) );
178        assert( parent->parent != b );
179
180        tr_ptrArrayInsertSorted( parent->children, b, comparePointers );
181        b->parent = parent;
182    }
183}
184
185void
186tr_bandwidthHonorParentLimits( tr_bandwidth  * b,
187                               tr_direction    dir,
188                               int             honorParentLimits )
189{
190    assert( isBandwidth( b ) );
191    assert( isDirection( dir ) );
192
193    b->band[dir].honorParentLimits = honorParentLimits != 0;
194}
195
196/***
197****
198***/
199
200void
201tr_bandwidthSetDesiredSpeed( tr_bandwidth  * b,
202                             tr_direction    dir,
203                             double          desiredSpeed )
204{
205    assert( isBandwidth( b ) );
206    assert( isDirection( dir ) );
207
208    b->band[dir].desiredSpeed = desiredSpeed; 
209}
210
211double
212tr_bandwidthGetDesiredSpeed( const tr_bandwidth  * b,
213                             tr_direction          dir )
214{
215    assert( isBandwidth( b ) );
216    assert( isDirection( dir ) );
217
218    return b->band[dir].desiredSpeed;
219}
220
221void
222tr_bandwidthSetLimited( tr_bandwidth  * b,
223                        tr_direction    dir,
224                        int             isLimited )
225{
226    assert( isBandwidth( b ) );
227    assert( isDirection( dir ) );
228
229    b->band[dir].isLimited = isLimited != 0;
230}
231
232int
233tr_bandwidthIsLimited( const tr_bandwidth  * b,
234                       tr_direction          dir )
235{
236    assert( isBandwidth( b ) );
237    assert( isDirection( dir ) );
238
239    return b->band[dir].isLimited != 0;
240}
241
242#if 0
243#warning do not check the code in with this enabled
244#define DEBUG_DIRECTION TR_UP
245#endif
246
247void
248tr_bandwidthAllocate( tr_bandwidth  * b,
249                      tr_direction    dir,
250                      int             period_msec )
251{
252    assert( isBandwidth( b ) );
253    assert( isDirection( dir ) );
254
255    if( b->band[dir].isLimited )
256    {
257        const double currentSpeed = getSpeed( &b->band[dir].piece, HISTORY_MSEC - period_msec );
258        const double desiredSpeed = b->band[dir].desiredSpeed;
259        const double pulseCount = ( HISTORY_MSEC - period_msec ) / (double)period_msec;
260        double nextPulseSpeed = desiredSpeed * ( pulseCount + 1 ) - ( currentSpeed * pulseCount );
261
262        /* clamp the speed to prevent oscillation */
263        const double minSpeed = desiredSpeed * 0.8;
264        const double maxSpeed = desiredSpeed * 1.2;
265        nextPulseSpeed = MAX( minSpeed, nextPulseSpeed );
266        nextPulseSpeed = MIN( maxSpeed, nextPulseSpeed );
267
268        b->band[dir].bytesLeft = nextPulseSpeed * 1024.0 * period_msec / 1000.0;
269
270#ifdef DEBUG_DIRECTION
271        if( dir == DEBUG_DIRECTION )
272                fprintf( stderr, "bandwidth %p currentPieceSpeed(%5.2f of %5.2f) desiredSpeed(%5.2f), allocating %5.2f\n",
273                         b, currentSpeed, tr_bandwidthGetRawSpeed( b, dir ), desiredSpeed,
274                         b->band[dir].bytesLeft/1024.0 );
275#endif
276    }
277
278    /* notify the io buffers that there's more bandwidth available */
279    if( !b->band[dir].isLimited || ( b->band[dir].bytesLeft > 0 ) )
280    {
281        int i, n=0;
282        short what = dir==TR_UP ? EV_WRITE : EV_READ;
283        struct tr_iobuf ** iobufs = (struct tr_iobuf**) tr_ptrArrayPeek( b->iobufs, &n );
284#ifdef DEBUG_DIRECTION
285if( ( dir == DEBUG_DIRECTION ) && ( n > 1 ) )
286fprintf( stderr, "bandwidth %p has %d iobufs\n", b, n );
287#endif
288        for( i=0; i<n; ++i )
289            tr_iobuf_enable( iobufs[i], what );
290    }
291
292    /* all children should reallocate too */
293    if( 1 ) {
294        int i, n=0;
295        struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayPeek( b->children, &n );
296        for( i=0; i<n; ++i )
297            tr_bandwidthAllocate( children[i], dir, period_msec );
298    }
299}
300
301/***
302****
303***/
304
305void
306tr_bandwidthAddBuffer( tr_bandwidth        * b,
307                       struct tr_iobuf     * iobuf )
308{
309    assert( isBandwidth( b ) );
310    assert( iobuf );
311
312    tr_ptrArrayInsertSorted( b->iobufs, iobuf, comparePointers );
313}
314
315void
316tr_bandwidthRemoveBuffer( tr_bandwidth        * b,
317                          struct tr_iobuf     * iobuf )
318{
319    assert( isBandwidth( b ) );
320    assert( iobuf );
321
322    tr_ptrArrayRemoveSorted( b->iobufs, iobuf, comparePointers );
323}
324
325/***
326****
327***/
328
329size_t
330tr_bandwidthClamp( const tr_bandwidth  * b,
331                   tr_direction          dir,
332                   size_t                byteCount )
333{
334    assert( isBandwidth( b ) );
335    assert( isDirection( dir ) );
336
337    if( b )
338    {
339        if( b->band[dir].isLimited )
340            byteCount = MIN( byteCount, b->band[dir].bytesLeft );
341
342        if( b->parent && b->band[dir].honorParentLimits )
343            byteCount = tr_bandwidthClamp( b->parent, dir, byteCount );
344    }
345
346    return byteCount;
347}
348
349double
350tr_bandwidthGetRawSpeed( const tr_bandwidth * b, tr_direction dir )
351{
352    assert( isBandwidth( b ) );
353    assert( isDirection( dir ) );
354
355    return getSpeed( &b->band[dir].raw, HISTORY_MSEC );
356}
357
358double
359tr_bandwidthGetPieceSpeed( const tr_bandwidth * b, tr_direction dir )
360{
361    assert( isBandwidth( b ) );
362    assert( isDirection( dir ) );
363
364    return getSpeed( &b->band[dir].piece, HISTORY_MSEC );
365}
366
367void
368tr_bandwidthUsed( tr_bandwidth  * b,
369                  tr_direction    dir,
370                  size_t          byteCount,
371                  int             isPieceData )
372{
373    struct tr_band * band;
374    size_t oldBytesLeft;
375
376    assert( isBandwidth( b ) );
377    assert( isDirection( dir ) );
378
379    band = &b->band[dir];
380
381    oldBytesLeft = band->bytesLeft;
382
383    if( band->isLimited && isPieceData )
384        band->bytesLeft -= MIN( band->bytesLeft, byteCount );
385
386#ifdef DEBUG_DIRECTION
387if( ( dir == DEBUG_DIRECTION ) && ( band->isLimited ) )
388fprintf( stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
389         b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft );
390#endif
391
392    bytesUsed( &band->raw, byteCount );
393
394    if( isPieceData )
395        bytesUsed( &band->piece, byteCount );
396
397    if( b->parent != NULL )
398        tr_bandwidthUsed( b->parent, dir, byteCount, isPieceData );
399}
Note: See TracBrowser for help on using the repository browser.