diff options
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 111 |
1 files changed, 70 insertions, 41 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index c9b59f229..0eb2d5b00 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4255,6 +4255,7 @@ struct unixShmNode { char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ + int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ @@ -4796,6 +4797,38 @@ shmpage_out: } /* +** Check that the pShmNode->aLock[] array comports with the locking bitmasks +** held by each client. Return true if it does, or false otherwise. This +** is to be used in an assert(). e.g. +** +** assert( assertLockingArrayOk(pShmNode) ); +*/ +#ifdef SQLITE_DEBUG +static int assertLockingArrayOk(unixShmNode *pShmNode){ + unixShm *pX; + int aLock[SQLITE_SHM_NLOCK]; + assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); + + memset(aLock, 0, sizeof(aLock)); + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + int i; + for(i=0; i<SQLITE_SHM_NLOCK; i++){ + if( pX->exclMask & (1<<i) ){ + assert( aLock[i]==0 ); + aLock[i] = -1; + }else if( pX->sharedMask & (1<<i) ){ + assert( aLock[i]>=0 ); + aLock[i]++; + } + } + } + + assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); + return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); +} +#endif + +/* ** Change the lock state for a shared-memory segment. ** ** Note that the relationship between SHAREd and EXCLUSIVE locks is a little @@ -4811,10 +4844,10 @@ static int unixShmLock( ){ 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 */ + int *aLock = pShmNode->aLock; assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); @@ -4853,21 +4886,25 @@ static int unixShmLock( mask = (1<<(ofst+n)) - (1<<ofst); assert( n>1 || mask==(1<<ofst) ); sqlite3_mutex_enter(pShmNode->pShmMutex); + assert( assertLockingArrayOk(pShmNode) ); if( flags & SQLITE_SHM_UNLOCK ){ - u16 allMask = 0; /* Mask of locks held by siblings */ + int ii; + int bUnlock = 1; - /* 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; + for(ii=ofst; ii<ofst+n; ii++){ + if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){ + bUnlock = 0; + } } - /* Unlock the system-level locks */ - if( (mask & allMask)==0 ){ + if( bUnlock ){ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); - }else{ - rc = SQLITE_OK; + if( rc==SQLITE_OK ){ + memset(&aLock[ofst], 0, sizeof(int)*n); + } + }else if( p->sharedMask & (1<<ofst) ){ + assert( n==1 && aLock[ofst]>1 ); + aLock[ofst]--; } /* Undo the local locks */ @@ -4876,55 +4913,47 @@ static int unixShmLock( 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->exclMask & mask)!=0 ){ + assert( n==1 ); + assert( (p->exclMask & (1<<ofst))==0 ); + if( (p->sharedMask & mask)==0 ){ + if( aLock[ofst]<0 ){ rc = SQLITE_BUSY; - break; - } - allShared |= pX->sharedMask; - } - - /* Get shared locks at the system level, if necessary */ - if( rc==SQLITE_OK ){ - if( (allShared & mask)==0 ){ + }else if( aLock[ofst]==0 ){ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); - }else{ - rc = SQLITE_OK; } - } - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + aLock[ofst]++; + } } }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->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ + ** lock. If any do, return SQLITE_BUSY right away. */ + int ii; + for(ii=ofst; ii<ofst+n; ii++){ + assert( (p->sharedMask & mask)==0 ); + if( (p->exclMask & (1<<ii))==0 && aLock[ii] ){ rc = SQLITE_BUSY; break; } } - - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. - */ + + /* Get the exclusive locks at the system level. Then if successful + ** also update the in-memory values. */ if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; + for(ii=ofst; ii<ofst+n; ii++){ + aLock[ii] = -1; + } } } } + assert( assertLockingArrayOk(pShmNode) ); sqlite3_mutex_leave(pShmNode->pShmMutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, osGetpid(0), p->sharedMask, p->exclMask)); |