source: trunk/libtransmission/choking.c @ 76

Last change on this file since 76 was 76, checked in by titer, 16 years ago

Fixes a crash (quite amazing that it didn't crash more often, actually...)

File size: 5.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        c->slots = lrintf( sqrt( 2 * limit ) );
52    tr_lockUnlock( &c->lock );
53}
54
55static inline void sortPeers( tr_peer_t ** peers, int count )
56{
57    int i, j;
58    tr_peer_t * tmp;
59
60    for( i = count - 1; i > 0; i-- )
61        for( j = 0; j < i; j++ )
62        {
63            if( tr_peerDownloadRate( peers[j] ) >
64                    tr_peerDownloadRate( peers[j+1] ) )
65            {
66                tmp        = peers[j];
67                peers[j]   = peers[j+1];
68                peers[j+1] = tmp;
69            }
70        }
71}
72
73void tr_chokingPulse( tr_choking_t * c )
74{
75    int i, j, peersTotalCount;
76    tr_peer_t * peer;
77    tr_peer_t ** peersCanChoke, ** peersCanUnchoke;
78    int peersCanChokeCount, peersCanUnchokeCount, unchokedCount;
79    tr_torrent_t * tor;
80    uint64_t now = tr_date();
81
82    tr_lockLock( &c->lock );
83
84    /* Lock all torrents and get the total number of peers */
85    peersTotalCount = 0;
86    for( i = 0; i < c->h->torrentCount; i++ )
87    {
88        tor = c->h->torrents[i];
89        tr_lockLock( &tor->lock );
90        peersTotalCount += tor->peerCount;
91    }
92
93    peersCanChoke   = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
94    peersCanUnchoke = malloc( peersTotalCount * sizeof( tr_peer_t * ) );
95    peersCanChokeCount   = 0;
96    peersCanUnchokeCount = 0;
97    unchokedCount        = 0;
98
99    /* Build two lists of interested peers: those who may choke,
100       those who may unchoke */
101    for( i = 0; i < c->h->torrentCount; i++ )
102    {
103        tor = c->h->torrents[i];
104        for( j = 0; j < tor->peerCount; j++ )
105        {
106            peer = tor->peers[j];
107
108            if( !tr_peerIsConnected( peer ) )
109                continue;
110
111            if( !tr_peerIsInterested( peer ) )
112            {
113                if( tr_peerIsUnchoked( peer ) )
114                    tr_peerChoke( peer );
115                continue;
116            }
117
118            if( tr_peerIsUnchoked( peer ) )
119            {
120                unchokedCount++;
121                if( tr_peerLastChoke( peer ) + 10000 < now )
122                    peersCanChoke[peersCanChokeCount++] = peer;
123            }
124            else
125            {
126                if( tr_peerLastChoke( peer ) + 10000 < now )
127                    peersCanUnchoke[peersCanUnchokeCount++] = peer;
128            }
129        }
130    }
131
132    sortPeers( peersCanChoke, peersCanChokeCount );
133    sortPeers( peersCanUnchoke, peersCanUnchokeCount );
134
135    if( unchokedCount > c->slots && peersCanChokeCount > 0 )
136    {
137        int willChoke;
138        willChoke = MIN( peersCanChokeCount, unchokedCount - c->slots );
139        for( i = 0; i < willChoke; i++ )
140            tr_peerChoke( peersCanChoke[i] );
141        peersCanChokeCount -= willChoke;
142        memmove( &peersCanChoke[0], &peersCanChoke[willChoke],
143                 peersCanChokeCount * sizeof( tr_peer_t * ) );
144    }
145    else if( unchokedCount < c->slots && peersCanUnchokeCount > 0 )
146    {
147        int willUnchoke;
148        willUnchoke = MIN( peersCanUnchokeCount, c->slots - unchokedCount );
149        for( i = 0; i < willUnchoke; i++ )
150            tr_peerUnchoke( peersCanUnchoke[peersCanUnchokeCount - i - 1] );
151        peersCanUnchokeCount -= willUnchoke;
152    }
153
154    while( peersCanChokeCount > 0 && peersCanUnchokeCount > 0 )
155    {
156        if( tr_peerDownloadRate( peersCanUnchoke[peersCanUnchokeCount - 1] )
157                < tr_peerDownloadRate( peersCanChoke[0] ) )
158            break;
159
160        tr_peerChoke( peersCanChoke[0] );
161        tr_peerUnchoke( peersCanUnchoke[peersCanUnchokeCount - 1] );
162        peersCanChokeCount--;
163        peersCanUnchokeCount--;
164        memmove( &peersCanChoke[0], &peersCanChoke[1],
165                 peersCanChokeCount * sizeof( tr_peer_t * ) );
166    }
167
168    free( peersCanChoke );
169    free( peersCanUnchoke );
170
171    /* Unlock all torrents */
172    for( i = 0; i < c->h->torrentCount; i++ )
173    {
174        tr_lockUnlock( &c->h->torrents[i]->lock );
175    }
176
177    tr_lockUnlock( &c->lock );
178}
179
180void tr_chokingClose( tr_choking_t * c )
181{
182    tr_lockClose( &c->lock );
183    free( c );
184}
Note: See TracBrowser for help on using the repository browser.