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

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

(1.4x libT) backport the bandwidth fixes back to the 1.4x branch, pass #3: add the new files bandwidth.[ch] and iobuf.[ch]

File size: 9.2 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        const double nextPulseSpeed = desiredSpeed * ( pulseCount + 1 ) - ( currentSpeed * pulseCount );
261        b->band[dir].bytesLeft = MAX( 0.0, nextPulseSpeed * 1024.0 * period_msec / 1000.0 );
262
263#ifdef DEBUG_DIRECTION
264        if( dir == DEBUG_DIRECTION )
265                fprintf( stderr, "bandwidth %p currentPieceSpeed(%5.2f of %5.2f) desiredSpeed(%5.2f), allocating %5.2f\n",
266                         b, currentSpeed, tr_bandwidthGetRawSpeed( b, dir ), desiredSpeed,
267                         b->band[dir].bytesLeft/1024.0 );
268#endif
269    }
270
271    /* notify the io buffers that there's more bandwidth available */
272    if( !b->band[dir].isLimited || ( b->band[dir].bytesLeft > 0 ) )
273    {
274        int i, n=0;
275        short what = dir==TR_UP ? EV_WRITE : EV_READ;
276        struct tr_iobuf ** iobufs = (struct tr_iobuf**) tr_ptrArrayPeek( b->iobufs, &n );
277#ifdef DEBUG_DIRECTION
278if( ( dir == DEBUG_DIRECTION ) && ( n > 1 ) )
279fprintf( stderr, "bandwidth %p has %d iobufs\n", b, n );
280#endif
281        for( i=0; i<n; ++i )
282            tr_iobuf_enable( iobufs[i], what );
283    }
284
285    /* all children should reallocate too */
286    if( 1 ) {
287        int i, n=0;
288        struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayPeek( b->children, &n );
289        for( i=0; i<n; ++i )
290            tr_bandwidthAllocate( children[i], dir, period_msec );
291    }
292}
293
294/***
295****
296***/
297
298void
299tr_bandwidthAddBuffer( tr_bandwidth        * b,
300                       struct tr_iobuf     * iobuf )
301{
302    assert( isBandwidth( b ) );
303    assert( iobuf );
304
305    tr_ptrArrayInsertSorted( b->iobufs, iobuf, comparePointers );
306}
307
308void
309tr_bandwidthRemoveBuffer( tr_bandwidth        * b,
310                          struct tr_iobuf     * iobuf )
311{
312    assert( isBandwidth( b ) );
313    assert( iobuf );
314
315    tr_ptrArrayRemoveSorted( b->iobufs, iobuf, comparePointers );
316}
317
318/***
319****
320***/
321
322size_t
323tr_bandwidthClamp( const tr_bandwidth  * b,
324                   tr_direction          dir,
325                   size_t                byteCount )
326{
327    assert( isBandwidth( b ) );
328    assert( isDirection( dir ) );
329
330    if( b )
331    {
332        if( b->band[dir].isLimited )
333            byteCount = MIN( byteCount, b->band[dir].bytesLeft );
334
335        if( b->parent && b->band[dir].honorParentLimits )
336            byteCount = tr_bandwidthClamp( b->parent, dir, byteCount );
337    }
338
339    return byteCount;
340}
341
342double
343tr_bandwidthGetRawSpeed( const tr_bandwidth * b, tr_direction dir )
344{
345    assert( isBandwidth( b ) );
346    assert( isDirection( dir ) );
347
348    return getSpeed( &b->band[dir].raw, HISTORY_MSEC );
349}
350
351double
352tr_bandwidthGetPieceSpeed( const tr_bandwidth * b, tr_direction dir )
353{
354    assert( isBandwidth( b ) );
355    assert( isDirection( dir ) );
356
357    return getSpeed( &b->band[dir].piece, HISTORY_MSEC );
358}
359
360void
361tr_bandwidthUsed( tr_bandwidth  * b,
362                  tr_direction    dir,
363                  size_t          byteCount,
364                  int             isPieceData )
365{
366    struct tr_band * band;
367    size_t oldBytesLeft;
368
369    assert( isBandwidth( b ) );
370    assert( isDirection( dir ) );
371
372    band = &b->band[dir];
373
374    oldBytesLeft = band->bytesLeft;
375
376    if( band->isLimited && isPieceData )
377        band->bytesLeft -= MIN( band->bytesLeft, byteCount );
378
379#ifdef DEBUG_DIRECTION
380if( ( dir == DEBUG_DIRECTION ) && ( band->isLimited ) )
381fprintf( stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
382         b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft );
383#endif
384
385    bytesUsed( &band->raw, byteCount );
386
387    if( isPieceData )
388        bytesUsed( &band->piece, byteCount );
389
390    if( b->parent != NULL )
391        tr_bandwidthUsed( b->parent, dir, byteCount, isPieceData );
392}
Note: See TracBrowser for help on using the repository browser.