1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Remove the BtreeMutexArray object - use the Vdbe.btreeMask field to accomplish

the same result.  Add a generation counter to btree mutexes in order to assert
that mutexes are never temporarily dropped over a range of instructions in order
to do deadlock avoidance in some subroutine.  Lock all btrees in any Vdbe
program that uses OP_ParseSchema.

FossilOrigin-Name: d81708f7d1eee399bfe76f6b8dac950a85dc2582
This commit is contained in:
drh
2011-04-04 00:14:43 +00:00
parent dddd779b2b
commit bdaec52c62
8 changed files with 242 additions and 198 deletions

View File

@@ -39,15 +39,34 @@ static void lockBtreeMutex(Btree *p){
** clear the p->locked boolean.
*/
static void unlockBtreeMutex(Btree *p){
BtShared *pBt = p->pBt;
assert( p->locked==1 );
assert( sqlite3_mutex_held(p->pBt->mutex) );
assert( sqlite3_mutex_held(pBt->mutex) );
assert( sqlite3_mutex_held(p->db->mutex) );
assert( p->db==p->pBt->db );
assert( p->db==pBt->db );
sqlite3_mutex_leave(p->pBt->mutex);
pBt->iMutexCounter++;
sqlite3_mutex_leave(pBt->mutex);
p->locked = 0;
}
#ifdef SQLITE_DEBUG
/*
** Return the number of times that the mutex has been exited for
** the given btree.
**
** This is a small circular counter that wraps around to zero on
** overflow. It is used only for sanity checking - to verify that
** mutexes are held continously by asserting that the value of
** this counter at the beginning of a region is the same as at
** the end.
*/
u32 sqlite3BtreeMutexCounter(Btree *p){
assert( p->locked==1 || p->sharable==0 );
return p->pBt->iMutexCounter;
}
#endif
/*
** Enter a mutex on the given BTree object.
**
@@ -92,6 +111,24 @@ void sqlite3BtreeEnter(Btree *p){
p->wantToLock++;
if( p->locked ) return;
/* Increment the mutex counter on all locked btrees in the same
** database connection. This simulates the unlocking that would
** occur on a worst-case mutex dead-lock avoidance scenario.
*/
#ifdef SQLITE_DEBUG
{
int ii;
sqlite3 *db = p->db;
Btree *pOther;
for(ii=0; ii<db->nDb; ii++){
if( ii==1 ) continue;
pOther = db->aDb[ii].pBt;
if( pOther==0 || pOther->sharable==0 || pOther->locked==0 ) continue;
pOther->pBt->iMutexCounter++;
}
}
#endif
/* In most cases, we should be able to acquire the lock we
** want without having to go throught the ascending lock
** procedure that follows. Just be sure not to block.
@@ -251,97 +288,17 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
}
#endif /* NDEBUG */
#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */
/*
** Add a new Btree pointer to a BtreeMutexArray.
** if the pointer can possibly be shared with
** another database connection.
** The following are special cases for mutex enter routines for use
** in single threaded applications that use shared cache. Except for
** these two routines, all mutex operations are no-ops in that case and
** are null #defines in btree.h.
**
** The pointers are kept in sorted order by pBtree->pBt. That
** way when we go to enter all the mutexes, we can enter them
** in order without every having to backup and retry and without
** worrying about deadlock.
**
** The number of shared btrees will always be small (usually 0 or 1)
** so an insertion sort is an adequate algorithm here.
** If shared cache is disabled, then all btree mutex routines, including
** the ones below, are no-ops and are null #defines in btree.h.
*/
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
int i, j;
BtShared *pBt;
if( pBtree==0 || pBtree->sharable==0 ) return;
#ifndef NDEBUG
{
for(i=0; i<pArray->nMutex; i++){
assert( pArray->aBtree[i]!=pBtree );
}
}
#endif
assert( pArray->nMutex>=0 );
assert( pArray->nMutex<ArraySize(pArray->aBtree)-1 );
pBt = pBtree->pBt;
for(i=0; i<pArray->nMutex; i++){
assert( pArray->aBtree[i]!=pBtree );
if( pArray->aBtree[i]->pBt>pBt ){
for(j=pArray->nMutex; j>i; j--){
pArray->aBtree[j] = pArray->aBtree[j-1];
}
pArray->aBtree[i] = pBtree;
pArray->nMutex++;
return;
}
}
pArray->aBtree[pArray->nMutex++] = pBtree;
}
/*
** Enter the mutex of every btree in the array. This routine is
** called at the beginning of sqlite3VdbeExec(). The mutexes are
** exited at the end of the same function.
*/
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
int i;
for(i=0; i<pArray->nMutex; i++){
Btree *p = pArray->aBtree[i];
/* Some basic sanity checking */
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
assert( !p->locked || p->wantToLock>0 );
/* We should already hold a lock on the database connection */
assert( sqlite3_mutex_held(p->db->mutex) );
/* The Btree is sharable because only sharable Btrees are entered
** into the array in the first place. */
assert( p->sharable );
p->wantToLock++;
if( !p->locked ){
lockBtreeMutex(p);
}
}
}
/*
** Leave the mutex of every btree in the group.
*/
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
int i;
for(i=0; i<pArray->nMutex; i++){
Btree *p = pArray->aBtree[i];
/* Some basic sanity checking */
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
assert( p->locked );
assert( p->wantToLock>0 );
/* We should already hold a lock on the database connection */
assert( sqlite3_mutex_held(p->db->mutex) );
p->wantToLock--;
if( p->wantToLock==0 ){
unlockBtreeMutex(p);
}
}
}
#else
void sqlite3BtreeEnter(Btree *p){
p->pBt->db = p->db;
}