Index: src/btInt.h ================================================================== --- src/btInt.h +++ src/btInt.h @@ -195,10 +195,12 @@ int sqlite4BtLogSnapshotWritable(BtLog*); int sqlite4BtLogSize(BtLog*); int sqlite4BtLogCheckpoint(BtLog*); +int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame); + #ifndef NDEBUG void sqlite4BtDebugReadPage(u32 pgno, u8 *aData, int pgsz); #else # define sqlite4BtDebugReadPage(a,b,c) #endif @@ -210,10 +212,11 @@ /************************************************************************* ** Interface to bt_lock.c functionality. */ typedef struct BtShared BtShared; typedef struct BtLock BtLock; +typedef struct BtReadSlot BtReadSlot; struct BtLock { /* These three are set by the bt_pager module and thereafter used by ** the bt_lock, bt_pager and bt_log modules. */ sqlite4_env *pEnv; /* SQLite environment */ bt_env *pVfs; /* Bt environment */ @@ -223,28 +226,35 @@ BtShared *pShared; /* Shared by all handles on this file */ BtLock *pNext; /* Next connection using pShared */ u32 mExclLock; /* Mask of exclusive locks held */ u32 mSharedLock; /* Mask of shared locks held */ }; + +struct BtReadSlot { + u32 iFirst; + u32 iLast; +}; /* Connect and disconnect procedures */ int sqlite4BtLockConnect(BtLock*, int (*xRecover)(BtLock*)); int sqlite4BtLockDisconnect(BtLock*, int(*xCkpt)(BtLock*), int(*xDel)(BtLock*)); /* Obtain and release the WRITER lock */ int sqlite4BtLockWriter(BtLock*); int sqlite4BtLockWriterUnlock(BtLock*); -/* Obtain, release and query READER locks. */ -int sqlite4BtLockReader(BtLock*, u32 *aLog, u32 *aLock); -int sqlite4BtLockReaderUnlock(BtLock*); -int sqlite4BtLockReaderMin(BtLock*, u32 *aLog, u32 *aLock, u32 *piMinFrame); - /* Obtain and release CHECKPOINTER lock */ int sqlite4BtLockCkpt(BtLock*); int sqlite4BtLockCkptUnlock(BtLock*); +/* Obtain and release READER locks. */ +int sqlite4BtLockReader(BtLock*, u32 *aLog, BtReadSlot *aLock); +int sqlite4BtLockReaderUnlock(BtLock*); + +/* Query READER locks. */ +int sqlite4BtLockReaderQuery(BtLock*, u32*, BtReadSlot*, u32*, int*); + /* Obtain pointers to shared-memory chunks */ int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut); /* ** End of bt_lock.c interface. Index: src/bt_lock.c ================================================================== --- src/bt_lock.c +++ src/bt_lock.c @@ -17,16 +17,17 @@ #include #include #include -#define BT_LOCK_DMS1 0 /* DMS1 */ -#define BT_LOCK_DMS2_RW 1 /* DMS2/rw */ -#define BT_LOCK_DMS2_RO 2 /* DMS2/ro */ -#define BT_LOCK_WRITER 3 /* WRITER lock */ -#define BT_LOCK_CKPTER 4 /* CHECKPOINTER lock */ -#define BT_LOCK_READER0 5 /* Array of BT_NREADER locks */ +#define BT_LOCK_DMS1 0 /* DMS1 */ +#define BT_LOCK_DMS2_RW 1 /* DMS2/rw */ +#define BT_LOCK_DMS2_RO 2 /* DMS2/ro */ +#define BT_LOCK_WRITER 3 /* WRITER lock */ +#define BT_LOCK_CKPTER 4 /* CHECKPOINTER lock */ +#define BT_LOCK_READER_DBONLY 5 /* Reading the db file only */ +#define BT_LOCK_READER0 6 /* Array of BT_NREADER locks */ #define BT_LOCK_UNLOCK 0 #define BT_LOCK_SHARED 1 #define BT_LOCK_EXCL 2 @@ -332,38 +333,157 @@ } btLockMutexLeave(); return rc; } -int sqlite4BtLockCheckpoint(BtLock *p, int (*xCkpt)(BtLock*)){ - return xCkpt(p); -} - -int sqlite4BtLockBegin(BtLock *p, int eLock){ - return SQLITE4_OK; -} - -int sqlite4BtLockEnd(BtLock *p, int eLock){ - return SQLITE4_OK; -} - /* ** Obtain a READER lock. ** ** Argument aLog points to an array of 6 frame addresses. These are the ** first and last frames in each of log regions A, B and C. Argument ** aLock points to the array of read-lock slots in shared memory. */ -int sqlite4BtLockReader(BtLock *pLock, u32 *aLog, u32 *aLock){ - /* todo... */ - return SQLITE4_OK; +int sqlite4BtLockReader( + BtLock *pLock, /* Lock module handle */ + u32 *aLog, /* Current log file topology */ + BtReadSlot *aSlot /* Array of read-lock slots (in shmem) */ +){ + int rc = SQLITE4_BUSY; /* Return code */ + int i; /* Loop counter */ + + /* Find the first and last log frames that this client will use. */ + u32 iLast = aLog[5]; + u32 iFirst = 0; + for(i=0; iFirst==0 && i<3; i++){ + iFirst = aLog[i*2]; + } + + if( iFirst==0 ){ + rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0); + }else{ + int nAttempt = 100; /* Remaining lock attempts */ + + while( rc==SQLITE4_BUSY && (nAttempt--)>0 ){ + + /* Try to find a slot populated with the values required. */ + for(i=0; ipLock)); int rc = SQLITE4_NOTFOUND; u32 iFrame = 0; int i; + + int bSeen = (iSafe==0); /* Loop through regions (c), (b) and (a) of the log file. In that order. */ for(i=2; i>=0 && rc==SQLITE4_NOTFOUND; i--){ u32 iLo = pLog->snapshot.aLog[i*2+0]; u32 iHi = pLog->snapshot.aLog[i*2+1]; @@ -848,13 +845,24 @@ iHashLast = btLogFrameHash(pLog, iLo); iSide = (pLog->snapshot.iHashSide + (i==0)) % 2; for( ; rc==SQLITE4_NOTFOUND && iHash>=iHashLast; iHash--){ rc = btLogHashSearch(pLog, iSide, iHash, iHi, pgno, &iFrame); - if( rc==SQLITE4_OK && (iFrameiHi) ){ - rc = SQLITE4_NOTFOUND; + if( rc==SQLITE4_OK ){ + if( iFrameiHi ){ + rc = SQLITE4_NOTFOUND; + }else{ + if( iSafe>=iLo && iSafe<=iHi ){ + if( iFrame>iSafe ) return SQLITE4_NOTFOUND; + }else if( bSeen==0 ){ + return SQLITE4_NOTFOUND; + } + } } + } + if( (iSafe>=iLo && iSafe<=iHi) ){ + bSeen = 1; } } if( rc==SQLITE4_OK ){ bt_env *pVfs = pLog->pLock->pVfs; @@ -869,10 +877,25 @@ #endif } return rc; } + +/* +** Attempt to read data for page pgno from the log file. If successful, +** the data is written into buffer aData[] (which must be at least as +** large as a database page). In this case SQLITE4_OK is returned. +** +** If the log does not contain any version of page pgno, SQLITE4_NOTFOUND +** is returned and the contents of buffer aData[] are not modified. +** +** If any other error occurs, an SQLite4 error code is returned. The final +** state of buffer aData[] is undefined in this case. +*/ +int sqlite4BtLogRead(BtLog *pLog, u32 pgno, u8 *aData){ + return btLogRead(pLog, pgno, aData, 0); +} /* ** Write a frame to the log file. */ int sqlite4BtLogWrite(BtLog *pLog, u32 pgno, u8 *aData, int bCommit){ @@ -1033,11 +1056,11 @@ /* Attempt to read a copy of the BtShmHdr from shared-memory. */ rc = btLogSnapshot(pLog, &pLog->snapshot); /* Take a read lock on the database */ if( rc==SQLITE4_OK ){ - u32 *aReadlock = btLogShm(pLog)->aReadlock; + BtReadSlot *aReadlock = btLogShm(pLog)->aReadlock; rc = sqlite4BtLockReader(pLog->pLock, pLog->snapshot.aLog, aReadlock); } /* Check that the BtShmHdr in shared-memory has not changed. If it has, ** drop the read-lock and re-attempt the entire operation. */ @@ -1126,10 +1149,29 @@ int nRight = MIN(nMerge, nPgno-iLeft-nLeft); btLogMergeInplace(aLeft, nLeft, aRight, nRight, aSpace, pnPgno); } } } + +int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame){ + int i; + int iRet = 0; + for(i=0; i<3; i++){ + u32 iFirst = aLog[i*2]; + u32 iLast = aLog[i*2+1]; + if( iFirst ){ + if( iFrame>=iFirst && iFrame<=iLast ){ + iRet += (iFrame - iFirst); + return iRet; + }else{ + iRet += (iLast - iFirst); + } + } + } + if( i==3 ) return -1; + return iRet; +} /* ** Parameters iFirst and iLast are frame numbers for frames that are part ** of the current log. This function scans the wal-index from iFirst to ** iLast (inclusive) and records the set of page numbers that occur once. @@ -1138,20 +1180,31 @@ */ static int btLogGatherPgno( BtLog *pLog, /* Log module handle */ u32 **paPgno, /* OUT: s4_malloc'd array of sorted pgno */ int *pnPgno, /* OUT: Number of entries in *paPgno */ - u32 *piLastFrame + u32 *piLastFrame /* OUT: Last frame checkpointed */ ){ + BtShm *pShm = btLogShm(pLog); + BtLock *pLock = pLog->pLock; u32 *aLog = pLog->snapshot.aLog;/* Log file topology */ u32 i; u32 *aPgno; /* Returned array */ int nPgno; /* Elements in aPgno[] */ u32 *aSpace; /* Temporary space used by merge-sort */ int nMax; int rc = SQLITE4_OK; int iRegion; + int bLocked; + u32 iSafe; /* Last frame in log it is safe to gather */ + + *paPgno = 0; + *pnPgno = 0; + *piLastFrame = 0; + + rc = sqlite4BtLockReaderQuery(pLock, aLog, pShm->aReadlock, &iSafe, &bLocked); + if( rc!=SQLITE4_OK || bLocked ) return rc; /* Determine an upper limit on the number of distinct page numbers. This ** limit is used to allocate space for the returned array. */ nMax = 0; for(iRegion=0; iRegion<3; iRegion++){ @@ -1169,10 +1222,16 @@ /* Copy the required page numbers into the allocated array */ for(iRegion=0; iRegion<3; iRegion++){ u32 iFirst = aLog[iRegion*2]; u32 iLast = aLog[iRegion*2+1]; if( iFirst ){ + /* If the last frame that it is safe to gather is part of this + ** region, gather no frames that lie beyond it. */ + if( iSafe>=iFirst && iSafe<=iLast ){ + iLast = iSafe; + } + for(i=iFirst; rc==SQLITE4_OK && i<=iLast; i++){ int iHash = btLogFrameHash(pLog, i); u32 *aPage; ht_slot *aHash; u32 iZero; @@ -1185,10 +1244,14 @@ if( rc==SQLITE4_OK ){ aPgno[nPgno++] = aPage[i-iZero]; } } *piLastFrame = iLast; + + /* If the last frame of this region is the last frame that it is + ** safe to gather, break out of the loop. */ + if( iLast==iSafe ) break; } } /* Sort the contents of the array in ascending order. This step also ** eliminates any duplicate page numbers. */ @@ -1249,15 +1312,17 @@ } /* Copy data from the log file to the database file. */ for(i=0; rc==SQLITE4_OK && ixWrite(pFd, iOff, aBuf, pgsz); + }else if( rc==SQLITE4_NOTFOUND ){ + rc = SQLITE4_OK; } } /* Update the first field of the checkpoint-header. This tells readers ** that they need not consider anything that in the log before this @@ -1305,7 +1370,6 @@ sqlite4_free(pLock->pEnv, aBuf); sqlite4BtLockCkptUnlock(pLog->pLock); } return rc; } -