source: trunk/libtransmission/verify.c @ 11506

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

(trunk libT) #2955 "Lazy Verification (aka Just-in-Time Verification)" -- implemented.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.5 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 11506 2010-12-09 20:43:23Z charles $
11 */
12
13#ifdef HAVE_POSIX_FADVISE
14 #define _XOPEN_SOURCE 600
15 #include <fcntl.h> /* posix_fadvise() */
16#endif
17
18#include <openssl/sha.h>
19
20#include "transmission.h"
21#include "completion.h"
22#include "fdlimit.h"
23#include "list.h"
24#include "platform.h" /* tr_lock() */
25#include "torrent.h"
26#include "utils.h" /* tr_valloc(), tr_free() */
27#include "verify.h"
28
29/***
30****
31***/
32
33enum
34{
35    MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY = 100
36};
37
38static tr_bool
39verifyTorrent( tr_torrent * tor, tr_bool * stopFlag )
40{
41    time_t end;
42    SHA_CTX sha;
43    int fd = -1;
44    int64_t filePos = 0;
45    tr_bool changed = 0;
46    tr_bool hadPiece = 0;
47    time_t lastSleptAt = 0;
48    uint32_t piecePos = 0;
49    tr_file_index_t fileIndex = 0;
50    tr_file_index_t prevFileIndex = !fileIndex;
51    tr_piece_index_t pieceIndex = 0;
52    const time_t begin = tr_time( );
53    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
54    uint8_t * buffer = tr_valloc( buflen );
55
56    SHA1_Init( &sha );
57
58    tr_tordbg( tor, "%s", "[LAZY] verifying torrent..." );
59    tr_torrentSetChecked( tor, 0 );
60    while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) )
61    {
62        uint32_t leftInPiece;
63        uint32_t bytesThisPass;
64        uint64_t leftInFile;
65        const tr_file * file = &tor->info.files[fileIndex];
66
67        /* if we're starting a new piece... */
68        if( piecePos == 0 )
69            hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex );
70
71        /* if we're starting a new file... */
72        if( !filePos && (fd<0) && (fileIndex!=prevFileIndex) )
73        {
74            char * filename = tr_torrentFindFile( tor, fileIndex );
75            fd = filename == NULL ? -1 : tr_open_file_for_scanning( filename );
76            tr_free( filename );
77            prevFileIndex = fileIndex;
78        }
79
80        /* figure out how much we can read this pass */
81        leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos;
82        leftInFile = file->length - filePos;
83        bytesThisPass = MIN( leftInFile, leftInPiece );
84        bytesThisPass = MIN( bytesThisPass, buflen );
85
86        /* read a bit */
87        if( fd >= 0 ) {
88            const ssize_t numRead = tr_pread( fd, buffer, bytesThisPass, filePos );
89            if( numRead > 0 ) {
90                bytesThisPass = (uint32_t)numRead;
91                SHA1_Update( &sha, buffer, bytesThisPass );
92#if defined HAVE_POSIX_FADVISE && defined POSIX_FADV_DONTNEED
93                posix_fadvise( fd, filePos, bytesThisPass, POSIX_FADV_DONTNEED );
94#endif
95            }
96        }
97
98        /* move our offsets */
99        leftInPiece -= bytesThisPass;
100        leftInFile -= bytesThisPass;
101        piecePos += bytesThisPass;
102        filePos += bytesThisPass;
103
104        /* if we're finishing a piece... */
105        if( leftInPiece == 0 )
106        {
107            time_t now;
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
114            if( hasPiece || hadPiece ) {
115                tr_torrentSetHasPiece( tor, pieceIndex, hasPiece );
116                changed |= hasPiece != hadPiece;
117            }
118            tr_torrentSetPieceChecked( tor, pieceIndex );
119            now = tr_time( );
120            tor->anyDate = now;
121
122            /* sleeping even just a few msec per second goes a long
123             * way towards reducing IO load... */
124            if( lastSleptAt != now ) {
125                lastSleptAt = now;
126                tr_wait_msec( MSEC_TO_SLEEP_PER_SECOND_DURING_VERIFY );
127            }
128
129            SHA1_Init( &sha );
130            ++pieceIndex;
131            piecePos = 0;
132        }
133
134        /* if we're finishing a file... */
135        if( leftInFile == 0 )
136        {
137            if( fd >= 0 ) { tr_close_file( fd ); fd = -1; }
138            ++fileIndex;
139            filePos = 0;
140        }
141    }
142    tr_tordbg( tor, "%s", "[LAZY] DONE verifying torrent..." );
143
144    /* cleanup */
145    if( fd >= 0 )
146        tr_close_file( fd );
147    free( buffer );
148
149    /* stopwatch */
150    end = tr_time( );
151    tr_tordbg( tor, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)",
152               (int)(end-begin), tor->info.totalSize,
153               (uint64_t)(tor->info.totalSize/(1+(end-begin))) );
154
155    return changed;
156}
157
158/***
159****
160***/
161
162struct verify_node
163{
164    tr_torrent *         torrent;
165    tr_verify_done_cb    verify_done_cb;
166    uint64_t             current_size;
167};
168
169static void
170fireCheckDone( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
171{
172    assert( tr_isTorrent( tor ) );
173
174    if( verify_done_cb )
175        verify_done_cb( tor );
176}
177
178static struct verify_node currentNode;
179static tr_list * verifyList = NULL;
180static tr_thread * verifyThread = NULL;
181static tr_bool stopCurrent = FALSE;
182
183static tr_lock*
184getVerifyLock( void )
185{
186    static tr_lock * lock = NULL;
187    if( lock == NULL )
188        lock = tr_lockNew( );
189    return lock;
190}
191
192static void
193verifyThreadFunc( void * unused UNUSED )
194{
195    for( ;; )
196    {
197        int changed = 0;
198        tr_torrent * tor;
199        struct verify_node * node;
200
201        tr_lockLock( getVerifyLock( ) );
202        stopCurrent = FALSE;
203        node = (struct verify_node*) verifyList ? verifyList->data : NULL;
204        if( node == NULL )
205        {
206            currentNode.torrent = NULL;
207            break;
208        }
209
210        currentNode = *node;
211        tor = currentNode.torrent;
212        tr_list_remove_data( &verifyList, node );
213        tr_free( node );
214        tr_lockUnlock( getVerifyLock( ) );
215
216        tr_torinf( tor, "%s", _( "Verifying torrent" ) );
217        tr_torrentSetVerifyState( tor, TR_VERIFY_NOW );
218        changed = verifyTorrent( tor, &stopCurrent );
219        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
220        assert( tr_isTorrent( tor ) );
221
222        if( !stopCurrent )
223        {
224            if( changed )
225                tr_torrentSetDirty( tor );
226            fireCheckDone( tor, currentNode.verify_done_cb );
227        }
228    }
229
230    verifyThread = NULL;
231    tr_lockUnlock( getVerifyLock( ) );
232}
233
234static int
235compareVerifyByPriorityAndSize( const void * va, const void * vb )
236{
237    const struct verify_node * a = va;
238    const struct verify_node * b = vb;
239
240    /* higher priority comes before lower priority */
241    const tr_priority_t pa = tr_torrentGetPriority( a->torrent );
242    const tr_priority_t pb = tr_torrentGetPriority( b->torrent );
243    if( pa != pb )
244        return pa > pb ? -1 : 1;
245
246    /* smaller torrents come before larger ones because they verify faster */
247    if( a->current_size < b->current_size ) return -1;
248    if( a->current_size > b->current_size ) return  1;
249    return 0;
250}
251
252void
253tr_verifyAdd( tr_torrent * tor, tr_verify_done_cb verify_done_cb )
254{
255    struct verify_node * node;
256
257    assert( tr_isTorrent( tor ) );
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    node->current_size = tr_torrentGetCurrentSizeOnDisk( tor );
264
265    tr_lockLock( getVerifyLock( ) );
266    tr_torrentSetVerifyState( tor, TR_VERIFY_WAIT );
267    tr_list_insert_sorted( &verifyList, node, compareVerifyByPriorityAndSize );
268    if( verifyThread == NULL )
269        verifyThread = tr_threadNew( verifyThreadFunc, NULL );
270    tr_lockUnlock( getVerifyLock( ) );
271}
272
273static int
274compareVerifyByTorrent( const void * va, const void * vb )
275{
276    const struct verify_node * a = va;
277    const tr_torrent * b = vb;
278    return a->torrent - b;
279}
280
281void
282tr_verifyRemove( tr_torrent * tor )
283{
284    tr_lock * lock = getVerifyLock( );
285    tr_lockLock( lock );
286
287    assert( tr_isTorrent( tor ) );
288
289    if( tor == currentNode.torrent )
290    {
291        stopCurrent = TRUE;
292        while( stopCurrent )
293        {
294            tr_lockUnlock( lock );
295            tr_wait_msec( 100 );
296            tr_lockLock( lock );
297        }
298    }
299    else
300    {
301        tr_free( tr_list_remove( &verifyList, tor, compareVerifyByTorrent ) );
302        tr_torrentSetVerifyState( tor, TR_VERIFY_NONE );
303    }
304
305    tr_lockUnlock( lock );
306}
307
308void
309tr_verifyClose( tr_session * session UNUSED )
310{
311    tr_lockLock( getVerifyLock( ) );
312
313    stopCurrent = TRUE;
314    tr_list_free( &verifyList, tr_free );
315
316    tr_lockUnlock( getVerifyLock( ) );
317}
Note: See TracBrowser for help on using the repository browser.