1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-16 23:02:26 +03:00

Initial code for incremental checkpoint in WAL mode. This check-in compiles

on unix and runs as long as you do not engage WAL mode.  WAL mode crashes and
burns.  Consider this check-in a baseline implementation for getting the new
capability up and running.

FossilOrigin-Name: ef3ba7a17ff90674d702e5694b9e792851ab6998
This commit is contained in:
drh
2010-05-30 19:55:15 +00:00
parent a7a0c615d7
commit 73b64e4d2e
13 changed files with 811 additions and 617 deletions

View File

@@ -110,8 +110,8 @@ int sqlite3OsShmGet(sqlite3_file *id,int reqSize,int *pSize,void volatile **pp){
int sqlite3OsShmRelease(sqlite3_file *id){
return id->pMethods->xShmRelease(id);
}
int sqlite3OsShmLock(sqlite3_file *id, int desiredLock, int *pGotLock){
return id->pMethods->xShmLock(id, desiredLock, pGotLock);
int sqlite3OsShmLock(sqlite3_file *id, int offset, int n, int flags){
return id->pMethods->xShmLock(id, offset, n, flags);
}
void sqlite3OsShmBarrier(sqlite3_file *id){
id->pMethods->xShmBarrier(id);

View File

@@ -247,7 +247,7 @@ int sqlite3OsShmOpen(sqlite3_file *id);
int sqlite3OsShmSize(sqlite3_file *id, int, int*);
int sqlite3OsShmGet(sqlite3_file *id, int, int*, void volatile**);
int sqlite3OsShmRelease(sqlite3_file *id);
int sqlite3OsShmLock(sqlite3_file *id, int, int*);
int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
void sqlite3OsShmBarrier(sqlite3_file *id);
int sqlite3OsShmClose(sqlite3_file *id, int);

View File

@@ -3168,30 +3168,20 @@ struct unixShmNode {
struct unixShm {
unixShmNode *pShmNode; /* The underlying unixShmNode object */
unixShm *pNext; /* Next unixShm with the same unixShmNode */
u8 lockState; /* Current lock state */
u8 hasMutex; /* True if holding the unixShmNode mutex */
u8 hasMutexBuf; /* True if holding pFile->mutexBuf */
u8 sharedMask; /* Mask of shared locks held */
u8 exclMask; /* Mask of exclusive locks held */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
#ifdef SQLITE_DEBUG
u8 id; /* Id of this connection within its unixShmNode */
#endif
};
/*
** Size increment by which shared memory grows
*/
#define SQLITE_UNIX_SHM_INCR 4096
/*
** Constants used for locking
*/
#define UNIX_SHM_BASE 80 /* Byte offset of the first lock byte */
#define UNIX_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */
#define UNIX_SHM_A 0x10 /* Mask for region locks... */
#define UNIX_SHM_B 0x20
#define UNIX_SHM_C 0x40
#define UNIX_SHM_D 0x80
#define UNIX_SHM_DMS 80 /* The deadman switch lock */
#ifdef SQLITE_DEBUG
/*
@@ -3205,30 +3195,32 @@ struct unixShm {
** This routine is for debugging purposes only and does not appear
** in a production build.
*/
static const char *unixShmLockString(u8 mask){
static char zBuf[48];
static const char *unixShmLockString(u16 maskShared, u16 maskExclusive){
static char zBuf[52];
static int iBuf = 0;
int i;
u16 mask;
char *z;
z = &zBuf[iBuf];
iBuf += 8;
iBuf += 16;
if( iBuf>=sizeof(zBuf) ) iBuf = 0;
z[0] = (mask & UNIX_SHM_DMS) ? 'S' : '.';
z[1] = (mask & UNIX_SHM_A) ? 'A' : '.';
z[2] = (mask & UNIX_SHM_B) ? 'B' : '.';
z[3] = (mask & UNIX_SHM_C) ? 'C' : '.';
z[4] = (mask & UNIX_SHM_D) ? 'D' : '.';
z[5] = 0;
for(i=0, mask=1; i<SQLITE_SHM_NLOCK; i++, mask += mask){
if( mask & maskShared ){
z[i] = 's';
}else if( mask & maskExclusive ){
z[i] = 'E';
}else{
z[i] = '.';
}
}
z[i] = 0;
return z;
}
#endif /* SQLITE_DEBUG */
/*
** Apply posix advisory locks for all bytes identified in lockMask.
**
** lockMask might contain multiple bits but all bits are guaranteed
** to be contiguous.
** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
**
** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
** otherwise.
@@ -3236,198 +3228,69 @@ static const char *unixShmLockString(u8 mask){
static int unixShmSystemLock(
unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */
int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */
u8 lockMask /* Which bytes to lock or unlock */
int ofst, /* First byte of the locking range */
int n /* Number of bytes to lock */
){
struct flock f; /* The posix advisory locking structure */
int lockOp; /* The opcode for fcntl() */
int i; /* Offset into the locking byte range */
int rc; /* Result code form fcntl() */
u8 mask; /* Mask of bits in lockMask */
int rc = SQLITE_OK; /* Result code form fcntl() */
/* Access to the unixShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
assert( n>=1 && ofst>=0 && ofst+n<SQLITE_SHM_NLOCK );
/* Initialize the locking parameters */
memset(&f, 0, sizeof(f));
f.l_type = lockType;
f.l_whence = SEEK_SET;
if( lockMask==UNIX_SHM_C && lockType!=F_UNLCK ){
lockOp = F_SETLKW;
OSTRACE(("SHM-LOCK requesting blocking lock\n"));
}else{
lockOp = F_SETLK;
}
f.l_start = ofst+UNIX_SHM_BASE;
f.l_len = n;
/* Find the first bit in lockMask that is set */
for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){}
assert( mask!=0 );
f.l_start = i+UNIX_SHM_BASE;
f.l_len = 1;
/* Extend the locking range for each additional bit that is set */
mask <<= 1;
while( mask!=0 && (lockMask & mask)!=0 ){
f.l_len++;
mask <<= 1;
}
/* Verify that all bits set in lockMask are contiguous */
assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 );
/* Acquire the system-level lock */
rc = fcntl(pShmNode->h, lockOp, &f);
rc = fcntl(pShmNode->h, F_SETLK, &f);
rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
/* Update the global lock state and do debug tracing */
#ifdef SQLITE_DEBUG
{ u16 mask;
OSTRACE(("SHM-LOCK "));
mask = (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
OSTRACE(("unlock ok"));
pShmNode->exclMask &= ~lockMask;
pShmNode->sharedMask &= ~lockMask;
OSTRACE(("unlock %d ok", ofst));
pShmNode->exclMask &= ~mask;
pShmNode->sharedMask &= ~mask;
}else if( lockType==F_RDLCK ){
OSTRACE(("read-lock ok"));
pShmNode->exclMask &= ~lockMask;
pShmNode->sharedMask |= lockMask;
OSTRACE(("read-lock %d ok", ofst));
pShmNode->exclMask &= ~mask;
pShmNode->sharedMask |= mask;
}else{
assert( lockType==F_WRLCK );
OSTRACE(("write-lock ok"));
pShmNode->exclMask |= lockMask;
pShmNode->sharedMask &= ~lockMask;
OSTRACE(("write-lock %d ok", ofst));
pShmNode->exclMask |= mask;
pShmNode->sharedMask &= ~mask;
}
}else{
if( lockType==F_UNLCK ){
OSTRACE(("unlock failed"));
OSTRACE(("unlock %d failed", ofst));
}else if( lockType==F_RDLCK ){
OSTRACE(("read-lock failed"));
}else{
assert( lockType==F_WRLCK );
OSTRACE(("write-lock failed"));
OSTRACE(("write-lock %d failed", ofst));
}
}
OSTRACE((" - change requested %s - afterwards %s:%s\n",
unixShmLockString(lockMask),
unixShmLockString(pShmNode->sharedMask),
unixShmLockString(pShmNode->exclMask)));
OSTRACE((" - afterwards %s\n",
unixShmLockString(pShmNode->sharedMask, pShmNode->exclMask)));
}
#endif
return rc;
}
/*
** For connection p, unlock all of the locks identified by the unlockMask
** parameter.
*/
static int unixShmUnlock(
unixShmNode *pShmNode, /* The underlying shared-memory file */
unixShm *p, /* The connection to be unlocked */
u8 unlockMask /* Mask of locks to be unlocked */
){
int rc; /* Result code */
unixShm *pX; /* For looping over all sibling connections */
u8 allMask; /* Union of locks held by connections other than "p" */
/* Access to the unixShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pShmNode->mutex) );
/* Compute locks held by sibling connections */
allMask = 0;
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
allMask |= pX->sharedMask;
}
/* Unlock the system-level locks */
if( (unlockMask & allMask)!=unlockMask ){
rc = unixShmSystemLock(pShmNode, F_UNLCK, unlockMask & ~allMask);
}else{
rc = SQLITE_OK;
}
/* Undo the local locks */
if( rc==SQLITE_OK ){
p->exclMask &= ~unlockMask;
p->sharedMask &= ~unlockMask;
}
return rc;
}
/*
** Get reader locks for connection p on all locks in the readMask parameter.
*/
static int unixShmSharedLock(
unixShmNode *pShmNode, /* The underlying shared-memory file */
unixShm *p, /* The connection to get the shared locks */
u8 readMask /* Mask of shared locks to be acquired */
){
int rc; /* Result code */
unixShm *pX; /* For looping over all sibling connections */
u8 allShared; /* Union of locks held by connections other than "p" */
/* Access to the unixShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pShmNode->mutex) );
/* Find out which shared locks are already held by sibling connections.
** If any sibling already holds an exclusive lock, go ahead and return
** SQLITE_BUSY.
*/
allShared = 0;
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY;
allShared |= pX->sharedMask;
}
/* Get shared locks at the system level, if necessary */
if( (~allShared) & readMask ){
rc = unixShmSystemLock(pShmNode, F_RDLCK, readMask);
}else{
rc = SQLITE_OK;
}
/* Get the local shared locks */
if( rc==SQLITE_OK ){
p->sharedMask |= readMask;
}
return rc;
}
/*
** For connection p, get an exclusive lock on all locks identified in
** the writeMask parameter.
*/
static int unixShmExclusiveLock(
unixShmNode *pShmNode, /* The underlying shared-memory file */
unixShm *p, /* The connection to get the exclusive locks */
u8 writeMask /* Mask of exclusive locks to be acquired */
){
int rc; /* Result code */
unixShm *pX; /* For looping over all sibling connections */
/* Access to the unixShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pShmNode->mutex) );
/* Make sure no sibling connections hold locks that will block this
** lock. If any do, return SQLITE_BUSY right away.
*/
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY;
if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY;
}
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
rc = unixShmSystemLock(pShmNode, F_WRLCK, writeMask);
if( rc==SQLITE_OK ){
p->sharedMask &= ~writeMask;
p->exclMask |= writeMask;
}
return rc;
}
/*
** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
@@ -3520,13 +3383,13 @@ static int unixShmOpen(
** If not, truncate the file to zero length.
*/
rc = SQLITE_OK;
if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS)==SQLITE_OK ){
if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
if( ftruncate(pShmNode->h, 0) ){
rc = SQLITE_IOERR;
}
}
if( rc==SQLITE_OK ){
rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS);
rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1);
}
if( rc ) goto shm_open_err;
}
@@ -3687,7 +3550,7 @@ static int unixShmGet(
assert( pShmNode==pDbFd->pInode->pShmNode );
assert( pShmNode->pInode==pDbFd->pInode );
if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){
if( p->hasMutexBuf==0 ){
assert( sqlite3_mutex_notheld(pShmNode->mutex) );
sqlite3_mutex_enter(pShmNode->mutexBuf);
p->hasMutexBuf = 1;
@@ -3731,7 +3594,7 @@ static int unixShmRelease(sqlite3_file *fd){
unixFile *pDbFd = (unixFile*)fd;
unixShm *p = pDbFd->pShm;
if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){
if( p->hasMutexBuf ){
assert( sqlite3_mutex_notheld(p->pShmNode->mutex) );
sqlite3_mutex_leave(p->pShmNode->mutexBuf);
p->hasMutexBuf = 0;
@@ -3739,147 +3602,113 @@ static int unixShmRelease(sqlite3_file *fd){
return SQLITE_OK;
}
/*
** Symbolic names for LOCK states used for debugging.
*/
#ifdef SQLITE_DEBUG
static const char *azLkName[] = {
"UNLOCK",
"READ",
"READ_FULL",
"WRITE",
"PENDING",
"CHECKPOINT",
"RECOVER"
};
#endif
/*
** Change the lock state for a shared-memory segment.
*/
static int unixShmLock(
sqlite3_file *fd, /* Database file holding the shared memory */
int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */
int *pGotLock /* The lock you actually got */
int ofst, /* First lock to acquire or release */
int n, /* Number of locks to acquire or release */
int flags /* What to do with the lock */
){
unixFile *pDbFd = (unixFile*)fd;
unixShm *p = pDbFd->pShm;
unixShmNode *pShmNode = p->pShmNode;
int rc = SQLITE_PROTOCOL;
unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */
unixShm *p = pDbFd->pShm; /* The shared memory being locked */
unixShm *pX; /* For looping over all siblings */
unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */
u16 mask; /* Mask of locks to take or release */
assert( pShmNode==pDbFd->pInode->pShmNode );
assert( pShmNode->pInode==pDbFd->pInode );
assert( ofst>=0 && ofst+n<SQLITE_SHM_NLOCK );
assert( n>=1 );
assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
|| flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
/* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never
** directly requested; they are side effects from requesting
** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively.
*/
assert( desiredLock==SQLITE_SHM_UNLOCK
|| desiredLock==SQLITE_SHM_READ
|| desiredLock==SQLITE_SHM_WRITE
|| desiredLock==SQLITE_SHM_CHECKPOINT
|| desiredLock==SQLITE_SHM_RECOVER );
/* Return directly if this is just a lock state query, or if
** the connection is already in the desired locking state.
*/
if( desiredLock==p->lockState
|| (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL)
){
OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s and got %s\n",
p->id, getpid(), azLkName[desiredLock], azLkName[p->lockState]));
if( pGotLock ) *pGotLock = p->lockState;
return SQLITE_OK;
}
OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s->%s\n",
p->id, getpid(), azLkName[p->lockState], azLkName[desiredLock]));
if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){
assert( sqlite3_mutex_notheld(pShmNode->mutex) );
sqlite3_mutex_enter(pShmNode->mutexBuf);
p->hasMutexBuf = 1;
}
mask = (1<<(ofst+n+1)) - (1<<(ofst+1));
assert( n>1 || mask==(1<<ofst) );
sqlite3_mutex_enter(pShmNode->mutex);
switch( desiredLock ){
case SQLITE_SHM_UNLOCK: {
assert( p->lockState!=SQLITE_SHM_RECOVER );
unixShmUnlock(pShmNode, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D);
if( flags & SQLITE_SHM_UNLOCK ){
u16 allMask = 0; /* Mask of locks held by siblings */
/* See if any siblings hold this same lock */
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
allMask |= pX->sharedMask;
}
/* Unlock the system-level locks */
if( (mask & allMask)==0 ){
rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+1, n);
}else{
rc = SQLITE_OK;
p->lockState = SQLITE_SHM_UNLOCK;
break;
}
case SQLITE_SHM_READ: {
if( p->lockState==SQLITE_SHM_UNLOCK ){
int nAttempt;
/* Undo the local locks */
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
}
}else if( flags & SQLITE_SHM_SHARED ){
u16 allShared = 0; /* Union of locks held by connections other than "p" */
/* Find out which shared locks are already held by sibling connections.
** If any sibling already holds an exclusive lock, go ahead and return
** SQLITE_BUSY.
*/
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
if( (pX->exclMask & mask)!=0 ){
rc = SQLITE_BUSY;
assert( p->lockState==SQLITE_SHM_UNLOCK );
for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){
rc = unixShmSharedLock(pShmNode, p, UNIX_SHM_A|UNIX_SHM_B);
if( rc==SQLITE_BUSY ){
rc = unixShmSharedLock(pShmNode, p, UNIX_SHM_D);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_READ_FULL;
}
}else{
unixShmUnlock(pShmNode, p, UNIX_SHM_B);
p->lockState = SQLITE_SHM_READ;
}
}
break;
}
allShared |= pX->sharedMask;
}
/* Get shared locks at the system level, if necessary */
if( rc==SQLITE_OK ){
if( (allShared & mask)==0 ){
rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+1, n);
}else{
assert( p->lockState==SQLITE_SHM_WRITE
|| p->lockState==SQLITE_SHM_RECOVER );
rc = unixShmSharedLock(pShmNode, p, UNIX_SHM_A);
unixShmUnlock(pShmNode, p, UNIX_SHM_C|UNIX_SHM_D);
p->lockState = SQLITE_SHM_READ;
rc = SQLITE_OK;
}
break;
}
case SQLITE_SHM_WRITE: {
assert( p->lockState==SQLITE_SHM_READ
|| p->lockState==SQLITE_SHM_READ_FULL );
rc = unixShmExclusiveLock(pShmNode, p, UNIX_SHM_C|UNIX_SHM_D);
/* Get the local shared locks */
if( rc==SQLITE_OK ){
p->sharedMask |= mask;
}
}else{
/* Make sure no sibling connections hold locks that will block this
** lock. If any do, return SQLITE_BUSY right away.
*/
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
rc = SQLITE_BUSY;
break;
}
}
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
if( rc==SQLITE_OK ){
rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+1, n);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_WRITE;
p->sharedMask &= ~mask;
p->exclMask |= mask;
}
break;
}
case SQLITE_SHM_CHECKPOINT: {
assert( p->lockState==SQLITE_SHM_UNLOCK
|| p->lockState==SQLITE_SHM_PENDING
);
if( p->lockState==SQLITE_SHM_UNLOCK ){
rc = unixShmExclusiveLock(pShmNode, p, UNIX_SHM_B|UNIX_SHM_C);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_PENDING;
}
}
if( p->lockState==SQLITE_SHM_PENDING ){
rc = unixShmExclusiveLock(pShmNode, p, UNIX_SHM_A);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_CHECKPOINT;
}
}
break;
}
default: {
assert( desiredLock==SQLITE_SHM_RECOVER );
assert( p->lockState==SQLITE_SHM_READ
|| p->lockState==SQLITE_SHM_READ_FULL
);
assert( sqlite3_mutex_held(pShmNode->mutexBuf) );
rc = unixShmExclusiveLock(pShmNode, p, UNIX_SHM_C);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_RECOVER;
}
break;
}
}
sqlite3_mutex_leave(pShmNode->mutex);
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %s\n",
p->id, getpid(), azLkName[p->lockState]));
if( pGotLock ) *pGotLock = p->lockState;
p->id, getpid(), unixShmLockString(p->sharedMask, p->exclMask)));
return rc;
}

View File

@@ -1203,7 +1203,7 @@ static int pagerUseWal(Pager *pPager){
# define pagerRollbackWal(x) 0
# define pagerWalFrames(v,w,x,y,z) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
# define pagerOpenSnapshot(z) SQLITE_OK
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
/*
@@ -1238,7 +1238,7 @@ static void pager_unlock(Pager *pPager){
pPager->dbSizeValid = 0;
if( pagerUseWal(pPager) ){
sqlite3WalCloseSnapshot(pPager->pWal);
sqlite3WalEndReadTransaction(pPager->pWal);
}else{
rc = osUnlock(pPager->fd, NO_LOCK);
}
@@ -1437,7 +1437,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
sqlite3PcacheCleanAll(pPager->pPCache);
if( pagerUseWal(pPager) ){
rc2 = sqlite3WalWriteLock(pPager->pWal, 0);
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
pPager->state = PAGER_SHARED;
/* If the connection was in locking_mode=exclusive mode but is no longer,
@@ -2362,15 +2362,20 @@ static int pagerWalFrames(
}
/*
** Open a WAL snapshot on the log file this pager is connected to.
** Begin a read transaction on the WAL.
**
** This routine used to be called "pagerOpenSnapshot()" because it essentially
** makes a snapshot of the database at the current point in time and preserves
** that snapshot for use by the reader in spite of concurrently changes by
** other writers or checkpointers.
*/
static int pagerOpenSnapshot(Pager *pPager){
static int pagerBeginReadTransaction(Pager *pPager){
int rc; /* Return code */
int changed = 0; /* True if cache must be reset */
assert( pagerUseWal(pPager) );
rc = sqlite3WalOpenSnapshot(pPager->pWal, &changed);
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
if( rc==SQLITE_OK ){
int dummy;
if( changed ){
@@ -2428,7 +2433,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){
pager_reset(pPager);
rc = sqlite3PagerOpenWal(pPager, 0);
if( rc==SQLITE_OK ){
rc = pagerOpenSnapshot(pPager);
rc = pagerBeginReadTransaction(pPager);
}
}else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
pPager->journalMode = PAGER_JOURNALMODE_DELETE;
@@ -4002,7 +4007,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
}
if( pagerUseWal(pPager) ){
rc = pagerOpenSnapshot(pPager);
rc = pagerBeginReadTransaction(pPager);
}else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
sqlite3_vfs * const pVfs = pPager->pVfs;
int isHotJournal = 0;
@@ -4561,7 +4566,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
** may copy data from the sub-journal into the database file as well
** as into the page cache. Which would be incorrect in WAL mode.
*/
rc = sqlite3WalWriteLock(pPager->pWal, 1);
rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
if( rc==SQLITE_OK ){
pPager->dbOrigSize = pPager->dbSize;
pPager->state = PAGER_RESERVED;
@@ -5892,8 +5897,7 @@ int sqlite3PagerCheckpoint(Pager *pPager){
u8 *zBuf = (u8 *)pPager->pTmpSpace;
rc = sqlite3WalCheckpoint(pPager->pWal,
(pPager->noSync ? 0 : pPager->sync_flags),
pPager->pageSize, zBuf,
pPager->xBusyHandler, pPager->pBusyHandlerArg
pPager->pageSize, zBuf
);
}
return rc;

View File

@@ -444,7 +444,8 @@ int sqlite3_exec(
#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_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) )
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@@ -658,7 +659,7 @@ struct sqlite3_io_methods {
int (*xShmSize)(sqlite3_file*, int reqSize, int *pNewSize);
int (*xShmGet)(sqlite3_file*, int reqSize, int *pSize, void volatile**);
int (*xShmRelease)(sqlite3_file*);
int (*xShmLock)(sqlite3_file*, int desiredLock, int *gotLock);
int (*xShmLock)(sqlite3_file*, int offset, int n, int flags);
void (*xShmBarrier)(sqlite3_file*);
int (*xShmClose)(sqlite3_file*, int deleteFlag);
/* Methods above are valid for version 2 */
@@ -888,16 +889,40 @@ struct sqlite3_vfs {
/*
** CAPI3REF: Flags for the xShmLock VFS method
**
** These integer constants define the various locking states that
** an sqlite3_shm object can be in.
** These integer constants define the various locking operations
** allowed by the xShmLock method of [sqlite3_io_methods]. The
** following are the only legal combinations of flags to the
** xShmLock method:
**
** <ul>
** <li> SQLITE_SHM_LOCK | SQLITE_SHM_SHARED
** <li> SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE
** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED
** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE
** </ul>
**
** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
** was given no the corresponding lock.
**
** The xShmLock method can transition between unlocked and SHARED or
** between unlocked and EXCLUSIVE. It cannot transition between SHARED
** and EXCLUSIVE.
*/
#define SQLITE_SHM_UNLOCK 0
#define SQLITE_SHM_READ 1
#define SQLITE_SHM_READ_FULL 2
#define SQLITE_SHM_WRITE 3
#define SQLITE_SHM_PENDING 4
#define SQLITE_SHM_CHECKPOINT 5
#define SQLITE_SHM_RECOVER 6
#define SQLITE_SHM_UNLOCK 1
#define SQLITE_SHM_LOCK 2
#define SQLITE_SHM_SHARED 4
#define SQLITE_SHM_EXCLUSIVE 8
/*
** CAPI3REF: Maximum xShmLock index
**
** The xShmLock method on [sqlite3_io_methods] may use values
** between 0 and this upper bound as its "offset" argument.
** The SQLite core will never attempt to acquire or release a
** lock outside of this range
*/
#define SQLITE_SHM_NLOCK 8
/*
** CAPI3REF: Initialize The SQLite Library

View File

@@ -540,8 +540,8 @@ static int cfShmGet(
static int cfShmRelease(sqlite3_file *pFile){
return sqlite3OsShmRelease(((CrashFile*)pFile)->pRealFile);
}
static int cfShmLock(sqlite3_file *pFile, int desired, int *pGot){
return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, desired, pGot);
static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, ofst, n, flags);
}
static void cfShmBarrier(sqlite3_file *pFile){
sqlite3OsShmBarrier(((CrashFile*)pFile)->pRealFile);

View File

@@ -54,7 +54,7 @@ static int devsymShmOpen(sqlite3_file*);
static int devsymShmSize(sqlite3_file*,int,int*);
static int devsymShmGet(sqlite3_file*,int,int*,volatile void**);
static int devsymShmRelease(sqlite3_file*);
static int devsymShmLock(sqlite3_file*,int,int*);
static int devsymShmLock(sqlite3_file*,int,int,int);
static void devsymShmBarrier(sqlite3_file*);
static int devsymShmClose(sqlite3_file*,int);
@@ -263,9 +263,9 @@ static int devsymShmRelease(sqlite3_file *pFile){
devsym_file *p = (devsym_file *)pFile;
return sqlite3OsShmRelease(p->pReal);
}
static int devsymShmLock(sqlite3_file *pFile, int desired, int *pGot){
static int devsymShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
devsym_file *p = (devsym_file *)pFile;
return sqlite3OsShmLock(p->pReal, desired, pGot);
return sqlite3OsShmLock(p->pReal, ofst, n, flags);
}
static void devsymShmBarrier(sqlite3_file *pFile){
devsym_file *p = (devsym_file *)pFile;

View File

@@ -155,7 +155,7 @@ static int vfslogShmOpen(sqlite3_file *pFile);
static int vfslogShmSize(sqlite3_file *pFile, int reqSize, int *pNewSize);
static int vfslogShmGet(sqlite3_file *pFile, int,int*,volatile void **);
static int vfslogShmRelease(sqlite3_file *pFile);
static int vfslogShmLock(sqlite3_file *pFile, int desiredLock, int *gotLock);
static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags);
static void vfslogShmBarrier(sqlite3_file*);
static int vfslogShmClose(sqlite3_file *pFile, int deleteFlag);
@@ -460,12 +460,12 @@ static int vfslogShmRelease(sqlite3_file *pFile){
vfslog_call(p->pVfslog, OS_SHMRELEASE, p->iFileId, t, rc, 0, 0);
return rc;
}
static int vfslogShmLock(sqlite3_file *pFile, int desiredLock, int *gotLock){
static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
int rc;
sqlite3_uint64 t;
VfslogFile *p = (VfslogFile *)pFile;
t = vfslog_time();
rc = p->pReal->pMethods->xShmLock(p->pReal, desiredLock, gotLock);
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
t = vfslog_time() - t;
vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, rc, 0, 0);
return rc;

View File

@@ -102,7 +102,7 @@ static int tvfsShmOpen(sqlite3_file*);
static int tvfsShmSize(sqlite3_file*, int , int *);
static int tvfsShmGet(sqlite3_file*, int , int *, volatile void **);
static int tvfsShmRelease(sqlite3_file*);
static int tvfsShmLock(sqlite3_file*, int , int *);
static int tvfsShmLock(sqlite3_file*, int , int, int);
static void tvfsShmBarrier(sqlite3_file*);
static int tvfsShmClose(sqlite3_file*, int);
@@ -544,31 +544,34 @@ static int tvfsShmRelease(sqlite3_file *pFile){
static int tvfsShmLock(
sqlite3_file *pFile,
int desiredLock,
int *gotLock
int ofst,
int n,
int flags
){
int rc = SQLITE_OK;
TestvfsFile *pFd = (TestvfsFile *)pFile;
Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
char *zLock = "";
int nLock;
char zLock[80];
switch( desiredLock ){
case SQLITE_SHM_READ: zLock = "READ"; break;
case SQLITE_SHM_WRITE: zLock = "WRITE"; break;
case SQLITE_SHM_CHECKPOINT: zLock = "CHECKPOINT"; break;
case SQLITE_SHM_RECOVER: zLock = "RECOVER"; break;
case SQLITE_SHM_PENDING: zLock = "PENDING"; break;
case SQLITE_SHM_UNLOCK: zLock = "UNLOCK"; break;
sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
nLock = strlen(zLock);
if( flags & SQLITE_SHM_LOCK ){
strcpy(&zLock[nLock], " lock");
}else{
strcpy(&zLock[nLock], " unlock");
}
nLock += strlen(&zLock[nLock]);
if( flags & SQLITE_SHM_SHARED ){
strcpy(&zLock[nLock], " shared");
}else{
strcpy(&zLock[nLock], " exclusive");
}
tvfsExecTcl(p, "xShmLock",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
Tcl_NewStringObj(zLock, -1)
);
tvfsResultCode(p, &rc);
if( rc==SQLITE_OK ){
*gotLock = desiredLock;
}
return rc;
}
@@ -716,9 +719,7 @@ static void testvfs_obj_del(ClientData cd){
**
** SCRIPT xShmLock FILENAME ID LOCK
**
** where LOCK is one of "UNLOCK", "READ", "READ_FULL", "WRITE", "PENDING",
** "CHECKPOINT" or "RECOVER". The script should return an SQLite error
** code.
** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
*/
static int testvfs_cmd(
ClientData cd,

773
src/wal.c

File diff suppressed because it is too large Load Diff

View File

@@ -20,19 +20,20 @@
#include "sqliteInt.h"
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalOpenSnapshot(y,z) 0
# define sqlite3WalCloseSnapshot(z)
# define sqlite3WalRead(v,w,x,y,z) 0
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalEndReadTransaction(z)
# define sqlite3WalRead(v,w,x,y,z) 0
# define sqlite3WalDbsize(y,z)
# define sqlite3WalWriteLock(y,z) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWRiteTransaction(x) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalSavepoint(y,z)
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
# define sqlite3WalCheckpoint(u,v,w,x,y,z) 0
# define sqlite3WalCallback(z) 0
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
# define sqlite3WalCheckpoint(u,v,w,x) 0
# define sqlite3WalCallback(z) 0
#else
#define WAL_SAVEPOINT_NDATA 3
@@ -53,8 +54,8 @@ int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *);
** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the
** transaction and releases the lock.
*/
int sqlite3WalOpenSnapshot(Wal *pWal, int *);
void sqlite3WalCloseSnapshot(Wal *pWal);
int sqlite3WalBeginReadTransaction(Wal *pWal, int *);
void sqlite3WalEndReadTransaction(Wal *pWal);
/* Read a page from the write-ahead log, if it is present. */
int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
@@ -64,7 +65,8 @@ int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno);
/* Obtain or release the WRITER lock. */
int sqlite3WalWriteLock(Wal *pWal, int op);
int sqlite3WalBeginWriteTransaction(Wal *pWal);
int sqlite3WalEndWriteTransaction(Wal *pWal);
/* Undo any frames written (but not committed) to the log */
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);
@@ -85,9 +87,7 @@ int sqlite3WalCheckpoint(
Wal *pWal, /* Write-ahead log connection */
int sync_flags, /* Flags to sync db file with (or 0) */
int nBuf, /* Size of buffer nBuf */
u8 *zBuf, /* Temporary buffer to use */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
u8 *zBuf /* Temporary buffer to use */
);
/* Return the value to pass to a sqlite3_wal_hook callback, the