source: trunk/libtransmission/choking.c @ 95

Last change on this file since 95 was 95, checked in by titer, 15 years ago

BeOS fixes

File size: 6.7 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2006 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include <math.h>
24#include "transmission.h"
25
26#ifdef SYS_BEOS
27#define lrintf(a) ((int)(0.5+(a)))
28#endif
29
30struct tr_choking_s
31{
32    tr_lock_t     lock;
33    tr_handle_t * h;
34    int           slots;
35};
36
37tr_choking_t * tr_chokingInit( tr_handle_t * h )
38{
39    tr_choking_t * c;
40
41    c        = calloc( sizeof( tr_choking_t ), 1 );
42    c->h     = h;
43    c->slots = 4242;
44    tr_lockInit( &c->lock );
45
46    return c;
47}
48
49void tr_chokingSetLimit( tr_choking_t * c, int limit )
50{
51    tr_lockLock( &c->lock );
52    if( limit < 0 )
53        c->slots = 4242;
54    else
55        /* Reckon a number of slots from the upload limit. There is no
56           official right way to do this, the formula below e.g. gives:
57            10  KB/s -> 4  * 2.50 KB/s
58            20  KB/s -> 6  * 3.33 KB/s
59            50  KB/s -> 10 * 5.00 KB/s
60            100 KB/s -> 14 * 7.14 KB/s */
61        c->slots = lrintf( sqrt( 2 * limit ) );
62    tr_lockUnlock( &c->lock );
63}
64
65#define sortPeersAscending(p,c)  sortPeers(p,c,0)
66#define sortPeersDescending(p,c) sortPeers(p,c,1)
67static inline void sortPeers( tr_peer_t ** peers, int count, int order )
68{
69    int i, j, sorted;
70    float rate1, rate2;
71    tr_peer_t * tmp;
72
73    for( i = count - 1; i > 0; i-- )
74    {
75        sorted = 1;
76        for( j = 0; j < i; j++ )
77        {
78            rate1 = tr_peerDownloadRate( peers[j] );
79            rate2 = tr_peerDownloadRate( peers[j+1] );
80            if( order ? ( rate1 < rate2 ) : ( rate1 > rate2 ) )
81            {
82                tmp        = peers[j];
83                peers[j]   = peers[j+1];
84                peers[j+1] = tmp;
85                sorted     = 0;
86            }
87        }
88        if( sorted )
89            break;
90    }
91}
92
93void tr_chokingPulse( tr_choking_t * c )
94{
95    int i, j, peersTotalCount;
96    tr_peer_t * peer;
97    tr_peer_t ** peersCanChoke, ** peersCanUnchoke;
98    int peersCanChokeCount, peersCanUnchokeCount, unchokedCount;
99    tr_torrent_t * tor;
100    uint64_t now = tr_date();
101
102    tr_lockLock( &c->lock );
103
104    /* Lock all torrents and get the total number of peers */
105    peersTotalCount = 0;
106    for( i = 0; i < c->h->torrentCount; i++ )
107    {
108        tor = c->h->torrents[i];
109        tr_lockLock( &tor->lock );
110        peersTotalCount += tor->peerCount;
111    }
112
113    peersCanChoke   = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
114    peersCanUnchoke = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
115    peersCanChokeCount   = 0;
116    peersCanUnchokeCount = 0;
117    unchokedCount        = 0;
118
119    for( i = 0; i < c->h->torrentCount; i++ )
120    {
121        tor = c->h->torrents[i];
122        for( j = 0; j < tor->peerCount; j++ )
123        {
124            peer = tor->peers[j];
125
126            if( !tr_peerIsConnected( peer ) )
127                continue;
128
129            /* Choke peers who have lost their interest in us */
130            if( !tr_peerIsInterested( peer ) )
131            {
132                if( tr_peerIsUnchoked( peer ) )
133                    tr_peerChoke( peer );
134                continue;
135            }
136
137            /* Build two lists of interested peers: those we may choke,
138               those we may unchoke. Whatever happens, we never choke a
139               peer less than 10 seconds after the time we unchoked him
140               (or the other way around). */
141            if( tr_peerIsUnchoked( peer ) )
142            {
143                unchokedCount++;
144                if( tr_peerLastChoke( peer ) + 10000 < now )
145                    peersCanChoke[peersCanChokeCount++] = peer;
146            }
147            else
148            {
149                if( tr_peerLastChoke( peer ) + 10000 < now )
150                    peersCanUnchoke[peersCanUnchokeCount++] = peer;
151            }
152        }
153    }
154
155    /* Sort peers by the rate we are downloading from them. */
156    sortPeersDescending( peersCanChoke, peersCanChokeCount );
157    sortPeersAscending( peersCanUnchoke, peersCanUnchokeCount );
158
159    if( unchokedCount > c->slots && peersCanChokeCount > 0 )
160    {
161        /* We have more open slots than what we should have (the user
162           has just lowered his upload limit. We need to choke some of the
163           peers we are uploading to. */
164        int willChoke;
165        willChoke = MIN( peersCanChokeCount, unchokedCount - c->slots );
166        for( i = 0; i < willChoke; i++ )
167            tr_peerChoke( peersCanChoke[--peersCanChokeCount] );
168    }
169    else if( unchokedCount < c->slots && peersCanUnchokeCount > 0 )
170    {
171        /* We have unused open slots. Let's unchoke some people. */
172        int willUnchoke;
173        willUnchoke = MIN( peersCanUnchokeCount, c->slots - unchokedCount );
174        for( i = 0; i < willUnchoke; i++ )
175            tr_peerUnchoke( peersCanUnchoke[--peersCanUnchokeCount] );
176    }
177
178    while( peersCanChokeCount > 0 && peersCanUnchokeCount > 0 )
179    {
180        /* All slots are used and more peers are waiting. If
181           applicable, choke some peers in favor of others who are
182           uploading more to us. */
183        if( tr_peerDownloadRate( peersCanUnchoke[peersCanUnchokeCount - 1] )
184                < tr_peerDownloadRate( peersCanChoke[peersCanChokeCount - 1] ) )
185            break;
186
187        tr_peerChoke( peersCanChoke[--peersCanChokeCount] );
188        tr_peerUnchoke( peersCanUnchoke[--peersCanUnchokeCount] );
189    }
190
191    free( peersCanChoke );
192    free( peersCanUnchoke );
193
194    /* Unlock all torrents */
195    for( i = 0; i < c->h->torrentCount; i++ )
196    {
197        tr_lockUnlock( &c->h->torrents[i]->lock );
198    }
199
200    tr_lockUnlock( &c->lock );
201}
202
203void tr_chokingClose( tr_choking_t * c )
204{
205    tr_lockClose( &c->lock );
206    free( c );
207}
Note: See TracBrowser for help on using the repository browser.