source: trunk/libtransmission/shared.c @ 2231

Last change on this file since 2231 was 2231, checked in by charles, 15 years ago

lots of performance improvements. fun!

  • dynamically resize the request queue on a per-peer basis based on its speed
  • fix a huge bug that sabotaged the `swift' performance
  • on startup, unchoke peers much sooner
  • bump MAX_PEERS and PERCENT_PEER_WANTED
  • do a better job of estimating speed on torrents less than 30 seconds old.
  • getting an unrecognized extension ID, ignore it instead of stopping the torrent.
  • Property svn:keywords set to Date Rev Author Id
File size: 12.2 KB
Line 
1/******************************************************************************
2 * $Id: shared.c 2231 2007-06-29 05:45:17Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "shared.h"
26#include "peer.h"
27
28/* Maximum number of peers that we keep in our local list */
29/* This is an arbitrary number, but it seems to work well */
30#define MAX_PEER_COUNT 128
31
32struct tr_shared_s
33{
34    tr_handle_t  * h;
35    volatile int die;
36    tr_thread_t  thread;
37    tr_lock_t    lock;
38
39    /* Incoming connections */
40    int          publicPort;
41    int          bindPort;
42    int          bindSocket;
43    int          peerCount;
44    tr_peer_t    * peers[MAX_PEER_COUNT];
45
46    /* NAT-PMP/UPnP */
47    tr_natpmp_t  * natpmp;
48    tr_upnp_t    * upnp;
49
50    /* Choking */
51    tr_choking_t * choking;
52};
53
54/***********************************************************************
55 * Local prototypes
56 **********************************************************************/
57static void SharedLoop( void * );
58static void SetPublicPort( tr_shared_t *, int );
59static void AcceptPeers( tr_shared_t * );
60static void ReadPeers( tr_shared_t * );
61static void DispatchPeers( tr_shared_t * );
62
63
64/***********************************************************************
65 * tr_sharedInit
66 ***********************************************************************
67 *
68 **********************************************************************/
69tr_shared_t * tr_sharedInit( tr_handle_t * h )
70{
71    tr_shared_t * s = calloc( 1, sizeof( tr_shared_t ) );
72
73    s->h = h;
74    tr_lockInit( &s->lock );
75
76    s->publicPort = -1;
77    s->bindPort   = -1;
78    s->bindSocket = -1;
79    s->natpmp     = tr_natpmpInit();
80    s->upnp       = tr_upnpInit();
81    s->choking    = tr_chokingInit( h );
82
83    /* Launch the thread */
84    s->die = 0;
85    tr_threadCreate( &s->thread, SharedLoop, s, "shared" );
86
87    return s;
88}
89
90/***********************************************************************
91 * tr_sharedClose
92 ***********************************************************************
93 *
94 **********************************************************************/
95void tr_sharedClose( tr_shared_t * s )
96{
97    int ii;
98
99    /* Stop the thread */
100    s->die = 1;
101    tr_threadJoin( &s->thread );
102
103    /* Clean up */
104    for( ii = 0; ii < s->peerCount; ii++ )
105    {
106        tr_peerDestroy( s->peers[ii] );
107    }
108    if( s->bindSocket > -1 )
109    {
110        tr_netClose( s->bindSocket );
111    }
112    tr_lockClose( &s->lock );
113    tr_natpmpClose( s->natpmp );
114    tr_upnpClose( s->upnp );
115    tr_chokingClose( s->choking );
116    free( s );
117}
118
119/***********************************************************************
120 * tr_sharedLock, tr_sharedUnlock
121 ***********************************************************************
122 *
123 **********************************************************************/
124void tr_sharedLock( tr_shared_t * s )
125{
126    tr_lockLock( &s->lock );
127}
128void tr_sharedUnlock( tr_shared_t * s )
129{
130    tr_lockUnlock( &s->lock );
131}
132
133/***********************************************************************
134 * tr_sharedSetPort
135 ***********************************************************************
136 *
137 **********************************************************************/
138void tr_sharedSetPort( tr_shared_t * s, int port )
139{
140#ifdef BEOS_NETSERVER
141    /* BeOS net_server seems to be unable to set incoming connections
142     * to non-blocking. Too bad. */
143    return;
144#endif
145
146    tr_sharedLock( s );
147
148    if( port == s->bindPort )
149    {
150        tr_sharedUnlock( s );
151        return;
152    }
153    s->bindPort = port;
154
155    /* Close the previous accept socket, if any */
156    if( s->bindSocket > -1 )
157    {
158        tr_netClose( s->bindSocket );
159    }
160
161    /* Create the new one */
162    /* XXX should handle failure here in a better way */
163    s->bindSocket = tr_netBindTCP( port );
164    if( 0 > s->bindSocket )
165    {
166        /* Notify the trackers */
167        SetPublicPort( s, 0 );
168        /* Remove the forwarding for the old port */
169        tr_natpmpRemoveForwarding( s->natpmp );
170        tr_upnpRemoveForwarding( s->upnp );
171    }
172    else
173    {
174        tr_inf( "Bound listening port %d", port );
175        listen( s->bindSocket, 5 );
176        if( port != s->publicPort )
177        {
178            /* Notify the trackers */
179            SetPublicPort( s, port );
180        }
181        /* Forward the new port */
182        tr_natpmpForwardPort( s->natpmp, port );
183        tr_upnpForwardPort( s->upnp, port );
184    }
185
186    tr_sharedUnlock( s );
187}
188
189/***********************************************************************
190 * tr_sharedGetPublicPort
191 ***********************************************************************
192 *
193 **********************************************************************/
194int tr_sharedGetPublicPort( tr_shared_t * s )
195{
196    return s->publicPort;
197}
198
199/***********************************************************************
200 * tr_sharedTraversalEnable, tr_sharedTraversalStatus
201 ***********************************************************************
202 *
203 **********************************************************************/
204void tr_sharedTraversalEnable( tr_shared_t * s, int enable )
205{
206    if( enable )
207    {
208        tr_natpmpStart( s->natpmp );
209        tr_upnpStart( s->upnp );
210    }
211    else
212    {
213        tr_natpmpStop( s->natpmp );
214        tr_upnpStop( s->upnp );
215    }
216}
217
218int tr_sharedTraversalStatus( tr_shared_t * s )
219{
220    int statuses[] = {
221        TR_NAT_TRAVERSAL_MAPPED,
222        TR_NAT_TRAVERSAL_MAPPING,
223        TR_NAT_TRAVERSAL_UNMAPPING,
224        TR_NAT_TRAVERSAL_ERROR,
225        TR_NAT_TRAVERSAL_NOTFOUND,
226        TR_NAT_TRAVERSAL_DISABLED,
227        -1,
228    };
229    int natpmp, upnp, ii;
230
231    natpmp = tr_natpmpStatus( s->natpmp );
232    upnp = tr_upnpStatus( s->upnp );
233
234    for( ii = 0; 0 <= statuses[ii]; ii++ )
235    {
236        if( statuses[ii] == natpmp || statuses[ii] == upnp )
237        {
238            return statuses[ii];
239        }
240    }
241
242    assert( 0 );
243
244    return TR_NAT_TRAVERSAL_ERROR;
245
246}
247
248/***********************************************************************
249 * tr_sharedSetLimit
250 **********************************************************************/
251void tr_sharedSetLimit( tr_shared_t * s, int limit )
252{
253    tr_chokingSetLimit( s->choking, limit );
254}
255
256
257/***********************************************************************
258 * Local functions
259 **********************************************************************/
260
261/***********************************************************************
262 * SharedLoop
263 **********************************************************************/
264static void SharedLoop( void * _s )
265{
266    tr_shared_t * s = _s;
267    uint64_t      date1, date2, lastchoke = 0;
268    int           newPort;
269
270    tr_sharedLock( s );
271
272    while( !s->die )
273    {
274        date1 = tr_date();
275
276        /* NAT-PMP and UPnP pulses */
277        newPort = -1;
278        tr_natpmpPulse( s->natpmp, &newPort );
279        if( 0 < newPort && newPort != s->publicPort )
280        {
281            SetPublicPort( s, newPort );
282        }
283        tr_upnpPulse( s->upnp );
284
285        /* Handle incoming connections */
286        AcceptPeers( s );
287        ReadPeers( s );
288        DispatchPeers( s );
289
290        /* Update choking every second */
291        if( date1 > lastchoke + 1000 )
292        {
293            tr_chokingPulse( s->choking );
294            lastchoke = date1;
295        }
296
297        tr_swiftPulse ( s->h );
298
299        /* Wait up to 20 ms */
300        date2 = tr_date();
301        if( date2 < date1 + 20 )
302        {
303            tr_sharedUnlock( s );
304            tr_wait( date1 + 20 - date2 );
305            tr_sharedLock( s );
306        }
307    }
308
309    tr_sharedUnlock( s );
310}
311
312/***********************************************************************
313 * SetPublicPort
314 **********************************************************************/
315static void SetPublicPort( tr_shared_t * s, int port )
316{
317    tr_handle_t * h = s->h;
318    tr_torrent_t * tor;
319
320    s->publicPort = port;
321
322    for( tor = h->torrentList; tor; tor = tor->next )
323    {
324        tr_torrentWriterLock( tor );
325        tor->publicPort = port;
326        tr_torrentWriterUnlock( tor );
327    }
328}
329
330/***********************************************************************
331 * AcceptPeers
332 ***********************************************************************
333 * Check incoming connections and add the peers to our local list
334 **********************************************************************/
335static void AcceptPeers( tr_shared_t * s )
336{
337    int socket;
338    struct in_addr addr;
339
340    for( ;; )
341    {
342        if( s->bindSocket < 0 || s->peerCount >= MAX_PEER_COUNT )
343        {
344            break;
345        }
346
347        socket = tr_netAccept( s->bindSocket, &addr, NULL );
348        if( socket < 0 )
349        {
350            break;
351        }
352        s->peers[s->peerCount++] = tr_peerInit( addr, 0, socket,
353                                                TR_PEER_FROM_INCOMING );
354    }
355}
356
357/***********************************************************************
358 * ReadPeers
359 ***********************************************************************
360 * Try to read handshakes
361 **********************************************************************/
362static void ReadPeers( tr_shared_t * s )
363{
364    int ii;
365
366    for( ii = 0; ii < s->peerCount; )
367    {
368        if( tr_peerRead( s->peers[ii] ) )
369        {
370            tr_peerDestroy( s->peers[ii] );
371            s->peerCount--;
372            memmove( &s->peers[ii], &s->peers[ii+1],
373                     ( s->peerCount - ii ) * sizeof( tr_peer_t * ) );
374            continue;
375        }
376        ii++;
377    }
378}
379
380/***********************************************************************
381 * DispatchPeers
382 ***********************************************************************
383 * If we got a handshake, try to find the torrent for this peer
384 **********************************************************************/
385static void DispatchPeers( tr_shared_t * s )
386{
387    tr_handle_t * h = s->h;
388    int ii;
389    const uint64_t now = tr_date();
390
391    for( ii = 0; ii < s->peerCount; )
392    {
393        const uint8_t * hash = tr_peerHash( s->peers[ii] );
394
395        if( !hash && now > tr_peerDate( s->peers[ii] ) + 10000 )
396        {
397            /* 10 seconds and no handshake, drop it */
398            tr_peerDestroy( s->peers[ii] );
399            goto removePeer;
400        }
401        if( hash )
402        {
403            tr_torrent_t * tor;
404
405            for( tor = h->torrentList; tor; tor = tor->next )
406            {
407                tr_torrentWriterLock( tor );
408                if( tor->cpStatus != TR_RUN_RUNNING )
409                {
410                    tr_torrentWriterUnlock( tor );
411                    continue;
412                }
413
414                if( !memcmp( tor->info.hash, hash, SHA_DIGEST_LENGTH ) )
415                {
416                    /* Found it! */
417                    tr_torrentAttachPeer( tor, s->peers[ii] );
418                    tr_torrentWriterUnlock( tor );
419                    goto removePeer;
420                }
421                tr_torrentWriterUnlock( tor );
422            }
423
424            /* Couldn't find a torrent, we probably removed it */
425            tr_peerDestroy( s->peers[ii] );
426            goto removePeer;
427        }
428        ii++;
429        continue;
430
431removePeer:
432        s->peerCount--;
433        memmove( &s->peers[ii], &s->peers[ii+1],
434                ( s->peerCount - ii ) * sizeof( tr_peer_t * ) );
435    }
436}
437
Note: See TracBrowser for help on using the repository browser.