mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-16 23:02:26 +03:00
More locking fixes. Now makes it all the way through quick.test. There
are errors but no assertion faults. Progress. (CVS 4319) FossilOrigin-Name: 844d40b8379d3374130e2d94f6e32c2cda34e0ca
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** $Id: btmutex.c,v 1.3 2007/08/28 23:28:08 drh Exp $
|
||||
** $Id: btmutex.c,v 1.4 2007/08/29 00:33:07 drh Exp $
|
||||
**
|
||||
** This file contains code used to implement mutexes on Btree objects.
|
||||
** This code really belongs in btree.c. But btree.c is getting too
|
||||
@@ -109,6 +109,61 @@ void sqlite3BtreeLeave(Btree *p){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter the mutex on every Btree associated with a database
|
||||
** connection. This is needed (for example) prior to parsing
|
||||
** a statement since we will be comparing table and column names
|
||||
** against all schemas and we do not want those schemas being
|
||||
** reset out from under us.
|
||||
**
|
||||
** There is a corresponding leave-all procedures.
|
||||
**
|
||||
** Enter the mutexes in accending order by BtShared pointer address
|
||||
** to avoid the possibility of deadlock when two threads with
|
||||
** two or more btrees in common both try to lock all their btrees
|
||||
** at the same instant.
|
||||
*/
|
||||
void sqlite3BtreeEnterAll(sqlite3 *db){
|
||||
int i;
|
||||
Btree *p;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
for(i=0; i<db->nDb && ((p = db->aDb[i].pBt)==0 || p->sharable==0); i++){}
|
||||
if( i<db->nDb ){
|
||||
while( p->pNext ) p = p->pNext;
|
||||
while( 1 ){
|
||||
if( p->locked ){
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
p->locked = 0;
|
||||
}
|
||||
if( p->pPrev==0 ) break;
|
||||
p = p->pPrev;
|
||||
}
|
||||
while( p ){
|
||||
p->wantToLock++;
|
||||
sqlite3_mutex_enter(p->pBt->mutex);
|
||||
p->locked = 1;
|
||||
p = p->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
void sqlite3BtreeLeaveAll(sqlite3 *db){
|
||||
int i;
|
||||
Btree *p;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
for(i=0; i<db->nDb && ((p = db->aDb[i].pBt)==0 || p->sharable==0); i++){}
|
||||
if( i<db->nDb ){
|
||||
while( p->pPrev ) p = p->pPrev;
|
||||
while( p ){
|
||||
p->wantToLock--;
|
||||
if( p->wantToLock==0 ){
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
p->locked = 0;
|
||||
}
|
||||
p = p->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Potentially dd a new Btree pointer to a BtreeMutexArray.
|
||||
** Really only add the Btree if it can possibly be shared with
|
||||
@@ -122,31 +177,32 @@ void sqlite3BtreeLeave(Btree *p){
|
||||
** The number of shared btrees will always be small (usually 0 or 1)
|
||||
** so an insertion sort is an adequate algorithm here.
|
||||
*/
|
||||
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pSet, Btree *pBtree){
|
||||
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
|
||||
int i, j;
|
||||
BtShared *pBt;
|
||||
if( !pBtree->sharable ) return;
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
for(i=0; i<pSet->nMutex; i++){
|
||||
assert( pSet->aBtree[i]!=pBtree );
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
assert( pArray->aBtree[i]!=pBtree );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
assert( pSet->nMutex>=0 );
|
||||
assert( pSet->nMutex<sizeof(pSet->aBtree)/sizeof(pSet->aBtree[0])-1 );
|
||||
assert( pArray->nMutex>=0 );
|
||||
assert( pArray->nMutex<sizeof(pArray->aBtree)/sizeof(pArray->aBtree[0])-1 );
|
||||
pBt = pBtree->pBt;
|
||||
for(i=0; i<pSet->nMutex; i++){
|
||||
assert( pSet->aBtree[i]!=pBtree );
|
||||
if( pSet->aBtree[i]->pBt>pBt ){
|
||||
for(j=pSet->nMutex; j>i; j--){
|
||||
pSet->aBtree[j] = pSet->aBtree[j-1];
|
||||
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];
|
||||
}
|
||||
pSet->aBtree[i] = pBtree;
|
||||
pArray->aBtree[i] = pBtree;
|
||||
pArray->nMutex++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pSet->aBtree[pSet->nMutex++] = pBtree;
|
||||
pArray->aBtree[pArray->nMutex++] = pBtree;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -154,12 +210,12 @@ void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pSet, Btree *pBtree){
|
||||
** called at the beginning of sqlite3VdbeExec(). The mutexes are
|
||||
** exited at the end of the same function.
|
||||
*/
|
||||
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pSet){
|
||||
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
|
||||
int i;
|
||||
for(i=0; i<pSet->nMutex; i++){
|
||||
Btree *p = pSet->aBtree[i];
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
Btree *p = pArray->aBtree[i];
|
||||
/* Some basic sanity checking */
|
||||
assert( i==0 || pSet->aBtree[i-1]->pBt<p->pBt );
|
||||
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
|
||||
assert( !p->locked || p->wantToLock>0 );
|
||||
assert( p->sharable );
|
||||
|
||||
@@ -177,12 +233,12 @@ void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pSet){
|
||||
/*
|
||||
** Leave the mutex of every btree in the group.
|
||||
*/
|
||||
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pSet){
|
||||
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
|
||||
int i;
|
||||
for(i=0; i<pSet->nMutex; i++){
|
||||
Btree *p = pSet->aBtree[i];
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
Btree *p = pArray->aBtree[i];
|
||||
/* Some basic sanity checking */
|
||||
assert( i==0 || pSet->aBtree[i-1]->pBt<p->pBt );
|
||||
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
|
||||
assert( p->locked );
|
||||
assert( p->sharable );
|
||||
assert( p->wantToLock>0 );
|
||||
|
||||
32
src/btree.h
32
src/btree.h
@@ -13,7 +13,7 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.88 2007/08/28 23:28:08 drh Exp $
|
||||
** @(#) $Id: btree.h,v 1.89 2007/08/29 00:33:07 drh Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@@ -107,21 +107,6 @@ void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
int sqlite3BtreeSchemaLocked(Btree *);
|
||||
int sqlite3BtreeLockTable(Btree *, int, u8);
|
||||
|
||||
/*
|
||||
** If we are not using shared cache, then there is no need to
|
||||
** use mutexes to access the BtShared structures. So make the
|
||||
** Enter and Leave procedures no-ops.
|
||||
*/
|
||||
#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
void sqlite3BtreeEnter(Btree*);
|
||||
void sqlite3BtreeLeave(Btree*);
|
||||
# define sqlite3BtreeMutexHeld(X) sqlite3_mutex_held(X)
|
||||
#else
|
||||
# define sqlite3BtreeEnter(X)
|
||||
# define sqlite3BtreeLeave(X)
|
||||
# define sqlite3BtreeMutexHeld(X) 1
|
||||
#endif
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
const char *sqlite3BtreeGetDirname(Btree *);
|
||||
const char *sqlite3BtreeGetJournalname(Btree *);
|
||||
@@ -182,11 +167,26 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
int sqlite3BtreePageDump(Btree*, int, int recursive);
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If we are not using shared cache, then there is no need to
|
||||
** use mutexes to access the BtShared structures. So make the
|
||||
** Enter and Leave procedures no-ops.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
|
||||
void sqlite3BtreeEnter(Btree*);
|
||||
void sqlite3BtreeLeave(Btree*);
|
||||
# define sqlite3BtreeMutexHeld(X) sqlite3_mutex_held(X)
|
||||
void sqlite3BtreeEnterAll(sqlite3*);
|
||||
void sqlite3BtreeLeaveAll(sqlite3*);
|
||||
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*);
|
||||
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*);
|
||||
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*);
|
||||
#else
|
||||
# define sqlite3BtreeEnter(X)
|
||||
# define sqlite3BtreeLeave(X)
|
||||
# define sqlite3BtreeMutexHeld(X) 1
|
||||
# define sqlite3BtreeEnterAll(X)
|
||||
# define sqlite3BtreeLeaveAll(X)
|
||||
# define sqlite3BtreeMutexArrayEnter(X)
|
||||
# define sqlite3BtreeMutexArrayLeave(X)
|
||||
# define sqlite3BtreeMutexArrayInsert(X,Y)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btreeInt.h,v 1.11 2007/08/28 22:24:35 drh Exp $
|
||||
** $Id: btreeInt.h,v 1.12 2007/08/29 00:33:07 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@@ -331,7 +331,7 @@ struct Btree {
|
||||
u8 sharable; /* True if we can share pBt with other pSqlite */
|
||||
u8 locked; /* True if pSqlite currently has pBt locked */
|
||||
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
|
||||
Btree *pNext; /* List of Btrees with the same pSqlite value */
|
||||
Btree *pNext; /* List of other sharable Btrees from the same pSqlite */
|
||||
Btree *pPrev; /* Back pointer of the same list */
|
||||
};
|
||||
|
||||
@@ -358,7 +358,10 @@ struct Btree {
|
||||
**
|
||||
** Fields in this structure are accessed under the BtShared.mutex
|
||||
** mutex, except for nRef and pNext which are accessed under the
|
||||
** global SQLITE_MUTEX_STATIC_MASTER mutex.
|
||||
** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
|
||||
** may not be modified once it is initially set as long as nRef>0.
|
||||
** The pSchema field may be set once under BtShared.mutex and
|
||||
** thereafter is unchanged as long as nRef>0.
|
||||
*/
|
||||
struct BtShared {
|
||||
Pager *pPager; /* The page cache */
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
** interface, and routines that contribute to loading the database schema
|
||||
** from disk.
|
||||
**
|
||||
** $Id: prepare.c,v 1.58 2007/08/22 02:56:44 drh Exp $
|
||||
** $Id: prepare.c,v 1.59 2007/08/29 00:33:07 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -47,6 +47,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
|
||||
sqlite3 *db = pData->db;
|
||||
int iDb = pData->iDb;
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
pData->rc = SQLITE_OK;
|
||||
DbClearProperty(db, iDb, DB_Empty);
|
||||
if( db->mallocFailed ){
|
||||
@@ -156,6 +157,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( db->aDb[iDb].pSchema );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
|
||||
/* zMasterSchema and zInitScript are set to point at the master schema
|
||||
** and initialisation script appropriate for the database being
|
||||
@@ -197,9 +199,11 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3BtreeEnter(pDb->pBt);
|
||||
rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, 0, &curMain);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){
|
||||
sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -228,6 +232,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
if( rc ){
|
||||
sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
@@ -251,6 +256,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
sqlite3SetString(pzErrMsg, "attached databases must use the same"
|
||||
" text encoding as main database", (char*)0);
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -277,6 +283,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
if( pDb->pSchema->file_format>SQLITE_MAX_FILE_FORMAT ){
|
||||
sqlite3BtreeCloseCursor(curMain);
|
||||
sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
@@ -321,6 +328,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
|
||||
DbSetProperty(db, iDb, DB_SchemaLoaded);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
sqlite3BtreeLeave(pDb->pBt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -338,6 +346,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int i, rc;
|
||||
int commit_internal = !(db->flags&SQLITE_InternChanges);
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( db->init.busy ) return SQLITE_OK;
|
||||
rc = SQLITE_OK;
|
||||
db->init.busy = 1;
|
||||
@@ -377,6 +386,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
|
||||
int sqlite3ReadSchema(Parse *pParse){
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3 *db = pParse->db;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( !db->init.busy ){
|
||||
rc = sqlite3Init(db, &pParse->zErrMsg);
|
||||
}
|
||||
@@ -399,6 +409,7 @@ static int schemaIsValid(sqlite3 *db){
|
||||
int cookie;
|
||||
int allOk = 1;
|
||||
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
for(iDb=0; allOk && iDb<db->nDb; iDb++){
|
||||
Btree *pBt;
|
||||
pBt = db->aDb[iDb].pBt;
|
||||
@@ -435,6 +446,7 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
** more likely to cause a segfault than -1 (of course there are assert()
|
||||
** statements too, but it never hurts to play the odds).
|
||||
*/
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( pSchema ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pSchema==pSchema ){
|
||||
@@ -475,11 +487,15 @@ int sqlite3Prepare(
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++) {
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeSchemaLocked(pBt) ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb);
|
||||
sqlite3SafetyOff(db);
|
||||
return SQLITE_LOCKED;
|
||||
if( pBt ){
|
||||
int rc;
|
||||
rc = sqlite3BtreeSchemaLocked(pBt);
|
||||
if( rc ){
|
||||
const char *zDb = db->aDb[i].zName;
|
||||
sqlite3Error(db, SQLITE_LOCKED, "database schema is locked: %s", zDb);
|
||||
sqlite3SafetyOff(db);
|
||||
return SQLITE_LOCKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +591,9 @@ static int sqlite3LockAndPrepare(
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
sqlite3BtreeEnterAll(db);
|
||||
rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail);
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
return rc;
|
||||
}
|
||||
@@ -591,13 +609,14 @@ int sqlite3Reprepare(Vdbe *p){
|
||||
const char *zSql;
|
||||
sqlite3 *db;
|
||||
|
||||
assert( sqlite3_mutex_held(sqlite3VdbeDb(p)->mutex) );
|
||||
zSql = sqlite3VdbeGetSql(p);
|
||||
if( zSql==0 ){
|
||||
return 0;
|
||||
}
|
||||
db = sqlite3VdbeDb(p);
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
rc = sqlite3Prepare(db, zSql, -1, 0, &pNew, 0);
|
||||
rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0);
|
||||
if( rc ){
|
||||
assert( pNew==0 );
|
||||
return 0;
|
||||
@@ -666,7 +685,7 @@ static int sqlite3Prepare16(
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
zSql8 = sqlite3Utf16to8(db, zSql, nBytes);
|
||||
if( zSql8 ){
|
||||
rc = sqlite3Prepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
|
||||
rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
|
||||
}
|
||||
|
||||
if( zTail8 && pzTail ){
|
||||
|
||||
Reference in New Issue
Block a user