diff options
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 435 |
1 files changed, 132 insertions, 303 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index 227a3c518..cdba3d9f1 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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; - } - - /* 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; + f.l_start = ofst+UNIX_SHM_BASE; + f.l_len = n; - /* 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 ); - - /* 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; - } + 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 ); + + 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; - } - } - }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; + break; } - break; + allShared |= pX->sharedMask; } - 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); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_WRITE; + + /* 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{ + rc = SQLITE_OK; } - 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; - } + + /* 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; } - 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); + + /* 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_RECOVER; + p->sharedMask &= ~mask; + p->exclMask |= mask; } - 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; } |