source: trunk/libtransmission/verify.c @ 11023

Last change on this file since 11023 was 11023, checked in by charles, 11 years ago

(trunk libT) #3427 "use shortest-job-first scheduling for verifying local data" -- patch from wateenellende and sadface

  • Property svn:keywords set to Date Rev Author Id
File size: 10.3 KB
Line 
1/*
2 * This file Copyright (C) 2007-2010 Mnemosyne LLC
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 11023 2010-07-19 14:44:24Z 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#endif
19#if defined(HAVE_POSIX_FADVISE) || defined(SYS_DARWIN)
20 #include <fcntl.h> /* posix_fadvise() / fcntl() */
21#endif
22
23#include <openssl/sha.h>
24
25#include "transmission.h"
26#include "completion.h"
27#include "fdlimit.h"
28#include "inout.h"
29#include "list.h"
30#include "platform.h"
31#include "torrent.h"
32#include "utils.h" /* tr_buildPath */
33#include "verify.h"
34
35
36/***
37****
38***/
39
40enum
41{
42    MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
43};
44
45/* #define STOPWATCH */
46
47static tr_bool
48verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
49{
50    SHA_CTX sha;
51    int fd = -1;
52    int64_t filePos = 0;
53    tr_bool changed = 0;
54    tr_bool hadPiece = 0;
55    time_t lastSleptAt = 0;
56    uint32_t piecePos = 0;
57    uint32_t pieceBytesRead = 0;
58    tr_file_index_t fileIndex = 0;
59    tr_file_index_t prevFileIndex = !fileIndex;
60    tr_piece_index_t pieceIndex = 0;
61    const time_t begin = tr_time( );
62    time_t end;
63    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
64    uint8_t * buffer = tr_valloc( buflen );
65
66    tr_torrentUncheck( tor );
67
68    SHA1_Init( &sha );
69
70    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
71    {
72        uint32_t leftInPiece;
73        uint32_t bytesThisPass;
74        uint64_t leftInFile;
75        const tr_file * file = &tor->info.files[fileIndex];
76
77        /* if we're starting a new piece... */
78        if( piecePos == 0 )
79        {
80            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
81            /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */
82        }
83
84        /* if we're starting a new file... */
85        if( !filePos && (fd<0) && (fileIndex!=prevFileIndex) )
86        {
87            char * filename = tr_torrentFindFile( tor, fileIndex );
88            fd = filename == NULL ? -1 : tr_open_file_for_scanning( filename );
89            /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */
90            tr_free( filename );
91            prevFileIndex = fileIndex;
92        }
93
94        /* figure out how much we can read this pass */
95        leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos;
96        leftInFile = file->length - filePos;
97        bytesThisPass = MIN( leftInFile, leftInPiece );
98        bytesThisPass = MIN( bytesThisPass, buflen );
99        /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */
100
101        /* read a bit */
102        if( fd >= 0 ) {
103            const ssize_t numRead = tr_pread( fd, buffer, bytesThisPass, filePos );
104            if( numRead == (ssize_t)bytesThisPass )
105                SHA1_Update( &sha, buffer, numRead );
106            if( numRead > 0 ) {
107                pieceBytesRead += numRead;
108#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
109                posix_fadvise( fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED );
110#endif
111            }
112        }
113
114        /* move our offsets */
115        leftInPiece -= bytesThisPass;
116        leftInFile -= bytesThisPass;
117        piecePos += bytesThisPass;
118        filePos += bytesThisPass;
119
120        /* if we're finishing a piece... */
121        if( leftInPiece == 0 )
122        {
123            time_t now;
124            tr_bool hasPiece;
125            uint8_t hash[SHA_DIGEST_LENGTH];
126
127            SHA1_Final( hash, &sha );
128            hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH );
129            /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */
130
131            if( hasPiece ) {
132                tr_torrentSetHasPiece( tor, pieceIndex, TRUE );
133                if( !hadPiece )
134                    changed = TRUE;
135            } else if( hadPiece ) {
136                tr_torrentSetHasPiece( tor, pieceIndex, FALSE );
137                changed = TRUE;
138            }
139            tr_torrentSetPieceChecked( tor, pieceIndex, TRUE );
140            now = tr_time( );
141            tor->anyDate = now;
142
143            /* sleeping even just a few msec per second goes a long
144             * way towards reducing IO load... */
145            if( lastSleptAt != now ) {
146                lastSleptAt = now;
147                tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY );
148            }
149
150            SHA1_Init( &sha );
151            ++pieceIndex;
152            piecePos = 0;
153            pieceBytesRead = 0;
154        }
155
156        /* if we're finishing a file... */
157        if( leftInFile == 0 )
158        {
159            /* fprintf( stderr, "closing file\n" ); */
160            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
161            ++fileIndex;
162            filePos = 0;
163        }
164    }
165
166    /* cleanup */
167    if( fd >= 0 )
168        tr_close_file( fd );
169    free( buffer );
170
171    /* stopwatch */
172    end = tr_time( );
173    tr_tordbg( tor, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
174               (int)(end-begin), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(end-begin))) );
175
176    return changed;
177}
178
179/***
180****
181***/
182
183struct verify_node
184{
185    tr_torrent *         torrent;
186    tr_verify_done_cb    verify_done_cb;
187    uint64_t             current_size;
188};
189
190static void
191fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
192{
193    assert( tr_isTorrent( tor ) );
194
195    if( verify_done_cb )
196        verify_done_cb( tor );
197}
198
199static struct verify_node currentNode;
200static tr_list * verifyList = NULL;
201static tr_thread * verifyThread = NULL;
202static tr_bool stopCurrent = FALSE;
203
204static tr_lock*
205getVerifyLock( void )
206{
207    static tr_lock * lock = NULL;
208
209    if( lock == NULL )
210        lock = tr_lockNew( );
211    return lock;
212}
213
214static void
215verifyThreadFunc( void * unused UNUSED )
216{
217    for( ;; )
218    {
219        int                  changed = 0;
220        tr_torrent         * tor;
221        struct verify_node * node;
222
223        tr_lockLock( getVerifyLock( ) );
224        stopCurrent = FALSE;
225        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
226        if( node == NULL )
227        {
228            currentNode.torrent = NULL;
229            break;
230        }
231
232        currentNode = *node;
233        tor = currentNode.torrent;
234        tr_list_remove_data( &verifyList, node );
235        tr_free( node );
236        tr_lockUnlock( getVerifyLock( ) );
237
238        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
239        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
240        changed = verifyTorrent( tor, &stopCurrent );
241        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
242        assert( tr_isTorrent( tor ) );
243
244        if( !stopCurrent )
245        {
246            if( changed )
247                tr_torrentSetDirty( tor );
248            fireCheckDone( tor, currentNode.verify_done_cb );
249        }
250    }
251
252    verifyThread = NULL;
253    tr_lockUnlock( getVerifyLock( ) );
254}
255
256static uint64_t
257getCurrentSize( tr_torrent * tor )
258{
259    tr_file_index_t i;
260    uint64_t byte_count = 0;
261    const tr_file_index_t n = tor->info.fileCount;
262
263    for( i=0; i<n; ++i )
264    {
265        struct stat sb;
266        char * filename = tr_torrentFindFile( tor, i );
267
268        sb.st_size = 0;
269        if( filename && !stat( filename, &sb ) )
270            byte_count += sb.st_size;
271
272        tr_free( filename );
273    }
274
275    return byte_count;
276}
277
278static int 
279compareVerifyBySize( const void * va, const void * vb ) 
280{ 
281    const struct verify_node * a = va; 
282    const struct verify_node * b = vb; 
283
284    if( a->current_size < b->current_size ) return -1;
285    if( a->current_size > b->current_size ) return  1;
286    return 0;
287} 
288
289void
290tr_verifyAdd( tr_torrent *      tor,
291              tr_verify_done_cb verify_done_cb )
292{
293    assert( tr_isTorrent( tor ) );
294
295    if( tr_torrentCountUncheckedPieces( tor ) == 0 )
296    {
297        /* doesn't need to be checked... */
298        fireCheckDone( tor, verify_done_cb );
299    }
300    else
301    {
302        const uint64_t current_size = getCurrentSize( tor );
303
304        if( !current_size )
305        {
306            /* we haven't downloaded anything for this torrent yet...
307             * no need to leave it waiting in the back of the queue.
308             * we can mark it as all-missing from here and fire
309             * the "done" callback */
310            const tr_bool hadAny = tr_cpHaveTotal( &tor->completion ) != 0;
311            tr_piece_index_t i;
312            for( i=0; i<tor->info.pieceCount; ++i ) {
313                tr_torrentSetHasPiece( tor, i, FALSE );
314                tr_torrentSetPieceChecked( tor, i, TRUE );
315            }
316            if( hadAny ) /* if we thought we had some, flag as dirty */
317                tr_torrentSetDirty( tor );
318            fireCheckDone( tor, verify_done_cb );
319        }
320        else
321        {
322            struct verify_node * node;
323
324            tr_torinf( tor, "%s", _( "Queued for verification" ) );
325
326            node = tr_new( struct verify_node, 1 );
327            node->torrent = tor;
328            node->verify_done_cb = verify_done_cb;
329            node->current_size = current_size;
330
331            tr_lockLock( getVerifyLock( ) );
332            tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
333            tr_list_insert_sorted( &verifyList, node, compareVerifyBySize );
334            if( verifyThread == NULL )
335                verifyThread = tr_threadNew( verifyThreadFunc, NULL );
336            tr_lockUnlock( getVerifyLock( ) );
337        }
338    }
339}
340
341static int
342compareVerifyByTorrent( const void * va, const void * vb )
343{
344    const struct verify_node * a = va;
345    const tr_torrent * b = vb;
346    return a->torrent - b;
347}
348
349void
350tr_verifyRemove( tr_torrent * tor )
351{
352    tr_lock * lock = getVerifyLock( );
353    tr_lockLock( lock );
354
355    assert( tr_isTorrent( tor ) );
356
357    if( tor == currentNode.torrent )
358    {
359        stopCurrent = TRUE;
360        while( stopCurrent )
361        {
362            tr_lockUnlock( lock );
363            tr_wait_msec( 100 );
364            tr_lockLock( lock );
365        }
366    }
367    else
368    {
369        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
370        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
371    }
372
373    tr_lockUnlock( lock );
374}
375
376void
377tr_verifyClose( tr_session * session UNUSED )
378{
379    tr_lockLock( getVerifyLock( ) );
380
381    stopCurrent = TRUE;
382    tr_list_free( &verifyList, tr_free );
383
384    tr_lockUnlock( getVerifyLock( ) );
385}
Note: See TracBrowser for help on using the repository browser.