aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/sqlite/win32/btmutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/sqlite/win32/btmutex.c')
-rwxr-xr-xlibraries/sqlite/win32/btmutex.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/libraries/sqlite/win32/btmutex.c b/libraries/sqlite/win32/btmutex.c
new file mode 100755
index 0000000..1f63434
--- /dev/null
+++ b/libraries/sqlite/win32/btmutex.c
@@ -0,0 +1,315 @@
1/*
2** 2007 August 27
3**
4** The author disclaims copyright to this source code. In place of
5** a legal notice, here is a blessing:
6**
7** May you do good and not evil.
8** May you find forgiveness for yourself and forgive others.
9** May you share freely, never taking more than you give.
10**
11*************************************************************************
12**
13** $Id: btmutex.c,v 1.7 2007/08/30 01:19:59 drh Exp $
14**
15** This file contains code used to implement mutexes on Btree objects.
16** This code really belongs in btree.c. But btree.c is getting too
17** big and we want to break it down some. This packaged seemed like
18** a good breakout.
19*/
20#include "btreeInt.h"
21#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
22
23
24/*
25** Enter a mutex on the given BTree object.
26**
27** If the object is not sharable, then no mutex is ever required
28** and this routine is a no-op. The underlying mutex is non-recursive.
29** But we keep a reference count in Btree.wantToLock so the behavior
30** of this interface is recursive.
31**
32** To avoid deadlocks, multiple Btrees are locked in the same order
33** by all database connections. The p->pNext is a list of other
34** Btrees belonging to the same database connection as the p Btree
35** which need to be locked after p. If we cannot get a lock on
36** p, then first unlock all of the others on p->pNext, then wait
37** for the lock to become available on p, then relock all of the
38** subsequent Btrees that desire a lock.
39*/
40void sqlite3BtreeEnter(Btree *p){
41 Btree *pLater;
42
43 /* Some basic sanity checking on the Btree. The list of Btrees
44 ** connected by pNext and pPrev should be in sorted order by
45 ** Btree.pBt value. All elements of the list should belong to
46 ** the same connection. Only shared Btrees are on the list. */
47 assert( p->pNext==0 || p->pNext->pBt>p->pBt );
48 assert( p->pPrev==0 || p->pPrev->pBt<p->pBt );
49 assert( p->pNext==0 || p->pNext->pSqlite==p->pSqlite );
50 assert( p->pPrev==0 || p->pPrev->pSqlite==p->pSqlite );
51 assert( p->sharable || (p->pNext==0 && p->pPrev==0) );
52
53 /* Check for locking consistency */
54 assert( !p->locked || p->wantToLock>0 );
55 assert( p->sharable || p->wantToLock==0 );
56
57 /* We should already hold a lock on the database connection */
58 assert( sqlite3_mutex_held(p->pSqlite->mutex) );
59
60 if( !p->sharable ) return;
61 p->wantToLock++;
62 if( p->locked ) return;
63
64 /* In most cases, we should be able to acquire the lock we
65 ** want without having to go throught the ascending lock
66 ** procedure that follows. Just be sure not to block.
67 */
68 if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
69 p->locked = 1;
70 return;
71 }
72
73 /* To avoid deadlock, first release all locks with a larger
74 ** BtShared address. Then acquire our lock. Then reacquire
75 ** the other BtShared locks that we used to hold in ascending
76 ** order.
77 */
78 for(pLater=p->pNext; pLater; pLater=pLater->pNext){
79 assert( pLater->sharable );
80 assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
81 assert( !pLater->locked || pLater->wantToLock>0 );
82 if( pLater->locked ){
83 sqlite3_mutex_leave(pLater->pBt->mutex);
84 pLater->locked = 0;
85 }
86 }
87 sqlite3_mutex_enter(p->pBt->mutex);
88 p->locked = 1;
89 for(pLater=p->pNext; pLater; pLater=pLater->pNext){
90 if( pLater->wantToLock ){
91 sqlite3_mutex_enter(pLater->pBt->mutex);
92 pLater->locked = 1;
93 }
94 }
95}
96
97/*
98** Exit the recursive mutex on a Btree.
99*/
100void sqlite3BtreeLeave(Btree *p){
101 if( p->sharable ){
102 assert( p->wantToLock>0 );
103 p->wantToLock--;
104 if( p->wantToLock==0 ){
105 assert( p->locked );
106 sqlite3_mutex_leave(p->pBt->mutex);
107 p->locked = 0;
108 }
109 }
110}
111
112#ifndef NDEBUG
113/*
114** Return true if the BtShared mutex is held on the btree.
115**
116** This routine makes no determination one why or another if the
117** database connection mutex is held.
118**
119** This routine is used only from within assert() statements.
120*/
121int sqlite3BtreeHoldsMutex(Btree *p){
122 return (p->sharable==0 ||
123 (p->locked && p->wantToLock && sqlite3_mutex_held(p->pBt->mutex)));
124}
125#endif
126
127
128#ifndef SQLITE_OMIT_INCRBLOB
129/*
130** Enter and leave a mutex on a Btree given a cursor owned by that
131** Btree. These entry points are used by incremental I/O and can be
132** omitted if that module is not used.
133*/
134void sqlite3BtreeEnterCursor(BtCursor *pCur){
135 sqlite3BtreeEnter(pCur->pBtree);
136}
137void sqlite3BtreeLeaveCursor(BtCursor *pCur){
138 sqlite3BtreeLeave(pCur->pBtree);
139}
140#endif /* SQLITE_OMIT_INCRBLOB */
141
142
143/*
144** Enter the mutex on every Btree associated with a database
145** connection. This is needed (for example) prior to parsing
146** a statement since we will be comparing table and column names
147** against all schemas and we do not want those schemas being
148** reset out from under us.
149**
150** There is a corresponding leave-all procedures.
151**
152** Enter the mutexes in accending order by BtShared pointer address
153** to avoid the possibility of deadlock when two threads with
154** two or more btrees in common both try to lock all their btrees
155** at the same instant.
156*/
157void sqlite3BtreeEnterAll(sqlite3 *db){
158 int i;
159 Btree *p, *pLater;
160 assert( sqlite3_mutex_held(db->mutex) );
161 for(i=0; i<db->nDb; i++){
162 p = db->aDb[i].pBt;
163 if( p && p->sharable ){
164 p->wantToLock++;
165 if( !p->locked ){
166 assert( p->wantToLock==1 );
167 while( p->pPrev ) p = p->pPrev;
168 while( p->locked && p->pNext ) p = p->pNext;
169 for(pLater = p->pNext; pLater; pLater=pLater->pNext){
170 if( pLater->locked ){
171 sqlite3_mutex_leave(pLater->pBt->mutex);
172 pLater->locked = 0;
173 }
174 }
175 while( p ){
176 sqlite3_mutex_enter(p->pBt->mutex);
177 p->locked++;
178 p = p->pNext;
179 }
180 }
181 }
182 }
183}
184void sqlite3BtreeLeaveAll(sqlite3 *db){
185 int i;
186 Btree *p;
187 assert( sqlite3_mutex_held(db->mutex) );
188 for(i=0; i<db->nDb; i++){
189 p = db->aDb[i].pBt;
190 if( p && p->sharable ){
191 assert( p->wantToLock>0 );
192 p->wantToLock--;
193 if( p->wantToLock==0 ){
194 assert( p->locked );
195 sqlite3_mutex_leave(p->pBt->mutex);
196 p->locked = 0;
197 }
198 }
199 }
200}
201
202#ifndef NDEBUG
203/*
204** Return true if the current thread holds the database connection
205** mutex and all required BtShared mutexes.
206**
207** This routine is used inside assert() statements only.
208*/
209int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
210 int i;
211 if( !sqlite3_mutex_held(db->mutex) ){
212 return 0;
213 }
214 for(i=0; i<db->nDb; i++){
215 Btree *p;
216 p = db->aDb[i].pBt;
217 if( p && p->sharable &&
218 (p->wantToLock==0 || !sqlite3_mutex_held(p->pBt->mutex)) ){
219 return 0;
220 }
221 }
222 return 1;
223}
224#endif /* NDEBUG */
225
226/*
227** Potentially dd a new Btree pointer to a BtreeMutexArray.
228** Really only add the Btree if it can possibly be shared with
229** another database connection.
230**
231** The Btrees are kept in sorted order by pBtree->pBt. That
232** way when we go to enter all the mutexes, we can enter them
233** in order without every having to backup and retry and without
234** worrying about deadlock.
235**
236** The number of shared btrees will always be small (usually 0 or 1)
237** so an insertion sort is an adequate algorithm here.
238*/
239void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
240 int i, j;
241 BtShared *pBt;
242 if( pBtree==0 || pBtree->sharable==0 ) return;
243#ifndef NDEBUG
244 {
245 for(i=0; i<pArray->nMutex; i++){
246 assert( pArray->aBtree[i]!=pBtree );
247 }
248 }
249#endif
250 assert( pArray->nMutex>=0 );
251 assert( pArray->nMutex<sizeof(pArray->aBtree)/sizeof(pArray->aBtree[0])-1 );
252 pBt = pBtree->pBt;
253 for(i=0; i<pArray->nMutex; i++){
254 assert( pArray->aBtree[i]!=pBtree );
255 if( pArray->aBtree[i]->pBt>pBt ){
256 for(j=pArray->nMutex; j>i; j--){
257 pArray->aBtree[j] = pArray->aBtree[j-1];
258 }
259 pArray->aBtree[i] = pBtree;
260 pArray->nMutex++;
261 return;
262 }
263 }
264 pArray->aBtree[pArray->nMutex++] = pBtree;
265}
266
267/*
268** Enter the mutex of every btree in the array. This routine is
269** called at the beginning of sqlite3VdbeExec(). The mutexes are
270** exited at the end of the same function.
271*/
272void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
273 int i;
274 for(i=0; i<pArray->nMutex; i++){
275 Btree *p = pArray->aBtree[i];
276 /* Some basic sanity checking */
277 assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
278 assert( !p->locked || p->wantToLock>0 );
279
280 /* We should already hold a lock on the database connection */
281 assert( sqlite3_mutex_held(p->pSqlite->mutex) );
282
283 p->wantToLock++;
284 if( !p->locked && p->sharable ){
285 sqlite3_mutex_enter(p->pBt->mutex);
286 p->locked = 1;
287 }
288 }
289}
290
291/*
292** Leave the mutex of every btree in the group.
293*/
294void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
295 int i;
296 for(i=0; i<pArray->nMutex; i++){
297 Btree *p = pArray->aBtree[i];
298 /* Some basic sanity checking */
299 assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
300 assert( p->locked || !p->sharable );
301 assert( p->wantToLock>0 );
302
303 /* We should already hold a lock on the database connection */
304 assert( sqlite3_mutex_held(p->pSqlite->mutex) );
305
306 p->wantToLock--;
307 if( p->wantToLock==0 && p->locked ){
308 sqlite3_mutex_leave(p->pBt->mutex);
309 p->locked = 0;
310 }
311 }
312}
313
314
315#endif /* SQLITE_THREADSAFE && !SQLITE_OMIT_SHARED_CACHE */