source: trunk/libtransmission/peermessages.h @ 79

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

Workarounded a race condition

File size: 9.5 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 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/***********************************************************************
24 * This file handles all outgoing messages
25 **********************************************************************/
26
27static uint8_t * messagesPending( tr_peer_t * peer, int * size )
28{
29    if( peer->outBlockSending || peer->outMessagesPos < 1 )
30    {
31        return NULL;
32    }
33
34    *size = MIN( peer->outMessagesPos, 1024 );
35
36    return peer->outMessages;
37}
38
39static void messagesSent( tr_peer_t * peer, int size )
40{
41    peer->outMessagesPos -= size;
42    memmove( peer->outMessages, &peer->outMessages[size],
43             peer->outMessagesPos );
44}
45
46static uint8_t * blockPending( tr_torrent_t * tor, tr_peer_t * peer,
47                               int * size )
48{
49    if( !peer->outBlockLoaded )
50    {
51        uint8_t * p;
52        tr_request_t * r;
53
54        if( peer->amChoking || peer->outRequestCount < 1 )
55        {
56            /* No piece to send */
57            return NULL;
58        }
59
60        /* We need to load the block for the next request */
61        r = &peer->outRequests[0];
62
63        /* Sanity check */
64        if( !tr_cpPieceIsComplete( tor->completion, r->index ) )
65        {
66            /* We have been asked for something we don't have, buggy client?
67               Let's just drop this request */
68            tr_inf( "Block %d/%d/%d was requested but we don't have it",
69                    r->index, r->begin, r->length );
70            (peer->outRequestCount)--;
71            memmove( &peer->outRequests[0], &peer->outRequests[1],
72                     peer->outRequestCount * sizeof( tr_request_t ) );
73            return NULL;
74        }
75       
76        p = (uint8_t *) peer->outBlock;
77
78        TR_HTONL( 9 + r->length, p );
79        p[4] = 7;
80        TR_HTONL( r->index, p + 5 );
81        TR_HTONL( r->begin, p + 9 );
82
83        tr_ioRead( tor->io, r->index, r->begin, r->length, &p[13] );
84
85        peer_dbg( "SEND piece %d/%d (%d bytes)",
86                  r->index, r->begin, r->length );
87
88        peer->outBlockSize   = 13 + r->length;
89        peer->outBlockLoaded = 1;
90
91        (peer->outRequestCount)--;
92        memmove( &peer->outRequests[0], &peer->outRequests[1],
93                 peer->outRequestCount * sizeof( tr_request_t ) );
94    }
95
96    *size = MIN( 1024, peer->outBlockSize );
97
98    return (uint8_t *) peer->outBlock;
99}
100
101static void blockSent( tr_peer_t * peer, int size )
102{
103    peer->outBlockSize -= size;
104    memmove( peer->outBlock, &peer->outBlock[size], peer->outBlockSize );
105
106    if( peer->outBlockSize > 0 )
107    {
108        /* We can't send messages until we are done sending the block */
109        peer->outBlockSending = 1;
110    }
111    else
112    {
113        /* Block fully sent */
114        peer->outBlockSending = 0;
115        peer->outBlockLoaded  = 0;
116    }
117}
118
119static uint8_t * getPointerForSize( tr_peer_t * peer, int size )
120{
121    uint8_t * p;
122
123    if( peer->outMessagesPos + size > peer->outMessagesSize )
124    {
125        peer->outMessagesSize = peer->outMessagesPos + size;
126        peer->outMessages     = realloc( peer->outMessages,
127                                         peer->outMessagesSize );
128    }
129
130    p                     = &peer->outMessages[peer->outMessagesPos];
131    peer->outMessagesPos += size;
132
133    return p;
134}
135
136/***********************************************************************
137 * sendKeepAlive
138 ***********************************************************************
139 *
140 **********************************************************************/
141static void sendKeepAlive( tr_peer_t * peer )
142{
143    uint8_t * p;
144
145    p = getPointerForSize( peer, 4 );
146
147    TR_HTONL( 0, p );
148
149    peer_dbg( "SEND keep-alive" );
150}
151
152
153/***********************************************************************
154 * sendChoke
155 ***********************************************************************
156 *
157 **********************************************************************/
158static void sendChoke( tr_peer_t * peer, int yes )
159{
160    uint8_t * p;
161
162    p = getPointerForSize( peer, 5 );
163
164    TR_HTONL( 1, p );
165    p[4] = yes ? 0 : 1;
166
167    peer->amChoking = yes;
168
169    if( !yes )
170    {
171        /* Drop older requests from the last time it was unchoked,
172           if any */
173        peer->outRequestCount = 0;
174    }
175
176    peer_dbg( "SEND %schoke", yes ? "" : "un" );
177}
178
179/***********************************************************************
180 * sendInterest
181 ***********************************************************************
182 *
183 **********************************************************************/
184static void sendInterest( tr_peer_t * peer, int yes )
185{
186    uint8_t * p;
187
188    p = getPointerForSize( peer, 5 );
189   
190    TR_HTONL( 1, p );
191    p[4] = yes ? 2 : 3;
192
193    peer->amInterested = yes;
194
195    peer_dbg( "SEND %sinterested", yes ? "" : "un" );
196}
197
198/***********************************************************************
199 * sendHave
200 ***********************************************************************
201 *
202 **********************************************************************/
203static void sendHave( tr_peer_t * peer, int piece )
204{
205    uint8_t * p;
206
207    p = getPointerForSize( peer, 9 );
208
209    TR_HTONL( 5, &p[0] );
210    p[4] = 4;
211    TR_HTONL( piece, &p[5] );
212
213    peer_dbg( "SEND have %d", piece );
214}
215
216/***********************************************************************
217 * sendBitfield
218 ***********************************************************************
219 * Builds a 'bitfield' message:
220 *  - size = 5 + X (4 bytes)
221 *  - id   = 5     (1 byte)
222 *  - bitfield     (X bytes)
223 **********************************************************************/
224static void sendBitfield( tr_torrent_t * tor, tr_peer_t * peer )
225{
226    uint8_t * p;
227    int       bitfieldSize = ( tor->info.pieceCount + 7 ) / 8;
228
229    p = getPointerForSize( peer, 5 + bitfieldSize );
230
231    TR_HTONL( 1 + bitfieldSize, p );
232    p[4] = 5;
233    memcpy( &p[5], tr_cpPieceBitfield( tor->completion ), bitfieldSize );
234
235    peer_dbg( "SEND bitfield" );
236}
237
238/***********************************************************************
239 * sendRequest
240 ***********************************************************************
241 *
242 **********************************************************************/
243static void sendRequest( tr_torrent_t * tor, tr_peer_t * peer, int block )
244{
245    tr_info_t * inf = &tor->info;
246    tr_request_t * r;
247    uint8_t * p;
248
249    /* Get the piece the block is a part of, its position in the piece
250       and its size */
251    r         = &peer->inRequests[peer->inRequestCount];
252    r->index  = block / ( inf->pieceSize / tor->blockSize );
253    r->begin  = ( block % ( inf->pieceSize / tor->blockSize ) ) *
254                    tor->blockSize;
255    r->length = tor->blockSize;
256    if( block == tor->blockCount - 1 )
257    {
258        int lastSize = inf->totalSize % tor->blockSize;
259        if( lastSize )
260        {
261            r->length = lastSize;
262        }
263    }
264    (peer->inRequestCount)++;
265
266    /* Build the "ask" message */
267    p = getPointerForSize( peer, 17 );
268
269    TR_HTONL( 13, p );
270    p[4] = 6;
271    TR_HTONL( r->index, p + 5 );
272    TR_HTONL( r->begin, p + 9 );
273    TR_HTONL( r->length, p + 13 );
274
275    tr_cpDownloaderAdd( tor->completion, block );
276
277    peer_dbg( "SEND request %d/%d (%d bytes)",
278              r->index, r->begin, r->length );
279}
280
281/***********************************************************************
282 * sendCancel
283 ***********************************************************************
284 *
285 **********************************************************************/
286static void sendCancel( tr_torrent_t * tor, int block )
287{
288    int i, j;
289    uint8_t * p;
290    tr_peer_t * peer;
291    tr_request_t * r;
292
293    for( i = 0; i < tor->peerCount; i++ )
294    {
295        peer = tor->peers[i];
296
297        for( j = 1; j < peer->inRequestCount; j++ )
298        {
299            r = &peer->inRequests[j];
300
301            if( block != tr_block( r->index, r->begin ) )
302            {
303                continue;
304            }
305
306            p = getPointerForSize( peer, 17 );
307       
308            /* Build the "cancel" message */
309            TR_HTONL( 13, p );
310            p[4] = 8;
311            TR_HTONL( r->index,  p + 5  );
312            TR_HTONL( r->begin,  p + 9  );
313            TR_HTONL( r->length, p + 13 );
314
315            peer_dbg( "SEND cancel %d/%d (%d bytes)",
316                      r->index, r->begin, r->length );
317
318            (peer->inRequestCount)--;
319            memmove( &peer->inRequests[j], &peer->inRequests[j+1],
320                     ( peer->inRequestCount - j ) * sizeof( tr_request_t ) );
321            break;
322        }
323    }
324}
Note: See TracBrowser for help on using the repository browser.