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:
137
src/btmutex.c
137
src/btmutex.c
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user