Ignore:
Timestamp:
Dec 9, 2010, 8:43:23 PM (11 years ago)
Author:
charles
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/torrent.c

    r11301 r11506  
    3535#include "resume.h"
    3636#include "fdlimit.h" /* tr_fdTorrentClose */
     37#include "inout.h" /* tr_ioTestPiece() */
    3738#include "magnet.h"
    3839#include "metainfo.h"
     
    460461    va_end( ap );
    461462
     463    tr_torerr( tor, "%s", tor->errorString );
     464
    462465    if( tor->isRunning )
    463466        tor->isStopping = TRUE;
     
    605608tr_torrentInitFilePieces( tr_torrent * tor )
    606609{
    607     tr_file_index_t  f;
     610    int * firstFiles;
     611    tr_file_index_t f;
    608612    tr_piece_index_t p;
    609613    uint64_t offset = 0;
    610614    tr_info * inf = &tor->info;
    611     int * firstFiles;
    612615
    613616    /* assign the file offsets */
     
    721724    tr_torrentInitFilePieces( tor );
    722725
    723     tr_bitfieldConstruct( &tor->checkedPieces, tor->info.pieceCount );
    724 
    725726    tor->completeness = tr_cpGetStatus( &tor->completion );
    726727}
     
    734735
    735736    tr_torrentFireMetadataCompleted( tor );
     737}
     738
     739static tr_bool
     740setLocalErrorIfFilesDisappeared( tr_torrent * tor )
     741{
     742    const tr_bool disappeared = ( tr_cpHaveTotal( &tor->completion ) > 0 ) && ( tr_torrentGetCurrentSizeOnDisk( tor ) == 0 );
     743
     744    if( disappeared )
     745    {
     746        tr_tordbg( tor, "%s", "[LAZY] uh oh, the files disappeared" );
     747        tr_torrentSetLocalError( tor, "%s", _( "No data found!  Ensure your drives are connected or use \"Set Location\".  To re-download, remove the torrent and re-add it." ) );
     748    }
     749
     750    return disappeared;
    736751}
    737752
     
    742757    uint64_t loaded;
    743758    const char * dir;
     759    tr_bool isNewTorrent;
     760    struct stat st;
    744761    static int nextUniqueId = 1;
    745762    tr_session * session = tr_ctorGetSession( ctor );
     
    779796    assert( !tor->uploadedCur );
    780797
    781     tr_torrentUncheck( tor );
    782 
    783798    tr_torrentSetAddedDate( tor, tr_time( ) ); /* this is a default value to be
    784799                                                  overwritten by the resume file */
     
    787802    loaded = tr_torrentLoadResume( tor, ~0, ctor );
    788803    tor->completeness = tr_cpGetStatus( &tor->completion );
     804    setLocalErrorIfFilesDisappeared( tor );
    789805
    790806    tr_ctorInitTorrentPriorities( ctor, tor );
     
    829845        ++session->torrentCount;
    830846    }
     847
     848    /* if we don't have a local .torrent file already, assume the torrent is new */
     849    isNewTorrent = stat( tor->info.torrent, &st );
    831850
    832851    /* maybe save our own copy of the metainfo */
     
    846865    tor->tiers = tr_announcerAddTorrent( tor->session->announcer, tor, onTrackerResponse, NULL );
    847866
    848     if( doStart )
     867    if( isNewTorrent )
     868    {
     869        tor->startAfterVerify = doStart;
     870        tr_torrentVerify( tor );
     871    }
     872    else if( doStart )
     873    {
    849874        torrentStart( tor );
     875    }
    850876
    851877    tr_sessionUnlock( session );
     
    10491075}
    10501076
     1077static double
     1078getVerifyProgress( const tr_torrent * tor )
     1079{
     1080    tr_piece_index_t i, n;
     1081    tr_piece_index_t checked = 0;
     1082
     1083    assert( tr_isTorrent( tor ) );
     1084
     1085    for( i=0, n=tor->info.pieceCount; i!=n; ++i )
     1086        if( tor->info.pieces[i].timeChecked )
     1087            ++checked;
     1088
     1089    return checked / (double)tor->info.pieceCount;
     1090}
     1091
    10511092const tr_stat *
    10521093tr_torrentStat( tr_torrent * tor )
     
    10981139    s->metadataPercentComplete = tr_torrentGetMetadataPercent( tor );
    10991140
    1100     s->percentDone   = tr_cpPercentDone  ( &tor->completion );
    1101     s->leftUntilDone = tr_cpLeftUntilDone( &tor->completion );
    1102     s->sizeWhenDone  = tr_cpSizeWhenDone ( &tor->completion );
    1103 
    1104     s->recheckProgress = s->activity == TR_STATUS_CHECK
    1105                        ? 1.0 - ( tr_torrentCountUncheckedPieces( tor ) / (float) tor->info.pieceCount )
    1106                        : 0.0;
    1107 
    1108     s->activityDate = tor->activityDate;
    1109     s->addedDate    = tor->addedDate;
    1110     s->doneDate     = tor->doneDate;
    1111     s->startDate    = tor->startDate;
     1141    s->percentDone      = tr_cpPercentDone  ( &tor->completion );
     1142    s->leftUntilDone    = tr_cpLeftUntilDone( &tor->completion );
     1143    s->sizeWhenDone     = tr_cpSizeWhenDone ( &tor->completion );
     1144    s->recheckProgress  = s->activity == TR_STATUS_CHECK ? getVerifyProgress( tor ) : 0;
     1145    s->activityDate     = tor->activityDate;
     1146    s->addedDate        = tor->addedDate;
     1147    s->doneDate         = tor->doneDate;
     1148    s->startDate        = tor->startDate;
    11121149
    11131150    if ((s->activity == TR_STATUS_DOWNLOAD || s->activity == TR_STATUS_SEED) && s->startDate != 0)
     
    14421479    tr_announcerRemoveTorrent( session->announcer, tor );
    14431480
    1444     tr_bitfieldDestruct( &tor->checkedPieces );
    1445 
    14461481    tr_free( tor->downloadDir );
    14471482    tr_free( tor->incompleteDir );
     
    14731508
    14741509static void
    1475 checkAndStartImpl( void * vtor )
    1476 {
     1510torrentStartImpl( void * vtor )
     1511{
     1512    time_t now;
    14771513    tr_torrent * tor = vtor;
    14781514
     
    14811517    tr_sessionLock( tor->session );
    14821518
    1483     /** If we had local data before, but it's disappeared,
    1484         stop the torrent and log an error. */
    1485     if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
    1486     {
    1487         tr_torrentSetLocalError( tor, "%s", _( "No data found!  Reconnect any disconnected drives, use \"Set Location\", or restart the torrent to re-download." ) );
    1488     }
    1489     else
    1490     {
    1491         const time_t now = tr_time( );
    1492         tor->isRunning = TRUE;
    1493         tor->completeness = tr_cpGetStatus( &tor->completion );
    1494         tor->startDate = tor->anyDate = now;
    1495         tr_torrentClearError( tor );
    1496         tor->finishedSeedingByIdle = FALSE;
    1497 
    1498         tr_torrentResetTransferStats( tor );
    1499         tr_announcerTorrentStarted( tor );
    1500         tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
    1501         tor->dhtAnnounce6At = now + tr_cryptoWeakRandInt( 20 );
    1502         tor->lpdAnnounceAt = now;
    1503         tr_peerMgrStartTorrent( tor );
    1504     }
     1519    tr_torrentRecheckCompleteness( tor );
     1520
     1521    now = tr_time( );
     1522    tor->isRunning = TRUE;
     1523    tor->completeness = tr_cpGetStatus( &tor->completion );
     1524    tor->startDate = tor->anyDate = now;
     1525    tr_torrentClearError( tor );
     1526    tor->finishedSeedingByIdle = FALSE;
     1527
     1528    tr_torrentResetTransferStats( tor );
     1529    tr_announcerTorrentStarted( tor );
     1530    tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
     1531    tor->dhtAnnounce6At = now + tr_cryptoWeakRandInt( 20 );
     1532    tor->lpdAnnounceAt = now;
     1533    tr_peerMgrStartTorrent( tor );
    15051534
    15061535    tr_sessionUnlock( tor->session );
    15071536}
    15081537
    1509 static void
    1510 checkAndStartCB( tr_torrent * tor )
    1511 {
    1512     assert( tr_isTorrent( tor ) );
    1513     assert( tr_isSession( tor->session ) );
    1514 
    1515     tr_runInEventThread( tor->session, checkAndStartImpl, tor );
     1538uint64_t
     1539tr_torrentGetCurrentSizeOnDisk( const tr_torrent * tor )
     1540{
     1541    tr_file_index_t i;
     1542    uint64_t byte_count = 0;
     1543    const tr_file_index_t n = tor->info.fileCount;
     1544
     1545    for( i=0; i<n; ++i )
     1546    {
     1547        struct stat sb;
     1548        char * filename = tr_torrentFindFile( tor, i );
     1549
     1550        sb.st_size = 0;
     1551        if( filename && !stat( filename, &sb ) )
     1552            byte_count += sb.st_size;
     1553
     1554        tr_free( filename );
     1555    }
     1556
     1557    return byte_count;
    15161558}
    15171559
     
    15211563    assert( tr_isTorrent( tor ) );
    15221564
     1565    /* already running... */
     1566    if( tor->isRunning )
     1567        return;
     1568
     1569    /* don't allow the torrent to be started if the files disappeared */
     1570    if( setLocalErrorIfFilesDisappeared( tor ) )
     1571        return;
     1572
     1573    /* otherwise, start it now... */
    15231574    tr_sessionLock( tor->session );
    15241575
    1525     if( !tor->isRunning )
    1526     {
    1527         /* allow finished torrents to be resumed */
    1528         if( tr_torrentIsSeedRatioDone( tor ) )
    1529         {
    1530             tr_torinf( tor, "Restarted manually -- disabling its seed ratio" );
    1531             tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
    1532         }
    1533 
    1534         tr_verifyRemove( tor );
    1535 
    1536         /* corresponds to the peer_id sent as a tracker request parameter.
    1537          * one tracker admin says: "When the same torrent is opened and
    1538          * closed and opened again without quitting Transmission ...
    1539          * change the peerid. It would help sometimes if a stopped event
    1540          * was missed to ensure that we didn't think someone was cheating. */
    1541         tr_free( tor->peer_id );
    1542         tor->peer_id = tr_peerIdNew( );
    1543 
    1544         tor->isRunning = 1;
    1545         tr_torrentSetDirty( tor );
    1546         tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
    1547         tr_verifyAdd( tor, checkAndStartCB );
    1548     }
     1576    /* allow finished torrents to be resumed */
     1577    if( tr_torrentIsSeedRatioDone( tor ) ) {
     1578        tr_torinf( tor, _( "Restarted manually -- disabling its seed ratio" ) );
     1579        tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
     1580    }
     1581
     1582    tr_verifyRemove( tor );
     1583
     1584    /* corresponds to the peer_id sent as a tracker request parameter.
     1585     * one tracker admin says: "When the same torrent is opened and
     1586     * closed and opened again without quitting Transmission ...
     1587     * change the peerid. It would help sometimes if a stopped event
     1588     * was missed to ensure that we didn't think someone was cheating. */
     1589    tr_free( tor->peer_id );
     1590    tor->peer_id = tr_peerIdNew( );
     1591    tor->isRunning = 1;
     1592    tr_torrentSetDirty( tor );
     1593    tr_runInEventThread( tor->session, torrentStartImpl, tor );
    15491594
    15501595    tr_sessionUnlock( tor->session );
     
    15621607{
    15631608    tr_torrent * tor = vtor;
    1564 
    1565     assert( tr_isTorrent( tor ) );
     1609    assert( tr_isTorrent( tor ) );
     1610
    15661611    tr_torrentRecheckCompleteness( tor );
    15671612
    1568     if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
    1569     {
    1570         tr_torrentSetLocalError( tor, "%s", _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
    1571     }
    1572     else if( tor->startAfterVerify )
    1573     {
     1613    if( tor->startAfterVerify ) {
    15741614        tor->startAfterVerify = FALSE;
    1575 
    1576         tr_torrentStart( tor );
     1615        torrentStart( tor );
    15771616    }
    15781617}
     
    16051644    }
    16061645
    1607     /* add the torrent to the recheck queue */
    1608     tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
    1609     tr_torrentUncheck( tor );
    1610     tr_verifyAdd( tor, torrentRecheckDoneCB );
     1646    if( setLocalErrorIfFilesDisappeared( tor ) )
     1647        tor->startAfterVerify = FALSE;
     1648    else
     1649        tr_verifyAdd( tor, torrentRecheckDoneCB );
    16111650
    16121651    tr_sessionUnlock( tor->session );
     
    22092248
    22102249void
    2211 tr_torrentSetPieceChecked( tr_torrent        * tor,
    2212                            tr_piece_index_t    piece,
    2213                            tr_bool             isChecked )
    2214 {
    2215     assert( tr_isTorrent( tor ) );
    2216 
    2217     if( isChecked )
    2218         tr_bitfieldAdd( &tor->checkedPieces, piece );
    2219     else
    2220         tr_bitfieldRem( &tor->checkedPieces, piece );
    2221 }
    2222 
    2223 void
    2224 tr_torrentSetFileChecked( tr_torrent *    tor,
    2225                           tr_file_index_t fileIndex,
    2226                           tr_bool         isChecked )
    2227 {
    2228     const tr_file *        file = &tor->info.files[fileIndex];
    2229     const tr_piece_index_t begin = file->firstPiece;
    2230     const tr_piece_index_t end = file->lastPiece + 1;
    2231 
    2232     assert( tr_isTorrent( tor ) );
    2233 
    2234     if( isChecked )
    2235         tr_bitfieldAddRange( &tor->checkedPieces, begin, end );
    2236     else
    2237         tr_bitfieldRemRange( &tor->checkedPieces, begin, end );
     2250tr_torrentSetPieceChecked( tr_torrent * tor, tr_piece_index_t pieceIndex )
     2251{
     2252    assert( tr_isTorrent( tor ) );
     2253    assert( pieceIndex < tor->info.pieceCount );
     2254
     2255    tr_tordbg( tor, "[LAZY] setting piece %zu timeChecked to now", (size_t)pieceIndex );
     2256    tor->info.pieces[pieceIndex].timeChecked = tr_time( );
     2257}
     2258
     2259void
     2260tr_torrentSetChecked( tr_torrent * tor, time_t when )
     2261{
     2262    tr_piece_index_t i, n;
     2263
     2264    assert( tr_isTorrent( tor ) );
     2265
     2266    for( i=0, n=tor->info.pieceCount; i!=n; ++i )
     2267        tor->info.pieces[i].timeChecked = when;
    22382268}
    22392269
    22402270tr_bool
    2241 tr_torrentIsFileChecked( const tr_torrent * tor,
    2242                          tr_file_index_t    fileIndex )
    2243 {
    2244     const tr_file *        file = &tor->info.files[fileIndex];
    2245     const tr_piece_index_t begin = file->firstPiece;
    2246     const tr_piece_index_t end = file->lastPiece + 1;
    2247     tr_piece_index_t       i;
    2248     tr_bool                isChecked = TRUE;
    2249 
    2250     assert( tr_isTorrent( tor ) );
    2251 
    2252     for( i = begin; isChecked && i < end; ++i )
    2253         if( !tr_torrentIsPieceChecked( tor, i ) )
    2254             isChecked = FALSE;
    2255 
    2256     return isChecked;
    2257 }
    2258 
    2259 void
    2260 tr_torrentUncheck( tr_torrent * tor )
    2261 {
    2262     assert( tr_isTorrent( tor ) );
    2263 
    2264     tr_bitfieldRemRange( &tor->checkedPieces, 0, tor->info.pieceCount );
    2265 }
    2266 
    2267 int
    2268 tr_torrentCountUncheckedPieces( const tr_torrent * tor )
    2269 {
    2270     assert( tr_isTorrent( tor ) );
    2271 
    2272     return tor->info.pieceCount - tr_bitfieldCountTrueBits( &tor->checkedPieces );
    2273 }
    2274 
    2275 time_t*
    2276 tr_torrentGetMTimes( const tr_torrent * tor, size_t * setme_n )
    2277 {
    2278     size_t       i;
    2279     const size_t n = tor->info.fileCount;
    2280     time_t *     m = tr_new0( time_t, n );
    2281 
    2282     assert( tr_isTorrent( tor ) );
    2283 
    2284     for( i = 0; i < n; ++i )
    2285     {
    2286         struct stat sb;
    2287         char * path = tr_torrentFindFile( tor, i );
    2288         if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
    2289         {
     2271tr_torrentCheckPiece( tr_torrent * tor, tr_piece_index_t pieceIndex )
     2272{
     2273    const tr_bool pass = tr_ioTestPiece( tor, pieceIndex );
     2274
     2275    tr_tordbg( tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass );
     2276    tr_torrentSetHasPiece( tor, pieceIndex, pass );
     2277    tr_torrentSetPieceChecked( tor, pieceIndex );
     2278    tor->anyDate = tr_time( );
     2279    tr_torrentSetDirty( tor );
     2280
     2281    return pass;
     2282}
     2283
     2284static time_t
     2285getFileMTime( const tr_torrent * tor, tr_file_index_t i )
     2286{
     2287    struct stat sb;
     2288    time_t mtime = 0;
     2289    char * path = tr_torrentFindFile( tor, i );
     2290
     2291    if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
     2292    {
    22902293#ifdef SYS_DARWIN
    2291             m[i] = sb.st_mtimespec.tv_sec;
     2294        mtime = sb.st_mtimespec.tv_sec;
    22922295#else
    2293             m[i] = sb.st_mtime;
     2296        mtime = sb.st_mtime;
    22942297#endif
     2298    }
     2299
     2300    tr_free( path );
     2301    return mtime;
     2302}
     2303
     2304tr_bool
     2305tr_torrentPieceNeedsCheck( const tr_torrent * tor, tr_piece_index_t p )
     2306{
     2307    uint64_t unused;
     2308    tr_file_index_t f;
     2309    const tr_info * inf = tr_torrentInfo( tor );
     2310 
     2311    /* if we've never checked this piece, then it needs to be checked */
     2312    if( !inf->pieces[p].timeChecked ) {
     2313        tr_tordbg( tor, "[LAZY] piece %zu needs to be tested because it's never been tested", (size_t)p );
     2314        return TRUE;
     2315    }
     2316
     2317    /* If we think we've completed one of the files in this piece,
     2318     * but it's been modified since we last checked it,
     2319     * then it needs to be rechecked */
     2320    tr_ioFindFileLocation( tor, p, 0, &f, &unused );
     2321    for( ; f < inf->fileCount && pieceHasFile( p, &inf->files[f] ); ++f ) {
     2322        if( tr_cpFileIsComplete( &tor->completion, f ) ) {
     2323            if( getFileMTime( tor, f ) > inf->pieces[p].timeChecked ) {
     2324                tr_tordbg( tor, "[LAZY] piece %zu needs to be tested because file %zu mtime is newer than check time %zu", (size_t)p, (size_t)f, (size_t)inf->pieces[p].timeChecked );
     2325                return TRUE;
     2326            }
    22952327        }
    2296         tr_free( path );
    2297     }
    2298 
    2299     *setme_n = n;
    2300     return m;
     2328    }
     2329
     2330    tr_tordbg( tor, "[LAZY] piece %zu does not need to be tested", (size_t)p );
     2331    return FALSE;
    23012332}
    23022333
Note: See TracChangeset for help on using the changeset viewer.