Ticket #1079: webseed_bandwidth.patch

File webseed_bandwidth.patch, 27.1 KB (added by alexat, 10 years ago)
  • libtransmission/bandwidth.c

     
    103103    b->children = TR_PTR_ARRAY_INIT;
    104104    b->magicNumber = BANDWIDTH_MAGIC_NUMBER;
    105105    b->uniqueKey = uniqueKey++;
     106    b->webtasks = TR_PTR_ARRAY_INIT;
    106107    b->band[TR_UP].honorParentLimits = true;
    107108    b->band[TR_DOWN].honorParentLimits = true;
    108     tr_bandwidthSetParent( b, parent );
     109
     110    if( parent )
     111        tr_bandwidthSetParent( b, parent );
     112    else
     113        b->lock = tr_lockNew( );
    109114}
    110115
     116static void bandwidthUnlink( tr_bandwidth * b, bool create_lock );
     117
    111118void
    112119tr_bandwidthDestruct( tr_bandwidth * b )
    113120{
    114121    assert( tr_isBandwidth( b ) );
    115122
    116     tr_bandwidthSetParent( b, NULL );
     123    if( b->parent )
     124        bandwidthUnlink( b, false );
     125    else
     126        tr_lockFree( b->lock );
     127
    117128    tr_ptrArrayDestruct( &b->children, NULL );
     129    tr_ptrArrayDestruct( &b->webtasks, NULL );
    118130
    119131    memset( b, ~0, sizeof( tr_bandwidth ) );
    120132}
     
    123135****
    124136***/
    125137
     138static void
     139bandwidthUnlink( tr_bandwidth * b,
     140                 bool           create_lock )
     141{
     142    void * removed;
     143    tr_bandwidth * parent = b->parent;
     144
     145    if( parent != NULL )
     146    {
     147        assert( tr_isBandwidth( parent ) );
     148
     149        tr_lockLock( parent->lock );
     150        removed = tr_ptrArrayRemoveSorted( &parent->children, b, compareBandwidth );
     151        b->parent = NULL;
     152        b->lock = create_lock ? tr_lockNew() : NULL;
     153        tr_lockUnlock( parent->lock );
     154
     155        assert( removed == b );
     156        assert( tr_ptrArrayFindSorted( &parent->children, b, compareBandwidth ) == NULL );
     157    }
     158}
     159
    126160void
    127161tr_bandwidthSetParent( tr_bandwidth  * b,
    128162                       tr_bandwidth  * parent )
     
    130164    assert( tr_isBandwidth( b ) );
    131165    assert( b != parent );
    132166
    133     if( b->parent )
    134     {
    135         void * removed;
     167    /* recursively setting locks isn't currently implemented */
     168    assert( tr_ptrArraySize( &b->children ) == 0 );
    136169
    137         assert( tr_isBandwidth( b->parent ) );
     170    bandwidthUnlink( b, parent == NULL );
    138171
    139         removed = tr_ptrArrayRemoveSorted( &b->parent->children, b, compareBandwidth );
    140         assert( removed == b );
    141         assert( tr_ptrArrayFindSorted( &b->parent->children, b, compareBandwidth ) == NULL );
    142 
    143         b->parent = NULL;
    144     }
    145 
    146172    if( parent )
    147173    {
    148174        assert( tr_isBandwidth( parent ) );
    149175        assert( parent->parent != b );
     176        assert( tr_ptrArrayFindSorted( &parent->children, b, compareBandwidth ) == NULL );
    150177
    151         assert( tr_ptrArrayFindSorted( &parent->children, b, compareBandwidth ) == NULL );
     178        tr_lockLock( parent->lock );
     179
    152180        tr_ptrArrayInsertSorted( &parent->children, b, compareBandwidth );
     181        b->parent = parent;
     182        if( b->lock ) tr_lockFree( b->lock );
     183        b->lock = parent->lock;
     184
     185        tr_lockUnlock( parent->lock );
     186
    153187        assert( tr_ptrArrayFindSorted( &parent->children, b, compareBandwidth ) == b );
    154         b->parent = parent;
    155188    }
    156189}
    157190
     
    164197                   tr_priority_t   parent_priority,
    165198                   tr_direction    dir,
    166199                   unsigned int    period_msec,
    167                    tr_ptrArray   * peer_pool )
     200                   tr_ptrArray   * peer_pool,
     201                   tr_ptrArray   * normal_prio,
     202                   tr_ptrArray   * high_prio )
    168203{
    169204    const tr_priority_t priority = MAX( parent_priority, b->priority );
    170205
     
    179214    }
    180215
    181216    /* add this bandwidth's peer, if any, to the peer pool */
    182     if( b->peer != NULL ) {
    183         b->peer->priority = priority;
    184         tr_ptrArrayAppend( peer_pool, b->peer );
     217    if( b->peer != NULL ||
     218        b->band[dir].bytesReserved < 0 ||
     219        ( dir == TR_DOWN && b->webBytesLeft > 0 ) )
     220    {
     221        switch( priority ) {
     222            case TR_PRI_HIGH:   tr_ptrArrayAppend( high_prio,    b ); /* fall through */
     223            case TR_PRI_NORMAL: tr_ptrArrayAppend( normal_prio,  b ); /* fall through */
     224            default:            tr_ptrArrayAppend( peer_pool,    b );
     225        }
    185226    }
    186227
    187228    /* traverse & repeat for the subtree */
     
    190231        struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayBase( &b->children );
    191232        const int n = tr_ptrArraySize( &b->children );
    192233        for( i=0; i<n; ++i )
    193             allocateBandwidth( children[i], priority, dir, period_msec, peer_pool );
     234        {
     235            allocateBandwidth( children[i], priority, dir, period_msec,
     236                               peer_pool, normal_prio, high_prio );
     237        }
    194238    }
    195239}
    196240
     241
     242static unsigned int bandwidthClamp( const tr_bandwidth * b,
     243                                    tr_direction dir, unsigned int byteCount );
     244
     245static inline unsigned int
     246reserveBytes ( tr_bandwidth * b,
     247               tr_direction   dir,
     248               unsigned int   period_msec,
     249               size_t         byteLimit )
     250{
     251    tr_bandwidth * tmp;
     252    struct tr_band * band = &b->band[dir];
     253    int byteCount = 0;
     254
     255    /* reserve bytes for webseeds */
     256    if( dir == TR_DOWN && b->webBytesLeft > 0 )
     257    {
     258        /* use a slightly lower 'now' value here, otherwise the transfers of
     259        exactly 2 seconds ago could be truncated due to timer inaccuracy */
     260        byteCount = getSpeed_Bps( &band->raw, HISTORY_MSEC, tr_time_msec( ) - 5 );
     261        byteCount = ( byteCount + WEBSPEED_INCREMENT ) * period_msec / 1000;
     262        if( b->webBytesLeft < (unsigned int) byteCount )
     263            byteCount = b->webBytesLeft;
     264    }
     265
     266    /* 'reserve' borrowed bytes for everyone */
     267    byteCount -= band->bytesReserved;
     268
     269    if( byteCount <= 0 )
     270        return 0;
     271           
     272    byteCount = bandwidthClamp( b, dir, MIN( (int)byteLimit, byteCount ));
     273
     274    /* no tr_bandwidthUsed() here */
     275    for( tmp = b; tmp != NULL; tmp = tmp->parent ) {
     276        struct tr_band * tb = &tmp->band[dir];
     277        if( tb->isLimited )
     278            tb->bytesLeft -= MIN( (unsigned int) byteCount, tb->bytesLeft );
     279    }
     280
     281    band->bytesReserved += byteCount;
     282
     283    return byteCount;
     284}
     285
    197286static void
    198 phaseOne( tr_ptrArray * peerArray, tr_direction dir )
     287phaseOne( tr_ptrArray * peerArray, tr_direction dir, unsigned int period_msec )
    199288{
    200289    int i, n;
    201290    int peerCount = tr_ptrArraySize( peerArray );
    202     struct tr_peerIo ** peers = (struct tr_peerIo**) tr_ptrArrayBase( peerArray );
     291    tr_bandwidth ** peers = (tr_bandwidth**) tr_ptrArrayBase( peerArray );
    203292
    204293    /* First phase of IO. Tries to distribute bandwidth fairly to keep faster
    205294     * peers from starving the others. Loop through the peers, giving each a
     
    214303         * frame right away and leave enough buffered data for the next frame to go
    215304         * out in a timely manner. */
    216305        const size_t increment = 3000;
     306        int bytesUsed;
     307        tr_bandwidth * b = peers[i];
    217308
    218         const int bytesUsed = tr_peerIoFlush( peers[i], dir, increment );
     309        /* first fill up bytesReserved */
     310        bytesUsed = reserveBytes( b, dir, period_msec, increment );
    219311
     312        /* bytesReserved is full, do actual io */
     313        if( b->peer && bytesUsed == 0 )
     314            bytesUsed = tr_peerIoFlush( b->peer, dir, increment );
     315
    220316        dbgmsg( "peer #%d of %d used %d bytes in this pass", i, n, bytesUsed );
    221317
    222318        if( bytesUsed == (int)increment )
    223319            ++i;
    224320        else {
    225321            /* peer is done writing for now; move it to the end of the list */
    226             tr_peerIo * pio = peers[i];
    227322            peers[i] = peers[n-1];
    228             peers[n-1] = pio;
     323            peers[n-1] = b;
    229324            --n;
    230325        }
    231326
     
    234329    }
    235330}
    236331
     332static void pauseWebtasks( tr_ptrArray * webtasks, bool unpause_all, bool unpause_one );
     333
    237334void
    238335tr_bandwidthAllocate( tr_bandwidth  * b,
    239336                      tr_direction    dir,
    240337                      unsigned int    period_msec )
    241338{
    242339    int i, peerCount;
    243     tr_ptrArray tmp = TR_PTR_ARRAY_INIT;
    244340    tr_ptrArray low = TR_PTR_ARRAY_INIT;
    245341    tr_ptrArray high = TR_PTR_ARRAY_INIT;
    246342    tr_ptrArray normal = TR_PTR_ARRAY_INIT;
    247     struct tr_peerIo ** peers;
     343    tr_bandwidth ** peers;
    248344
     345    tr_lockLock( b->lock );
     346
    249347    /* allocateBandwidth() is a helper function with two purposes:
    250348     * 1. allocate bandwidth to b and its subtree
    251349     * 2. accumulate an array of all the peerIos from b and its subtree. */
    252     allocateBandwidth( b, TR_PRI_LOW, dir, period_msec, &tmp );
    253     peers = (struct tr_peerIo**) tr_ptrArrayBase( &tmp );
    254     peerCount = tr_ptrArraySize( &tmp );
     350    allocateBandwidth( b, TR_PRI_LOW, dir, period_msec, &low, &normal, &high );
     351    peers = (tr_bandwidth**) tr_ptrArrayBase( &low );
     352    peerCount = tr_ptrArraySize( &low );
    255353
    256354    for( i=0; i<peerCount; ++i )
    257355    {
    258         tr_peerIo * io = peers[i];
    259         tr_peerIoRef( io );
    260 
    261         tr_peerIoFlushOutgoingProtocolMsgs( io );
    262 
    263         switch( io->priority ) {
    264             case TR_PRI_HIGH:   tr_ptrArrayAppend( &high,   io ); /* fall through */
    265             case TR_PRI_NORMAL: tr_ptrArrayAppend( &normal, io ); /* fall through */
    266             default:            tr_ptrArrayAppend( &low,    io );
     356        tr_peerIo * io = peers[i]->peer;
     357        if( io ) {
     358            tr_peerIoRef( io );
     359            tr_peerIoFlushOutgoingProtocolMsgs( io );
    267360        }
    268361    }
    269362
     
    271364     * peers from starving the others. Loop through the peers, giving each a
    272365     * small chunk of bandwidth. Keep looping until we run out of bandwidth
    273366     * and/or peers that can use it */
    274     phaseOne( &high, dir );
    275     phaseOne( &normal, dir );
    276     phaseOne( &low, dir );
     367    phaseOne( &high, dir, period_msec );
     368    phaseOne( &normal, dir, period_msec );
     369    phaseOne( &low, dir, period_msec );
    277370
    278371    /* Second phase of IO. To help us scale in high bandwidth situations,
    279372     * enable on-demand IO for peers with bandwidth left to burn.
    280373     * This on-demand IO is enabled until (1) the peer runs out of bandwidth,
    281374     * or (2) the next tr_bandwidthAllocate() call, when we start over again. */
    282     for( i=0; i<peerCount; ++i )
    283         tr_peerIoSetEnabled( peers[i], dir, tr_peerIoHasBandwidthLeft( peers[i], dir ) );
     375    for( i=0; i<peerCount; ++i ) {
     376        tr_bandwidth * pb = peers[i];
     377        const unsigned int bytesLeft = tr_bandwidthClamp( pb, dir, 2048 );
     378 
     379        if( dir == TR_DOWN && pb->webBytesLeft > 0 ) {
     380/*            fprintf (stderr, "%p: %d webtasks, %d bytes reserved, %d bytes left\n",
     381                     pb, tr_ptrArraySize( &pb->webtasks ), pb->band[TR_DOWN].bytesReserved,
     382                     pb->webBytesLeft ); */
     383            /* if we have really little bandwidth unpause only one web task */
     384            pauseWebtasks( &pb->webtasks, bytesLeft == 2048, bytesLeft > 0 );
     385        }
    284386
    285     for( i=0; i<peerCount; ++i )
    286         tr_peerIoUnref( peers[i] );
     387        if( pb->peer ) {
     388            tr_peerIoSetEnabled( pb->peer, dir, bytesLeft > 0 );
     389            tr_peerIoUnref( pb->peer );
     390        }
     391    }
    287392
     393    tr_lockUnlock( b->lock );
     394
    288395    /* cleanup */
    289396    tr_ptrArrayDestruct( &normal, NULL );
    290397    tr_ptrArrayDestruct( &high, NULL );
    291398    tr_ptrArrayDestruct( &low, NULL );
    292     tr_ptrArrayDestruct( &tmp, NULL );
    293399}
    294400
    295401void
     
    307413
    308414static unsigned int
    309415bandwidthClamp( const tr_bandwidth  * b,
    310                 uint64_t              now,
    311416                tr_direction          dir,
    312417                unsigned int          byteCount )
    313418{
    314419    assert( tr_isBandwidth( b ) );
    315420    assert( tr_isDirection( dir ) );
    316421
    317     if( b )
    318     {
    319         if( b->band[dir].isLimited )
    320         {
    321             byteCount = MIN( byteCount, b->band[dir].bytesLeft );
     422    if( b->band[dir].isLimited )
     423        byteCount = MIN( byteCount, b->band[dir].bytesLeft );
    322424
    323             /* if we're getting close to exceeding the speed limit,
    324              * clamp down harder on the bytes available */
    325             if( byteCount > 0 )
    326             {
    327                 double current;
    328                 double desired;
    329                 double r;
     425    if( b->parent && b->band[dir].honorParentLimits && ( byteCount > 0 ) )
     426        byteCount = bandwidthClamp( b->parent, dir, byteCount );
    330427
    331                 if( now == 0 )
    332                     now = tr_time_msec( );
    333 
    334                 current = tr_bandwidthGetRawSpeed_Bps( b, now, TR_DOWN );
    335                 desired = tr_bandwidthGetDesiredSpeed_Bps( b, TR_DOWN );
    336                 r = desired >= 1 ? current / desired : 0;
    337 
    338                      if( r > 1.0 ) byteCount = 0;
    339                 else if( r > 0.9 ) byteCount *= 0.8;
    340                 else if( r > 0.8 ) byteCount *= 0.9;
    341             }
    342         }
    343 
    344         if( b->parent && b->band[dir].honorParentLimits && ( byteCount > 0 ) )
    345             byteCount = bandwidthClamp( b->parent, now, dir, byteCount );
    346     }
    347 
    348428    return byteCount;
    349429}
    350430unsigned int
     
    352432                   tr_direction          dir,
    353433                   unsigned int          byteCount )
    354434{
    355     return bandwidthClamp( b, 0, dir, byteCount );
     435    int bytesLeft;
     436
     437    assert( tr_isBandwidth( b ) );
     438    assert( b->lock != NULL );
     439
     440    tr_lockLock( b->lock );
     441
     442    bytesLeft = bandwidthClamp( b, dir, byteCount ) +
     443                b->band[dir].bytesReserved;
     444
     445    tr_lockUnlock( b->lock );
     446
     447    if( bytesLeft <= 0 )
     448        return 0;
     449    if( (unsigned int)bytesLeft >= byteCount )
     450        return byteCount;
     451
     452    return bytesLeft;
    356453}
    357454
    358455
    359456unsigned int
    360457tr_bandwidthGetRawSpeed_Bps( const tr_bandwidth * b, const uint64_t now, const tr_direction dir )
    361458{
     459    unsigned int bps;
     460
    362461    assert( tr_isBandwidth( b ) );
     462    assert( b->lock != NULL );
    363463    assert( tr_isDirection( dir ) );
    364464
    365     return getSpeed_Bps( &b->band[dir].raw, HISTORY_MSEC, now );
     465    tr_lockLock( b->lock );
     466    bps = getSpeed_Bps( &b->band[dir].raw, HISTORY_MSEC, now );
     467    tr_lockUnlock( b->lock );
     468
     469    return bps;
    366470}
    367471
    368472unsigned int
    369473tr_bandwidthGetPieceSpeed_Bps( const tr_bandwidth * b, const uint64_t now, const tr_direction dir )
    370474{
     475    unsigned int bps;
     476
    371477    assert( tr_isBandwidth( b ) );
     478    assert( b->lock != NULL );
    372479    assert( tr_isDirection( dir ) );
    373480
    374     return getSpeed_Bps( &b->band[dir].piece, HISTORY_MSEC, now );
     481    tr_lockLock( b->lock );
     482    bps = getSpeed_Bps( &b->band[dir].piece, HISTORY_MSEC, now );
     483    tr_lockUnlock( b->lock );
     484
     485    return bps;
    375486}
    376487
     488static void
     489pauseWebtasks( tr_ptrArray * webtasks,
     490               bool unpause_all,
     491               bool unpause_one )
     492{
     493    int i;
     494    const int n = tr_ptrArraySize( webtasks );
     495    const int single = (!unpause_all && unpause_one ) ?
     496                           tr_cryptoWeakRandInt( n ) :
     497                           -1;
     498
     499    for( i = 0; i < n; i++ ) {
     500        tr_webPauseTask( tr_ptrArrayNth( webtasks, i ),
     501                         unpause_all ? false : single != i );
     502    }
     503}
     504
     505static void
     506bandwidthLimitReached( tr_bandwidth * b )
     507{
     508    struct tr_band * band = &b->band[TR_DOWN];
     509    tr_bandwidth ** children = (tr_bandwidth **) tr_ptrArrayBase( &b->children );
     510    const int n = tr_ptrArraySize( &b->children );
     511    int i;
     512
     513    if( band->isLimited && band->bytesLeft == 0 )
     514        return; /* limit already reached within this period */
     515
     516    /* don't pause tasks with webBytesLeft == 0 */
     517    if( band->bytesReserved <= 0 && b->webBytesLeft > 0 )
     518        pauseWebtasks( &b->webtasks, false, false );
     519
     520    for( i = 0; i < n; i++ )
     521        if( children[i]->band[TR_DOWN].honorParentLimits )
     522            bandwidthLimitReached( children[i] );
     523}
     524
    377525void
    378526tr_bandwidthUsed( tr_bandwidth  * b,
    379527                  tr_direction    dir,
     
    382530                  uint64_t        now )
    383531{
    384532    struct tr_band * band;
     533    unsigned int bytesAvailable = byteCount;
     534    tr_lock * bandwidthLock;
    385535
    386536    assert( tr_isBandwidth( b ) );
    387537    assert( tr_isDirection( dir ) );
     538    assert( b->lock != NULL );
    388539
     540    tr_lockLock( bandwidthLock = b->lock );
     541
    389542    band = &b->band[dir];
     543/*
     544    if( b->webBytesLeft < byteCount && !tr_ptrArrayEmpty( &b->webtasks ) )
     545        fprintf( stderr, "%p used %d bytes, but only has %d web bytes left!\n",
     546                 b, byteCount, b->webBytesLeft );
     547*/
     548    /* If we have reserved bytes, use these first. Otherwise find out how
     549    many bytes we have left in the current period and, if its too less, 'borrow'
     550    bytes from the next one by setting bytesReserved to a negative value. */
     551    if( band->bytesReserved > 0 )
     552        bytesAvailable -= MIN( (unsigned int) band->bytesReserved, bytesAvailable );
    390553
    391     if( band->isLimited && isPieceData )
    392         band->bytesLeft -= MIN( band->bytesLeft, byteCount );
     554    bytesAvailable = bandwidthClamp( b, dir, bytesAvailable );
     555    band->bytesReserved -= ( byteCount - bytesAvailable );
    393556
     557    b->webBytesLeft -= MIN( b->webBytesLeft, byteCount );
     558    if( b->webBytesLeft == 0 && !tr_ptrArrayEmpty( &b->webtasks ) )
     559        pauseWebtasks( &b->webtasks, true, true );
     560
     561    if( b->webBytesLeft && bytesAvailable <= 0 && band->bytesReserved <= 0)
     562        pauseWebtasks( &b->webtasks, false, false );
     563
     564    while( b )
     565    {
     566        band = &b->band[dir];
     567
     568        if( band->isLimited && isPieceData && band->bytesLeft > 0 && bytesAvailable > 0 ) {
     569            if( band->bytesLeft <= bytesAvailable )
     570               bandwidthLimitReached( b );
     571            band->bytesLeft -= MIN( band->bytesLeft, bytesAvailable );
     572        }
     573
    394574#ifdef DEBUG_DIRECTION
    395575if( ( dir == DEBUG_DIRECTION ) && ( band->isLimited ) )
    396576fprintf( stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\n",
    397577         b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft );
    398578#endif
    399579
    400     bytesUsed( now, &band->raw, byteCount );
     580        bytesUsed( now, &band->raw, byteCount );
    401581
    402     if( isPieceData )
    403         bytesUsed( now, &band->piece, byteCount );
     582        if( isPieceData )
     583            bytesUsed( now, &band->piece, byteCount );
    404584
    405     if( b->parent != NULL )
    406         tr_bandwidthUsed( b->parent, dir, byteCount, isPieceData, now );
     585        b = b->parent;
     586    }
     587
     588    tr_lockUnlock( bandwidthLock );
    407589}
     590
     591void
     592tr_bandwidthAddWebtask( tr_bandwidth * b,
     593                        struct tr_web_task * task,
     594                        unsigned int estimatedSize )
     595{
     596    assert( tr_isBandwidth( b ) );
     597    assert( b->lock != NULL );
     598
     599    tr_lockLock( b->lock );
     600
     601    tr_ptrArrayAppend( &b->webtasks, task );
     602    b->webBytesLeft += estimatedSize;
     603/*
     604    fprintf (stderr, "Adding webtask to %p - estimatedSize: %d, webBytesLeft: %d, bytesReserved: %d, count: %d.\n",
     605             b, estimatedSize, b->webBytesLeft, b->band[TR_DOWN].bytesReserved, tr_ptrArraySize( &b->webtasks ) );
     606*/
     607    tr_lockUnlock( b->lock );
     608}
     609
     610void
     611tr_bandwidthRemoveWebtask( tr_bandwidth * b,
     612                           struct tr_web_task * task,
     613                           unsigned int outstanding )
     614{
     615    int i, n;
     616
     617    assert( tr_isBandwidth( b ) );
     618    assert( b->lock != NULL );
     619
     620    tr_lockLock( b->lock );
     621/*
     622    fprintf( stderr, "Removing webtask from %p - outstanding: %d, webBytesLeft: %d, bytesReserved: %d, count: %d.\n",
     623             b, outstanding, b->webBytesLeft, b->band[TR_DOWN].bytesReserved, tr_ptrArraySize( &b->webtasks ) );
     624*/
     625    b->webBytesLeft -= MIN( b->webBytesLeft, outstanding );
     626    n = tr_ptrArraySize( &b->webtasks );
     627
     628    for( i = 0; i < n; i++ )
     629    {
     630        if( tr_ptrArrayNth( &b->webtasks, i ) == task ) {
     631            tr_ptrArrayRemove( &b->webtasks, i );
     632            break;
     633        }
     634        assert( i < n );
     635    }
     636
     637    if( n == 1 )
     638    {
     639        if( b->band[TR_DOWN].bytesReserved > 0 )
     640            b->band[TR_DOWN].bytesReserved = 0;
     641        b->webBytesLeft = 0;
     642    }
     643    else if( b->webBytesLeft == 0 )
     644    {
     645        /* with webBytesLeft == 0 the remaining tasks would
     646        never be unpaused again */
     647        pauseWebtasks( &b->webtasks, true, true );
     648    }
     649
     650    tr_lockUnlock( b->lock );
     651}
  • libtransmission/peer-io.h

     
    7979    bool                  dhtSupported;
    8080    bool                  utpSupported;
    8181
    82     tr_priority_t         priority;
    83 
    8482    short int             pendingEvents;
    8583
    8684    int                   magicNumber;
     
    364362                                  size_t                byteCount,
    365363                                  int                   isPieceData );
    366364
    367 static inline bool
    368 tr_peerIoHasBandwidthLeft( const tr_peerIo * io, tr_direction dir )
    369 {
    370     return tr_bandwidthClamp( &io->bandwidth, dir, 1024 ) > 0;
    371 }
    372 
    373365static inline unsigned int
    374366tr_peerIoGetPieceSpeed_Bps( const tr_peerIo * io, uint64_t now, tr_direction dir )
    375367{
  • libtransmission/bandwidth.h

     
    2222#include "transmission.h"
    2323#include "ptrarray.h"
    2424#include "utils.h" /* tr_new(), tr_free() */
     25#include "platform.h"
     26#include "web.h"
    2527
    2628struct tr_peerIo;
    2729
     
    3840    INTERVAL_MSEC = HISTORY_MSEC,
    3941    GRANULARITY_MSEC = 200,
    4042    HISTORY_SIZE = ( INTERVAL_MSEC / GRANULARITY_MSEC ),
    41     BANDWIDTH_MAGIC_NUMBER = 43143
     43    BANDWIDTH_MAGIC_NUMBER = 43143,
     44    WEBSPEED_INCREMENT = 1024
    4245};
    4346
    4447/* these are PRIVATE IMPLEMENTATION details that should not be touched.
     
    5962    bool honorParentLimits;
    6063    unsigned int bytesLeft;
    6164    unsigned int desiredSpeed_Bps;
     65    int bytesReserved;
    6266    struct bratecontrol raw;
    6367    struct bratecontrol piece;
    6468};
     
    101105 *   The peer-ios all have a pointer to their associated tr_bandwidth object,
    102106 *   and call tr_bandwidthClamp() before performing I/O to see how much
    103107 *   bandwidth they can safely use.
     108 *
     109 * THREAD SAFETY
     110 *
     111 *   Bandwidth can be consumed from multiple threads. All tr_bandwidth functions
     112 *   lock the bandwidth tree themselves, so you don't have to worry about
     113 *   thread safey when doing this.
     114 *
     115 *   You shouldn't call any other functions except tr_bandwidthClamp() and
     116 *   tr_bandwidthUsed() from different threads.
    104117 */
    105118typedef struct tr_bandwidth
    106119{
     
    115128    tr_session * session;
    116129    tr_ptrArray children; /* struct tr_bandwidth */
    117130    struct tr_peerIo * peer;
     131    tr_lock * lock;
     132    tr_ptrArray webtasks; /* struct tr_web_task */
     133    unsigned int webBytesLeft;
    118134}
    119135tr_bandwidth;
    120136
     
    263279void tr_bandwidthSetPeer( tr_bandwidth        * bandwidth,
    264280                          struct tr_peerIo    * peerIo );
    265281
     282void tr_bandwidthAddWebtask( tr_bandwidth * b,
     283                             struct tr_web_task * task,
     284                             unsigned int estimatedSize );
     285
     286void tr_bandwidthRemoveWebtask( tr_bandwidth * b,
     287                                struct tr_web_task * task,
     288                                unsigned int outstanding );
     289
    266290/* @} */
    267291#endif
  • libtransmission/peer-mgr.c

     
    35743574    tr_session * session = mgr->session;
    35753575    managerLock( mgr );
     3576    /* call this now, the timer shouldn't depend on this function's execution time */
     3577    tr_timerAddMsec( mgr->bandwidthTimer, BANDWIDTH_PERIOD_MSEC );
    35763578
    35773579    /* FIXME: this next line probably isn't necessary... */
    35783580    pumpAllPeers( mgr );
    35793581
     
    36063608
    36073609    reconnectPulse( 0, 0, mgr );
    36083610
    3609     tr_timerAddMsec( mgr->bandwidthTimer, BANDWIDTH_PERIOD_MSEC );
    36103611    managerUnlock( mgr );
    36113612}
    36123613
  • libtransmission/webseed.c

     
    227227        tr_free( w->file_urls[file_index] );
    228228        w->file_urls[file_index] = data->real_url;
    229229    }
     230
     231    tr_free( data );
    230232}
    231233
    232234/***
     
    254256
    255257    len = evbuffer_get_length( buf );
    256258
     259    while( task->web_task == NULL ) /* extremely unlikely, but possible */
     260        tr_wait_msec( 10 );
     261
    257262    if( !task->response_code )
    258263    {
    259264        tr_webGetTaskInfo( task->web_task, TR_WEB_GET_CODE, &task->response_code );
     
    336341        w->retry_challenge = running_tasks + w->idle_connections + 1;
    337342    }
    338343
    339     if( w->is_stopping && !webseed_has_tasks( w ) )
     344
     345    if( !w->is_stopping && tor
     346                        && tor->isRunning
     347                        && !tr_torrentIsSeed( tor )
     348                        && want )
    340349    {
    341         webseed_free( w );
    342     }
    343     else if( !w->is_stopping && tor
    344                              && tor->isRunning
    345                              && !tr_torrentIsSeed( tor )
    346                              && want )
    347     {
    348350        int i;
    349351        int got = 0;
    350352        tr_block_index_t * blocks = NULL;
     
    371373            task->response_code = 0;
    372374            task->block_size = tor->blockSize;
    373375            task->content = evbuffer_new( );
     376            task->web_task = NULL;
    374377            evbuffer_add_cb( task->content, on_content_changed, task );
    375378            tr_list_append( &w->tasks, task );
    376379            task_request_next_chunk( task );
     
    393396    struct tr_webseed_task * t = vtask;
    394397    tr_webseed * w = t->webseed;
    395398    tr_torrent * tor = tr_torrentFindFromId( session, w->torrent_id );
     399    struct tr_web_task * web_task = t->web_task;
     400
     401    const uint32_t bytes_done = t->blocks_done * t->block_size;
     402    const uint32_t buf_len = evbuffer_get_length( t->content );
     403    const uint32_t bytes_left = t->length - ( bytes_done + buf_len );
     404
    396405    const int success = ( response_code == 206 );
    397406
    398407    if( tor )
     
    421430        }
    422431        else
    423432        {
    424             const uint32_t bytes_done = t->blocks_done * tor->blockSize;
    425             const uint32_t buf_len = evbuffer_get_length( t->content );
    426 
    427             if( bytes_done + buf_len < t->length )
     433            if( bytes_left > 0 )
    428434            {
    429435                /* request finished successfully but there's still data missing. that
    430436                means we've reached the end of a file and need to request the next one */
     
    454460            }
    455461        }
    456462    }
     463
     464    /* remove web task *after* adding the new one */
     465    tr_bandwidthRemoveWebtask( &w->bandwidth, web_task, bytes_left );
     466
     467    if( w->is_stopping && !webseed_has_tasks( w ) )
     468        webseed_free( w );
    457469}
    458470
    459471static struct evbuffer *
     
    507519                     file_offset, file_offset + this_pass - 1 );
    508520        t->web_task = tr_webRunWithBuffer( w->session, urls[file_index],
    509521                                           range, NULL, web_response_func, t, t->content );
     522
     523        tr_bandwidthAddWebtask( &w->bandwidth, t->web_task, this_pass );
    510524    }
    511525}
    512526
     
    533547webseed_timer_func( evutil_socket_t foo UNUSED, short bar UNUSED, void * vw )
    534548{
    535549    tr_webseed * w = vw;
     550
    536551    if( w->retry_tickcount )
    537552        ++w->retry_tickcount;
    538     on_idle( w );
    539     tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
     553
     554    if( w->is_stopping && !webseed_has_tasks( w ) ) {
     555        webseed_free( w );
     556    }
     557    else {
     558        on_idle( w );
     559        tr_timerAddMsec( w->timer, TR_IDLE_TIMER_MSEC );
     560    }
    540561}
    541562
    542563tr_webseed*