source: trunk/libtransmission/choking.c @ 93

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

Cleaning and minor optims

File size: 6.6 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
26struct tr_choking_s
27{
28    tr_lock_t     lock;
29    tr_handle_t * h;
30    int           slots;
31};
32
33tr_choking_t * tr_chokingInit( tr_handle_t * h )
34{
35    tr_choking_t * c;
36
37    c        = calloc( sizeof( tr_choking_t ), 1 );
38    c->h     = h;
39    c->slots = 4242;
40    tr_lockInit( &c->lock );
41
42    return c;
43}
44
45void tr_chokingSetLimit( tr_choking_t * c, int limit )
46{
47    tr_lockLock( &c->lock );
48    if( limit < 0 )
49        c->slots = 4242;
50    else
51        /* Reckon a number of slots from the upload limit. There is no
52           official right way to do this, the formula below e.g. gives:
53            10  KB/s -> 4  * 2.50 KB/s
54            20  KB/s -> 6  * 3.33 KB/s
55            50  KB/s -> 10 * 5.00 KB/s
56            100 KB/s -> 14 * 7.14 KB/s */
57        c->slots = lrintf( sqrt( 2 * limit ) );
58    tr_lockUnlock( &c->lock );
59}
60
61#define sortPeersAscending(p,c)  sortPeers(p,c,0)
62#define sortPeersDescending(p,c) sortPeers(p,c,1)
63static inline void sortPeers( tr_peer_t ** peers, int count, int order )
64{
65    int i, j, sorted;
66    float rate1, rate2;
67    tr_peer_t * tmp;
68
69    for( i = count - 1; i > 0; i-- )
70    {
71        sorted = 1;
72        for( j = 0; j < i; j++ )
73        {
74            rate1 = tr_peerDownloadRate( peers[j] );
75            rate2 = tr_peerDownloadRate( peers[j+1] );
76            if( order ? ( rate1 < rate2 ) : ( rate1 > rate2 ) )
77            {
78                tmp        = peers[j];
79                peers[j]   = peers[j+1];
80                peers[j+1] = tmp;
81                sorted     = 0;
82            }
83        }
84        if( sorted )
85            break;
86    }
87}
88
89void tr_chokingPulse( tr_choking_t * c )
90{
91    int i, j, peersTotalCount;
92    tr_peer_t * peer;
93    tr_peer_t ** peersCanChoke, ** peersCanUnchoke;
94    int peersCanChokeCount, peersCanUnchokeCount, unchokedCount;
95    tr_torrent_t * tor;
96    uint64_t now = tr_date();
97
98    tr_lockLock( &c->lock );
99
100    /* Lock all torrents and get the total number of peers */
101    peersTotalCount = 0;
102    for( i = 0; i < c->h->torrentCount; i++ )
103    {
104        tor = c->h->torrents[i];
105        tr_lockLock( &tor->lock );
106        peersTotalCount += tor->peerCount;
107    }
108
109    peersCanChoke   = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
110    peersCanUnchoke = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
111    peersCanChokeCount   = 0;
112    peersCanUnchokeCount = 0;
113    unchokedCount        = 0;
114
115    for( i = 0; i < c->h->torrentCount; i++ )
116    {
117        tor = c->h->torrents[i];
118        for( j = 0; j < tor->peerCount; j++ )
119        {
120            peer = tor->peers[j];
121
122            if( !tr_peerIsConnected( peer ) )
123                continue;
124
125            /* Choke peers who have lost their interest in us */
126            if( !tr_peerIsInterested( peer ) )
127            {
128                if( tr_peerIsUnchoked( peer ) )
129                    tr_peerChoke( peer );
130                continue;
131            }
132
133            /* Build two lists of interested peers: those we may choke,
134               those we may unchoke. Whatever happens, we never choke a
135               peer less than 10 seconds after the time we unchoked him
136               (or the other way around). */
137            if( tr_peerIsUnchoked( peer ) )
138            {
139                unchokedCount++;
140                if( tr_peerLastChoke( peer ) + 10000 < now )
141                    peersCanChoke[peersCanChokeCount++] = peer;
142            }
143            else
144            {
145                if( tr_peerLastChoke( peer ) + 10000 < now )
146                    peersCanUnchoke[peersCanUnchokeCount++] = peer;
147            }
148        }
149    }
150
151    /* Sort peers by the rate we are downloading from them. */
152    sortPeersDescending( peersCanChoke, peersCanChokeCount );
153    sortPeersAscending( peersCanUnchoke, peersCanUnchokeCount );
154
155    if( unchokedCount > c->slots && peersCanChokeCount > 0 )
156    {
157        /* We have more open slots than what we should have (the user
158           has just lowered his upload limit. We need to choke some of the
159           peers we are uploading to. */
160        int willChoke;
161        willChoke = MIN( peersCanChokeCount, unchokedCount - c->slots );
162        for( i = 0; i < willChoke; i++ )
163            tr_peerChoke( peersCanChoke[--peersCanChokeCount] );
164    }
165    else if( unchokedCount < c->slots && peersCanUnchokeCount > 0 )
166    {
167        /* We have unused open slots. Let's unchoke some people. */
168        int willUnchoke;
169        willUnchoke = MIN( peersCanUnchokeCount, c->slots - unchokedCount );
170        for( i = 0; i < willUnchoke; i++ )
171            tr_peerUnchoke( peersCanUnchoke[--peersCanUnchokeCount] );
172    }
173
174    while( peersCanChokeCount > 0 && peersCanUnchokeCount > 0 )
175    {
176        /* All slots are used and more peers are waiting. If
177           applicable, choke some peers in favor of others who are
178           uploading more to us. */
179        if( tr_peerDownloadRate( peersCanUnchoke[peersCanUnchokeCount - 1] )
180                < tr_peerDownloadRate( peersCanChoke[peersCanChokeCount - 1] ) )
181            break;
182
183        tr_peerChoke( peersCanChoke[--peersCanChokeCount] );
184        tr_peerUnchoke( peersCanUnchoke[--peersCanUnchokeCount] );
185    }
186
187    free( peersCanChoke );
188    free( peersCanUnchoke );
189
190    /* Unlock all torrents */
191    for( i = 0; i < c->h->torrentCount; i++ )
192    {
193        tr_lockUnlock( &c->h->torrents[i]->lock );
194    }
195
196    tr_lockUnlock( &c->lock );
197}
198
199void tr_chokingClose( tr_choking_t * c )
200{
201    tr_lockClose( &c->lock );
202    free( c );
203}
Note: See TracBrowser for help on using the repository browser.