From: dan Date: Thu, 5 Aug 2010 18:53:26 +0000 (+0000) Subject: Add comments describing UNKNOWN_LOCK to pager.c. Improve some other comments i the... X-Git-Tag: version-3.7.2~53^2~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=54919f82386d6fe6e38bfcb71520ec950b2cab0e;p=thirdparty%2Fsqlite.git Add comments describing UNKNOWN_LOCK to pager.c. Improve some other comments i the same file. FossilOrigin-Name: 54eff6de9d8d87f33192c192ca91907c4c090988 --- diff --git a/manifest b/manifest index 4b915c1909..510ddb5501 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\schanges\swith\sexperimental\sbranch. -D 2010-08-05T16:22:50 +C Add\scomments\sdescribing\sUNKNOWN_LOCK\sto\spager.c.\sImprove\ssome\sother\scomments\si\sthe\ssame\sfile. +D 2010-08-05T18:53:27 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -156,7 +156,7 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e F src/os_unix.c ae5ca8a6031380708f3fec7be325233d49944914 F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7 -F src/pager.c 7232578996c9ecd7b57bb9952a81549f2f133a4b +F src/pager.c 07bda904e5b3e6fe6c9425a205fcaf1dca5a444c F src/pager.h 80726162dc3942f59ab27b738fb667b9ba0a89d5 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07 @@ -842,7 +842,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 800f496929fb0d45d16c573c5dca0197ac922e2b ca479f3de2927ccc05dc76d10e40c00b8e0c88d1 -R 5a6390c995b69a7f924deee65f1b7dc5 +P acd26b8b746980c344db017a0e96dbd92c89acdf +R c079a70d95550574b0e39982e236e749 U dan -Z 434bd8bfb5691e86b993c0ccfbfc4d4c +Z 83b26f553c42e8e1edd2dad0b7de50dd diff --git a/manifest.uuid b/manifest.uuid index c5356f714b..8f9281da3f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -acd26b8b746980c344db017a0e96dbd92c89acdf \ No newline at end of file +54eff6de9d8d87f33192c192ca91907c4c090988 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index cbbce3ff0a..f5a2c4c4b3 100644 --- a/src/pager.c +++ b/src/pager.c @@ -163,12 +163,23 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ ** rollback (non-WAL) mode are met. Unless the pager is (or recently ** was) in exclusive-locking mode, a user-level read transaction is ** open. The database size is known in this state. +** +** A connection running with locking_mode=normal enters this state when +** it opens a read-transaction on the database and returns to state +** NONE after the read-transaction is completed. However a connection +** running in locking_mode=exclusive (including temp databases) remains in +** this state even after the read-transaction is closed. The only way +** a locking_mode=exclusive connection can transition from READER to NONE +** is via the ERROR state (see below). ** -** * A read transaction may be active. +** * A read transaction may be active (but a write-transaction cannot). ** * A SHARED or greater lock is held on the database file. ** * The dbSize variable may be trusted (even if a user-level read ** transaction is not active). The dbOrigSize and dbFileSize variables ** may not be trusted at this point. +** * If the database is a WAL database, then the WAL connection is open. +** * Even if a read-transaction is not open, it is guaranteed that +** there is no hot-journal in the file-system. ** ** WRITER_INITIAL: ** @@ -290,62 +301,55 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ #define PAGER_WRITER_FINISHED 5 #define PAGER_ERROR 6 - /* -** The page cache as a whole is always in one of the following -** states: -** -** PAGER_UNLOCK The page cache is not currently reading or -** writing the database file. There is no -** data held in memory. This is the initial -** state. -** -** PAGER_SHARED The page cache is reading the database. -** Writing is not permitted. There can be -** multiple readers accessing the same database -** file at the same time. -** -** PAGER_RESERVED This process has reserved the database for writing -** but has not yet made any changes. Only one process -** at a time can reserve the database. The original -** database file has not been modified so other -** processes may still be reading the on-disk -** database file. -** -** PAGER_EXCLUSIVE The page cache is writing the database. -** Access is exclusive. No other processes or -** threads can be reading or writing while one -** process is writing. -** -** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE -** after all dirty pages have been written to the -** database file and the file has been synced to -** disk. All that remains to do is to remove or -** truncate the journal file and the transaction -** will be committed. -** -** The page cache comes up in PAGER_UNLOCK. The first time a -** sqlite3PagerGet() occurs, the state transitions to PAGER_SHARED. -** After all pages have been released using sqlite_page_unref(), -** the state transitions back to PAGER_UNLOCK. The first time -** that sqlite3PagerWrite() is called, the state transitions to -** PAGER_RESERVED. (Note that sqlite3PagerWrite() can only be -** called on an outstanding page which means that the pager must -** be in PAGER_SHARED before it transitions to PAGER_RESERVED.) -** PAGER_RESERVED means that there is an open rollback journal. -** The transition to PAGER_EXCLUSIVE occurs before any changes -** are made to the database file, though writes to the rollback -** journal occurs with just PAGER_RESERVED. After an sqlite3PagerRollback() -** or sqlite3PagerCommitPhaseTwo(), the state can go back to PAGER_SHARED, -** or it can stay at PAGER_EXCLUSIVE if we are in exclusive access mode. +** The Pager.eLock variable is almost always set to one of the +** following locking-states, according to the lock currently held on +** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK. +** This variable is kept up to date as locks are taken and released by +** the pagerLockDb() and pagerUnlockDb() wrappers. +** +** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY +** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not +** the operation was successful. In these circumstances pagerLockDb() and +** pagerUnlockDb() take a conservative approach - eLock is always updated +** when unlocking the file, and only updated when locking the file if the +** VFS call is successful. This way, the Pager.eLock variable may be set +** to a less exclusive (lower) value than the lock that is actually held +** at the system level, but it is never set to a more exclusive value. +** +** This is usually safe. If an xUnlock fails or appears to fail, there may +** be a few redundant xLock() calls or a lock may be held for longer than +** required, but nothing really goes wrong. +** +** The exception is when the database file is unlocked as the pager moves +** from ERROR to NONE state. At this point there may be a hot-journal file +** in the file-system that needs to be rolled back (as part of a NONE->SHARED +** transition, by the same pager or any other). If the call to xUnlock() +** fails at this point and the pager is left holding an EXCLUSIVE lock, this +** can confuse the call to xCheckReservedLock() call made later as part +** of hot-journal detection. +** +** xCheckReservedLock() is defined as returning true "if there is a RESERVED +** lock held by this process or any others". So xCheckReservedLock may +** return true because the caller itself is holding an EXCLUSIVE lock (but +** doesn't know it because of a previous error in xUnlock). If this happens +** a hot-journal may be mistaken for a journal being created by an active +** transaction in another process, causing SQLite to read from the database +** without rolling it back. +** +** To work around this, if a call to xUnlock() fails when unlocking the +** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It +** is only changed back to a real locking state after a successful call +** to xLock(EXCLUSIVE). Also, the code to do the NONE->SHARED state transition +** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK +** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE +** lock on the database file before attempting to roll it back. See function +** PagerSharedLock() for more detail. +** +** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in +** PAGER_NONE state. */ -#define PAGER_UNLOCK 0 -#define PAGER_SHARED 1 /* same as SHARED_LOCK */ -#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */ -#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */ -#define PAGER_SYNCED 5 - -#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) +#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) /* ** A macro used for invoking the codec if there is one @@ -398,14 +402,11 @@ struct PagerSavepoint { ** A open page cache is an instance of the following structure. ** ** errCode +** The Pager.errCode variable is only ever non-zero when the condition +** (Pager.eState==PAGER_ERROR) is true. ** -** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or -** or SQLITE_FULL. Once one of the first three errors occurs, it persists -** and is returned as the result of every major pager API call. The -** SQLITE_FULL return code is slightly different. It persists only until the -** next successful rollback is performed on the pager cache. Also, -** SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup() -** APIs, they may still be used successfully. +** Pager.errCode may be set to SQLITE_FULL, SQLITE_IOERR or one of the +** SQLITE_IOERR_XXX sub-codes. ** ** dbSize, dbOrigSize, dbFileSize ** @@ -418,10 +419,7 @@ struct PagerSavepoint { ** out from the cache to the actual file on disk. Or if the image has been ** truncated by an incremental-vacuum operation. The Pager.dbOrigSize variable ** contains the number of pages in the database image when the current -** transaction was opened. The contents of all three of these variables is -** only guaranteed to be correct if the boolean Pager.dbSizeValid is true. -** -** TODO: Under what conditions is dbSizeValid set? Cleared? +** transaction was opened. ** ** changeCountDone ** @@ -719,6 +717,7 @@ static int assert_pager_state(Pager *p){ ** on the file. */ assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK ); + assert( p->eLock!=PENDING_LOCK ); switch( p->eState ){ case PAGER_NONE: @@ -899,16 +898,22 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){ } /* -** This function and pagerLockDb() are wrappers around sqlite3OsLock() and -** sqlite3OsUnlock() that set the Pager.eLock variable to reflect the -** current lock held on the database file. +** Unlock the database file to level eLock, which must be either NO_LOCK +** or SHARED_LOCK. Regardless of whether or not the call to xUnlock() +** succeeds, set the Pager.eLock variable to match the (attempted) new lock. +** +** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is +** called, do not modify it. See the comment above the #define of +** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; + assert( !pPager->exclusiveMode ); + assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); + assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); - assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); rc = sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = eLock; @@ -918,12 +923,21 @@ static int pagerUnlockDb(Pager *pPager, int eLock){ return rc; } +/* +** Lock the database file to level eLock, which must be either SHARED_LOCK, +** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the +** Pager.eLock variable to the new locking state. +** +** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is +** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. +** See the comment above the #define of UNKNOWN_LOCK for an explanation +** of this. +*/ static int pagerLockDb(Pager *pPager, int eLock){ - int rc; + int rc = SQLITE_OK; + assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK ); - if( pPager->eLock>=eLock && pPager->eLock!=UNKNOWN_LOCK ){ - rc = SQLITE_OK; - }else{ + if( pPager->eLockeLock==UNKNOWN_LOCK ){ rc = sqlite3OsLock(pPager->fd, eLock); if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){ pPager->eLock = eLock; @@ -1583,6 +1597,11 @@ static void pager_unlock(Pager *pPager){ sqlite3OsClose(pPager->jfd); } + /* If the pager is in the ERROR state and the call to unlock the database + ** file fails, set the current lock to UNKNOWN_LOCK. See the comment + ** above the #define for UNKNOWN_LOCK for an explanation of why this + ** is necessary. + */ rc = pagerUnlockDb(pPager, NO_LOCK); if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){ pPager->eLock = UNKNOWN_LOCK; @@ -2750,7 +2769,7 @@ static int pagerBeginReadTransaction(Pager *pPager){ int changed = 0; /* True if cache must be reset */ assert( pagerUseWal(pPager) ); - assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_SHARED ); + assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_READER ); /* sqlite3WalEndReadTransaction() was not called for the previous ** transaction in locking_mode=EXCLUSIVE. So call it now. If we @@ -3284,7 +3303,7 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ ** this is considered a 1 page file. */ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){ - assert( pPager->eState>=PAGER_SHARED ); + assert( pPager->eState>=PAGER_READER ); assert( pPager->eState!=PAGER_WRITER_FINISHED ); *pnPage = (int)pPager->dbSize; return SQLITE_OK; @@ -3308,11 +3327,6 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){ static int pager_wait_on_lock(Pager *pPager, int locktype){ int rc; /* Return code */ - /* The OS lock values must be the same as the Pager lock values */ - assert( PAGER_SHARED==SHARED_LOCK ); - assert( PAGER_RESERVED==RESERVED_LOCK ); - assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); - /* Check that this is either a no-op (because the requested lock is ** already held, or one of the transistions that the busy-handler ** may be invoked during, according to the comment above @@ -4424,7 +4438,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ if( pPager->noReadlock==0 ){ rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ - assert( pPager->eLock==PAGER_UNLOCK ); + assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); goto failed; } } @@ -5393,7 +5407,7 @@ int sqlite3PagerExclusiveLock(Pager *pPager){ ); assert( assert_pager_state(pPager) ); if( 0==pagerUseWal(pPager) ){ - rc = pager_wait_on_lock(pPager, PAGER_EXCLUSIVE); + rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); } return rc; }