source: trunk/libtransmission/verify.c @ 8931

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

(trunk libT) #2335: when verifying local data, pause a few msec between pieces to give the system some breathing room and other processes a shot at the disk IO

  • Property svn:keywords set to Date Rev Author Id
File size: 8.7 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 8931 2009-08-14 20:55:22Z 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
38/* #define STOPWATCH */
39
40static tr_bool
41verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
42{
43    SHA_CTX sha;
44    int fd = -1;
45    int64_t filePos = 0;
46    tr_bool changed = 0;
47    tr_bool hadPiece = 0;
48    uint32_t piecePos = 0;
49    uint32_t pieceBytesRead = 0;
50    tr_file_index_t fileIndex = 0;
51    tr_piece_index_t pieceIndex = 0;
52    const int64_t buflen = tor->info.pieceSize;
53    uint8_t * buffer = tr_new( uint8_t, buflen );
54#ifdef STOPWATCH
55    time_t now = time( NULL );
56#endif
57
58    SHA1_Init( &sha );
59
60    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
61    {
62        int64_t leftInPiece;
63        int64_t leftInFile;
64        int64_t bytesThisPass;
65        const tr_file * file = &tor->info.files[fileIndex];
66
67        /* if we're starting a new piece... */
68        if( piecePos == 0 )
69        {
70            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
71            /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */
72        }
73
74        /* if we're starting a new file... */
75        if( !filePos && (fd<0) )
76        {
77            char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
78            fd = tr_open_file_for_scanning( filename );
79            /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */
80            tr_free( filename );
81        }
82
83        /* figure out how much we can read this pass */
84        leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos;
85        leftInFile = file->length - filePos;
86        bytesThisPass = MIN( leftInFile, leftInPiece );
87        bytesThisPass = MIN( bytesThisPass, buflen );
88        /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */
89
90        /* read a bit */
91        if( (fd>=0) && tr_lseek( fd, filePos, SEEK_SET ) != -1 ) {
92            const int64_t numRead = read( fd, buffer, bytesThisPass );
93            if( numRead > 0 )
94                pieceBytesRead += numRead;
95            if( numRead == bytesThisPass )
96                SHA1_Update( &sha, buffer, numRead );
97        }
98
99        /* move our offsets */
100        leftInPiece -= bytesThisPass;
101        leftInFile -= bytesThisPass;
102        piecePos += bytesThisPass;
103        filePos += bytesThisPass;
104
105        /* if we're finishing a piece... */
106        if( leftInPiece == 0 )
107        {
108            tr_bool hasPiece;
109            uint8_t hash[SHA_DIGEST_LENGTH];
110
111            SHA1_Final( hash, &sha );
112            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
113            /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */
114
115            if( hasPiece ) {
116                tr_torrentSetHasPiece( tor, pieceIndex, TRUE );
117                if( !hadPiece )
118                    changed = TRUE;
119            } else if( hadPiece ) {
120                tr_torrentSetHasPiece( tor, pieceIndex, FALSE );
121                changed = TRUE;
122            }
123            tr_torrentSetPieceChecked( tor, pieceIndex, TRUE );
124            tor->anyDate = time( NULL );
125
126            /* going full-throttle on a verify can choke other processes'
127             * disk IO, so wait a fwe msec between pieces.
128             * The msec is arbitrary, and the "if" clause is to make sure we
129             * don't slow down verification of files that don't exist */
130            if( pieceBytesRead == tr_torPieceCountBytes( tor, pieceIndex ) )
131                tr_wait( 50 );
132
133            SHA1_Init( &sha );
134            ++pieceIndex;
135            piecePos = 0;
136            pieceBytesRead = 0;
137        }
138
139        /* if we're finishing a file... */
140        if( leftInFile == 0 )
141        {
142            /* fprintf( stderr, "closing file\n" ); */
143            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
144            ++fileIndex;
145            filePos = 0;
146        }
147    }
148
149    /* cleanup */
150    if( fd >= 0 )
151        tr_close_file( fd );
152    tr_free( buffer );
153
154#ifdef STOPWATCH
155{
156    time_t now2 = time( NULL );
157    fprintf( stderr, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)\n",
158             (int)(now2-now), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(now2-now))) );
159}
160#endif
161
162    return changed;
163}
164
165/***
166****
167***/
168
169struct verify_node
170{
171    tr_torrent *         torrent;
172    tr_verify_done_cb    verify_done_cb;
173};
174
175static void
176fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
177{
178    assert( tr_isTorrent( tor ) );
179
180    if( verify_done_cb )
181        verify_done_cb( tor );
182}
183
184static struct verify_node currentNode;
185static tr_list * verifyList = NULL;
186static tr_thread * verifyThread = NULL;
187static tr_bool stopCurrent = FALSE;
188
189static tr_lock*
190getVerifyLock( void )
191{
192    static tr_lock * lock = NULL;
193
194    if( lock == NULL )
195        lock = tr_lockNew( );
196    return lock;
197}
198
199static void
200verifyThreadFunc( void * unused UNUSED )
201{
202    for( ;; )
203    {
204        int                  changed = 0;
205        tr_torrent         * tor;
206        struct verify_node * node;
207
208        tr_lockLock( getVerifyLock( ) );
209        stopCurrent = FALSE;
210        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
211        if( node == NULL )
212        {
213            currentNode.torrent = NULL;
214            break;
215        }
216
217        currentNode = *node;
218        tor = currentNode.torrent;
219        tr_list_remove_data( &verifyList, node );
220        tr_free( node );
221        tr_lockUnlock( getVerifyLock( ) );
222
223        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
224        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
225        changed = verifyTorrent( tor, &stopCurrent );
226        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
227        assert( tr_isTorrent( tor ) );
228
229        if( !stopCurrent )
230        {
231            if( changed )
232                tr_torrentSetDirty( tor );
233            fireCheckDone( tor, currentNode.verify_done_cb );
234        }
235    }
236
237    verifyThread = NULL;
238    tr_lockUnlock( getVerifyLock( ) );
239}
240
241void
242tr_verifyAdd( tr_torrent *      tor,
243              tr_verify_done_cb verify_done_cb )
244{
245    const int uncheckedCount = tr_torrentCountUncheckedPieces( tor );
246
247    assert( tr_isTorrent( tor ) );
248
249    if( !uncheckedCount )
250    {
251        /* doesn't need to be checked... */
252        fireCheckDone( tor, verify_done_cb );
253    }
254    else
255    {
256        struct verify_node * node;
257
258        tr_torinf( tor, "%s", _( "Queued for verification" ) );
259
260        node = tr_new( struct verify_node, 1 );
261        node->torrent = tor;
262        node->verify_done_cb = verify_done_cb;
263
264        tr_lockLock( getVerifyLock( ) );
265        tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
266        tr_list_append( &verifyList, node );
267        if( verifyThread == NULL )
268            verifyThread = tr_threadNew( verifyThreadFunc, NULL );
269        tr_lockUnlock( getVerifyLock( ) );
270    }
271}
272
273static int
274compareVerifyByTorrent( const void * va,
275                        const void * vb )
276{
277    const struct verify_node * a = va;
278    const tr_torrent *         b = vb;
279
280    return a->torrent - b;
281}
282
283tr_bool
284tr_verifyInProgress( const tr_torrent * tor )
285{
286    tr_bool found = FALSE;
287    tr_lock * lock = getVerifyLock( );
288    tr_lockLock( lock );
289
290    assert( tr_isTorrent( tor ) );
291
292    found = ( tor == currentNode.torrent )
293         || ( tr_list_find( verifyList, tor, compareVerifyByTorrent ) != NULL );
294
295    tr_lockUnlock( lock );
296    return found;
297}
298
299void
300tr_verifyRemove( tr_torrent * tor )
301{
302    tr_lock * lock = getVerifyLock( );
303    tr_lockLock( lock );
304
305    assert( tr_isTorrent( tor ) );
306
307    if( tor == currentNode.torrent )
308    {
309        stopCurrent = TRUE;
310        while( stopCurrent )
311        {
312            tr_lockUnlock( lock );
313            tr_wait( 100 );
314            tr_lockLock( lock );
315        }
316    }
317    else
318    {
319        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
320        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
321    }
322
323    tr_lockUnlock( lock );
324}
325
326void
327tr_verifyClose( tr_session * session UNUSED )
328{
329    tr_lockLock( getVerifyLock( ) );
330
331    stopCurrent = TRUE;
332    tr_list_free( &verifyList, tr_free );
333
334    tr_lockUnlock( getVerifyLock( ) );
335}
Note: See TracBrowser for help on using the repository browser.