source: trunk/libtransmission/verify.c @ 9044

Last change on this file since 9044 was 9044, checked in by charles, 12 years ago

(trunk libT) #2383: Faster verification for local data with small piece size

  • Property svn:keywords set to Date Rev Author Id
File size: 8.8 KB
Line 
1/*
2 * This file Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: verify.c 9044 2009-09-06 14:05:06Z charles $
11 */
12
13#include <unistd.h> /* S_ISREG */
14#include <sys/stat.h>
15
16#ifdef HAVE_POSIX_FADVISE
17 #define _XOPEN_SOURCE 600
18 #include <fcntl.h> /* posix_fadvise() */
19#endif
20
21#include <openssl/sha.h>
22
23#include "transmission.h"
24#include "completion.h"
25#include "fdlimit.h"
26#include "inout.h"
27#include "list.h"
28#include "platform.h"
29#include "torrent.h"
30#include "utils.h" /* tr_buildPath */
31#include "verify.h"
32
33
34/***
35****
36***/
37
38enum
39{
40    MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 200
41};
42
43/* #define STOPWATCH */
44
45static tr_bool
46verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
47{
48    SHA_CTX sha;
49    int fd = -1;
50    int64_t filePos = 0;
51    tr_bool changed = 0;
52    tr_bool hadPiece = 0;
53    time_t lastSleptAt = 0;
54    uint32_t piecePos = 0;
55    uint32_t pieceBytesRead = 0;
56    tr_file_index_t fileIndex = 0;
57    tr_piece_index_t pieceIndex = 0;
58    const int64_t buflen = tor->info.pieceSize;
59    uint8_t * buffer = tr_new( uint8_t, buflen );
60#ifdef STOPWATCH
61    const time_t begin = time( NULL );
62#endif
63
64    SHA1_Init( &sha );
65
66    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
67    {
68        int64_t leftInPiece;
69        int64_t leftInFile;
70        int64_t bytesThisPass;
71        const tr_file * file = &tor->info.files[fileIndex];
72
73        /* if we're starting a new piece... */
74        if( piecePos == 0 )
75        {
76            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
77            /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */
78        }
79
80        /* if we're starting a new file... */
81        if( !filePos && (fd<0) )
82        {
83            char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
84            fd = tr_open_file_for_scanning( filename );
85            /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */
86            tr_free( filename );
87        }
88
89        /* figure out how much we can read this pass */
90        leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos;
91        leftInFile = file->length - filePos;
92        bytesThisPass = MIN( leftInFile, leftInPiece );
93        bytesThisPass = MIN( bytesThisPass, buflen );
94        /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */
95
96        /* read a bit */
97        if( (fd>=0) && tr_lseek( fd, filePos, SEEK_SET ) != -1 ) {
98            const int64_t numRead = read( fd, buffer, bytesThisPass );
99            if( numRead > 0 )
100                pieceBytesRead += numRead;
101            if( numRead == bytesThisPass )
102                SHA1_Update( &sha, buffer, numRead );
103        }
104
105        /* move our offsets */
106        leftInPiece -= bytesThisPass;
107        leftInFile -= bytesThisPass;
108        piecePos += bytesThisPass;
109        filePos += bytesThisPass;
110
111        /* if we're finishing a piece... */
112        if( leftInPiece == 0 )
113        {
114            time_t now;
115            tr_bool hasPiece;
116            uint8_t hash[SHA_DIGEST_LENGTH];
117
118            SHA1_Final( hash, &sha );
119            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
120            /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */
121
122            if( hasPiece ) {
123                tr_torrentSetHasPiece( tor, pieceIndex, TRUE );
124                if( !hadPiece )
125                    changed = TRUE;
126            } else if( hadPiece ) {
127                tr_torrentSetHasPiece( tor, pieceIndex, FALSE );
128                changed = TRUE;
129            }
130            tr_torrentSetPieceChecked( tor, pieceIndex, TRUE );
131            now = time( NULL );
132            tor->anyDate = now;
133
134            /* sleeping even just a few msec per second goes a long
135             * way towards reducing IO load... */
136            if( lastSleptAt != now ) {
137                lastSleptAt = now;
138                tr_wait( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY );
139            }
140
141            SHA1_Init( &sha );
142            ++pieceIndex;
143            piecePos = 0;
144            pieceBytesRead = 0;
145        }
146
147        /* if we're finishing a file... */
148        if( leftInFile == 0 )
149        {
150            /* fprintf( stderr, "closing file\n" ); */
151            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
152            ++fileIndex;
153            filePos = 0;
154        }
155    }
156
157    /* cleanup */
158    if( fd >= 0 )
159        tr_close_file( fd );
160    tr_free( buffer );
161
162#ifdef STOPWATCH
163{
164    const time_t end = time( NULL );
165    fprintf( stderr, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)\n",
166             (int)(end-begin), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(end-begin))) );
167}
168#endif
169
170    return changed;
171}
172
173/***
174****
175***/
176
177struct verify_node
178{
179    tr_torrent *         torrent;
180    tr_verify_done_cb    verify_done_cb;
181};
182
183static void
184fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
185{
186    assert( tr_isTorrent( tor ) );
187
188    if( verify_done_cb )
189        verify_done_cb( tor );
190}
191
192static struct verify_node currentNode;
193static tr_list * verifyList = NULL;
194static tr_thread * verifyThread = NULL;
195static tr_bool stopCurrent = FALSE;
196
197static tr_lock*
198getVerifyLock( void )
199{
200    static tr_lock * lock = NULL;
201
202    if( lock == NULL )
203        lock = tr_lockNew( );
204    return lock;
205}
206
207static void
208verifyThreadFunc( void * unused UNUSED )
209{
210    for( ;; )
211    {
212        int                  changed = 0;
213        tr_torrent         * tor;
214        struct verify_node * node;
215
216        tr_lockLock( getVerifyLock( ) );
217        stopCurrent = FALSE;
218        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
219        if( node == NULL )
220        {
221            currentNode.torrent = NULL;
222            break;
223        }
224
225        currentNode = *node;
226        tor = currentNode.torrent;
227        tr_list_remove_data( &verifyList, node );
228        tr_free( node );
229        tr_lockUnlock( getVerifyLock( ) );
230
231        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
232        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
233        changed = verifyTorrent( tor, &stopCurrent );
234        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
235        assert( tr_isTorrent( tor ) );
236
237        if( !stopCurrent )
238        {
239            if( changed )
240                tr_torrentSetDirty( tor );
241            fireCheckDone( tor, currentNode.verify_done_cb );
242        }
243    }
244
245    verifyThread = NULL;
246    tr_lockUnlock( getVerifyLock( ) );
247}
248
249void
250tr_verifyAdd( tr_torrent *      tor,
251              tr_verify_done_cb verify_done_cb )
252{
253    const int uncheckedCount = tr_torrentCountUncheckedPieces( tor );
254
255    assert( tr_isTorrent( tor ) );
256
257    if( !uncheckedCount )
258    {
259        /* doesn't need to be checked... */
260        fireCheckDone( tor, verify_done_cb );
261    }
262    else
263    {
264        struct verify_node * node;
265
266        tr_torinf( tor, "%s", _( "Queued for verification" ) );
267
268        node = tr_new( struct verify_node, 1 );
269        node->torrent = tor;
270        node->verify_done_cb = verify_done_cb;
271
272        tr_lockLock( getVerifyLock( ) );
273        tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
274        tr_list_append( &verifyList, node );
275        if( verifyThread == NULL )
276            verifyThread = tr_threadNew( verifyThreadFunc, NULL );
277        tr_lockUnlock( getVerifyLock( ) );
278    }
279}
280
281static int
282compareVerifyByTorrent( const void * va,
283                        const void * vb )
284{
285    const struct verify_node * a = va;
286    const tr_torrent *         b = vb;
287
288    return a->torrent - b;
289}
290
291tr_bool
292tr_verifyInProgress( const tr_torrent * tor )
293{
294    tr_bool found = FALSE;
295    tr_lock * lock = getVerifyLock( );
296    tr_lockLock( lock );
297
298    assert( tr_isTorrent( tor ) );
299
300    found = ( tor == currentNode.torrent )
301         || ( tr_list_find( verifyList, tor, compareVerifyByTorrent ) != NULL );
302
303    tr_lockUnlock( lock );
304    return found;
305}
306
307void
308tr_verifyRemove( tr_torrent * tor )
309{
310    tr_lock * lock = getVerifyLock( );
311    tr_lockLock( lock );
312
313    assert( tr_isTorrent( tor ) );
314
315    if( tor == currentNode.torrent )
316    {
317        stopCurrent = TRUE;
318        while( stopCurrent )
319        {
320            tr_lockUnlock( lock );
321            tr_wait( 100 );
322            tr_lockLock( lock );
323        }
324    }
325    else
326    {
327        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
328        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
329    }
330
331    tr_lockUnlock( lock );
332}
333
334void
335tr_verifyClose( tr_session * session UNUSED )
336{
337    tr_lockLock( getVerifyLock( ) );
338
339    stopCurrent = TRUE;
340    tr_list_free( &verifyList, tr_free );
341
342    tr_lockUnlock( getVerifyLock( ) );
343}
Note: See TracBrowser for help on using the repository browser.