/* ** 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); } } }
/* ** Return true if the BtShared mutex is held on the btree, or if the ** B-Tree is not marked as sharable. ** ** This routine is used only from within assert() statements. */ int sqlite3BtreeHoldsMutex(Btree *p) { assert(p->sharable == 0 || p->locked == 0 || p->wantToLock > 0); assert(p->sharable == 0 || p->locked == 0 || p->db == p->pBt->db); assert(p->sharable == 0 || p->locked == 0 || sqlite3_mutex_held(p->pBt->mutex)); assert(p->sharable == 0 || p->locked == 0 || sqlite3_mutex_held(p->db->mutex)); return(p->sharable == 0 || p->locked); }
/* ** 2007 August 27 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains code used to implement mutexes on Btree objects. ** This code really belongs in btree.c. But btree.c is getting too ** big and we want to break it down some. This packaged seemed like ** a good breakout. ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e ** ************************************************************************* */ //#include "btreeInt.h" #if !SQLITE_OMIT_SHARED_CACHE #if SQLITE_THREADSAFE /* ** Obtain the BtShared mutex associated with B-Tree handle p. Also, ** set BtShared.db to the database handle associated with p and the ** p->locked boolean to true. */ static void lockBtreeMutex(Btree *p) { assert(p->locked == 0); assert(sqlite3_mutex_notheld(p->pBt->mutex)); assert(sqlite3_mutex_held(p->db->mutex)); sqlite3_mutex_enter(p->pBt->mutex); p->pBt->db = p->db; p->locked = 1; }
/* ** Release the BtShared mutex associated with B-Tree handle p and ** clear the p->locked boolean. */ static void unlockBtreeMutex(Btree *p) { assert(p->locked == 1); assert(sqlite3_mutex_held(p->pBt->mutex)); assert(sqlite3_mutex_held(p->db->mutex)); assert(p->db == p->pBt->db); sqlite3_mutex_leave(p->pBt->mutex); p->locked = 0; }
/* ** Exit the recursive mutex on a Btree. */ void sqlite3BtreeLeave(Btree *p) { if (p->sharable) { assert(p->wantToLock > 0); p->wantToLock--; if (p->wantToLock == 0) { unlockBtreeMutex(p); } } }
void sqlite3BtreeEnterAll(sqlite3 db) { int i; for (i = 0; i < db->nDb; i++) { Btree *p = db->aDb[i].pBt; if (p) { p->pBt->db = p->db; } } }
/* ** 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); } } }
/* ** Add a new Btree pointer to a BtreeMutexArray. ** if the pointer can possibly be shared with ** another database connection. ** ** 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. */ void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree) { int i, j; BtShared *pBt; if (pBtree == 0 || pBtree->sharable == 0) { return; } #if !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; }
/* ** 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. ** ** 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 sqlite3BtreeEnter(Btree *p) { p->pBt->db = p->db; }
/* ** Return true if a particular Btree requires a lock. Return FALSE if ** no lock is ever required since it is not sharable. */ int sqlite3BtreeSharable(Btree *p) { return(p->sharable); }
/* ** Enter a mutex on the given BTree object. ** ** If the object is not sharable, then no mutex is ever required ** and this routine is a no-op. The underlying mutex is non-recursive. ** But we keep a reference count in Btree.wantToLock so the behavior ** of this interface is recursive. ** ** To avoid deadlocks, multiple Btrees are locked in the same order ** by all database connections. The p->pNext is a list of other ** Btrees belonging to the same database connection as the p Btree ** which need to be locked after p. If we cannot get a lock on ** p, then first unlock all of the others on p->pNext, then wait ** for the lock to become available on p, then relock all of the ** subsequent Btrees that desire a lock. */ void sqlite3BtreeEnter(Btree *p) { Btree *pLater; /* Some basic sanity checking on the Btree. The list of Btrees ** connected by pNext and pPrev should be in sorted order by ** Btree.pBt value. All elements of the list should belong to ** the same connection. Only shared Btrees are on the list. */ assert(p->pNext == 0 || p->pNext->pBt > p->pBt); assert(p->pPrev == 0 || p->pPrev->pBt < p->pBt); assert(p->pNext == 0 || p->pNext->db == p->db); assert(p->pPrev == 0 || p->pPrev->db == p->db); assert(p->sharable || (p->pNext == 0 && p->pPrev == 0)); /* Check for locking consistency */ assert(!p->locked || p->wantToLock > 0); assert(p->sharable || p->wantToLock == 0); /* We should already hold a lock on the database connection */ assert(sqlite3_mutex_held(p->db->mutex)); /* Unless the database is sharable and unlocked, then BtShared.db ** should already be set correctly. */ assert((p->locked == 0 && p->sharable) || p->pBt->db == p->db); if (!p->sharable) { return; } p->wantToLock++; if (p->locked) { return; } /* 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. */ if (sqlite3_mutex_try(p->pBt->mutex) == SQLITE_OK) { p->pBt->db = p->db; p->locked = 1; return; } /* To avoid deadlock, first release all locks with a larger ** BtShared address. Then acquire our lock. Then reacquire ** the other BtShared locks that we used to hold in ascending ** order. */ for (pLater = p->pNext; pLater; pLater = pLater->pNext) { assert(pLater->sharable); assert(pLater->pNext == 0 || pLater->pNext->pBt > pLater->pBt); assert(!pLater->locked || pLater->wantToLock > 0); if (pLater->locked) { unlockBtreeMutex(pLater); } } lockBtreeMutex(p); for (pLater = p->pNext; pLater; pLater = pLater->pNext) { if (pLater->wantToLock) { lockBtreeMutex(pLater); } } }