Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -3360,11 +3360,11 @@ ** If not, truncate the file to zero length. */ rc = SQLITE_OK; if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ if( ftruncate(pShmNode->h, 0) ){ - rc = SQLITE_IOERR; + rc = SQLITE_IOERR_SHMOPEN; } } if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); } @@ -3467,11 +3467,11 @@ if( fstat(pShmNode->h, &sStat)==0 ){ *pNewSize = (int)sStat.st_size; if( reqSize<=(int)sStat.st_size ) break; }else{ *pNewSize = 0; - rc = SQLITE_IOERR; + rc = SQLITE_IOERR_SHMSIZE; break; } rc = ftruncate(pShmNode->h, reqSize); reqSize = -1; } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -386,11 +386,11 @@ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ #define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ #define SQLITE_EMPTY 16 /* Database is empty */ #define SQLITE_SCHEMA 17 /* The database schema changed */ #define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ #define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ #define SQLITE_MISMATCH 20 /* Data type mismatch */ @@ -442,10 +442,13 @@ #define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) #define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) #define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) #define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) +#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) +#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) +#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) /* Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -375,10 +375,13 @@ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ WalIndexHdr hdr; /* Wal-index header for current transaction */ char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ +#ifdef SQLITE_DEBUG + u8 lockError; /* True if a locking error has occurred */ +#endif }; /* ** Return a pointer to the WalCkptInfo structure in the wal-index. */ @@ -627,10 +630,11 @@ if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, SQLITE_SHM_LOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal, walLockName(lockIdx), rc ? "failed" : "ok")); + VVA_ONLY( pWal->lockError = (rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) return rc; } static void walUnlockShared(Wal *pWal, int lockIdx){ if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1, @@ -642,10 +646,11 @@ if( pWal->exclusiveMode ) return SQLITE_OK; rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, walLockName(lockIdx), n, rc ? "failed" : "ok")); + VVA_ONLY( pWal->lockError = (rc!=SQLITE_OK && rc!=SQLITE_BUSY); ) return rc; } static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ if( pWal->exclusiveMode ) return; (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, @@ -1691,19 +1696,25 @@ ** checkpoint process do as much work as possible. This routine might ** update values of the aReadMark[] array in the header, but if it does ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ -static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal){ +static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ volatile WalIndexHdr *pHdr; /* Header of the wal-index */ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ u32 mxReadMark; /* Largest aReadMark[] value */ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc; /* Return code */ assert( pWal->readLock<0 ); /* Not currently locked */ + + /* Take steps to avoid spinning forever if there is a protocol error. */ + if( cnt>5 ){ + if( cnt>100 ) return SQLITE_PROTOCOL; + sqlite3OsSleep(pWal->pVfs, 1); + } if( !useWal ){ rc = walIndexReadHdr(pWal, pChanged); if( rc==SQLITE_BUSY ){ /* If there is not a recovery running in another thread or process @@ -1818,13 +1829,14 @@ ** Pager layer will use this to know that is cache is stale and ** needs to be flushed. */ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ + int cnt = 0; /* Number of TryBeginRead attempts */ do{ - rc = walTryBeginRead(pWal, pChanged, 0); + rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); walIndexUnmap(pWal); return rc; } @@ -1857,12 +1869,12 @@ int rc; /* Return code */ u32 iRead = 0; /* If !=0, WAL frame to return data from */ u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */ int iHash; /* Used to loop through N hash tables */ - /* This routine is only called from within a read transaction */ - assert( pWal->readLock>=0 ); + /* This routine is only be called from within a read transaction. */ + assert( pWal->readLock>=0 || pWal->lockError ); /* If the "last page" field of the wal-index header snapshot is 0, then ** no data will be read from the wal under any circumstances. Return early ** in this case to avoid the walIndexMap/Unmap overhead. Likewise, if ** pWal->readLock==0, then the WAL is ignored by the reader so @@ -1980,11 +1992,11 @@ /* ** Set *pPgno to the size of the database file (or zero, if unknown). */ void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno){ - assert( pWal->readLock>=0 ); + assert( pWal->readLock>=0 || pWal->lockError ); *pPgno = pWal->hdr.nPage; } /* @@ -2131,10 +2143,12 @@ ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned ** if some error */ static int walRestartLog(Wal *pWal){ int rc = SQLITE_OK; + int cnt; + if( pWal->readLock==0 && SQLITE_OK==(rc = walIndexMap(pWal, walMappingSize(pWal->hdr.mxFrame))) ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); assert( pInfo->nBackfill==pWal->hdr.mxFrame ); @@ -2161,13 +2175,14 @@ walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; + cnt = 0; do{ int notUsed; - rc = walTryBeginRead(pWal, ¬Used, 1); + rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); /* Unmap the wal-index before returning. Otherwise the VFS layer may ** hold a mutex for the duration of the IO performed by WalFrames(). */ @@ -2416,26 +2431,29 @@ ** should acquire the database exclusive lock prior to invoking ** the op==1 case. */ int sqlite3WalExclusiveMode(Wal *pWal, int op){ int rc; - assert( pWal->writeLock==0 && pWal->readLock>=0 ); + assert( pWal->writeLock==0 ); + /* pWal->readLock is usually set, but might be -1 if there was a prior OOM */ + assert( pWal->readLock>=0 || pWal->lockError ); if( op==0 ){ if( pWal->exclusiveMode ){ pWal->exclusiveMode = 0; - if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){ + if( pWal->readLock>=0 + && walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK + ){ pWal->exclusiveMode = 1; } rc = pWal->exclusiveMode==0; }else{ - /* No changes. Either already in locking_mode=NORMAL or else the - ** acquisition of the read-lock failed. The pager must continue to - ** hold the database exclusive lock. */ + /* Already in locking_mode=NORMAL */ rc = 0; } }else if( op>0 ){ assert( pWal->exclusiveMode==0 ); + assert( pWal->readLock>=0 ); walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); pWal->exclusiveMode = 1; rc = 1; }else{ rc = pWal->exclusiveMode==0;