wasCSharpSQLite – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System.Diagnostics;
2  
3 namespace Community.CsharpSqlite
4 {
5 public partial class Sqlite3
6 {
7 /*
8 ** 2009 March 3
9 **
10 ** The author disclaims copyright to this source code. In place of
11 ** a legal notice, here is a blessing:
12 **
13 ** May you do good and not evil.
14 ** May you find forgiveness for yourself and forgive others.
15 ** May you share freely, never taking more than you give.
16 **
17 *************************************************************************
18 **
19 ** This file contains the implementation of the sqlite3_unlock_notify()
20 ** API method and its associated functionality.
21 *************************************************************************
22 ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart
23 ** C#-SQLite is an independent reimplementation of the SQLite software library
24 **
25 ** SQLITE_SOURCE_ID: 2009-12-07 16:39:13 1ed88e9d01e9eda5cbc622e7614277f29bcc551c
26 **
27 *************************************************************************
28 */
29 //#include "sqliteInt.h"
30 //#include "btreeInt.h"
31  
32 /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
33 #if SQLITE_ENABLE_UNLOCK_NOTIFY
34  
35 /*
36 ** Public interfaces:
37 **
38 ** sqlite3ConnectionBlocked()
39 ** sqlite3ConnectionUnlocked()
40 ** sqlite3ConnectionClosed()
41 ** sqlite3_unlock_notify()
42 */
43  
44 //#define assertMutexHeld() \
45 assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
46  
47 /*
48 ** Head of a linked list of all sqlite3 objects created by this process
49 ** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
50 ** is not NULL. This variable may only accessed while the STATIC_MASTER
51 ** mutex is held.
52 */
53 static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
54  
55 #if !NDEBUG
56 /*
57 ** This function is a complex assert() that verifies the following
58 ** properties of the blocked connections list:
59 **
60 ** 1) Each entry in the list has a non-NULL value for either
61 ** pUnlockConnection or pBlockingConnection, or both.
62 **
63 ** 2) All entries in the list that share a common value for
64 ** xUnlockNotify are grouped together.
65 **
66 ** 3) If the argument db is not NULL, then none of the entries in the
67 ** blocked connections list have pUnlockConnection or pBlockingConnection
68 ** set to db. This is used when closing connection db.
69 */
70 static void checkListProperties(sqlite3 *db){
71 sqlite3 *p;
72 for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
73 int seen = 0;
74 sqlite3 *p2;
75  
76 /* Verify property (1) */
77 assert( p->pUnlockConnection || p->pBlockingConnection );
78  
79 /* Verify property (2) */
80 for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
81 if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
82 assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
83 assert( db==0 || p->pUnlockConnection!=db );
84 assert( db==0 || p->pBlockingConnection!=db );
85 }
86 }
87 }
88 #else
89 //# define checkListProperties(x)
90 #endif
91  
92 /*
93 ** Remove connection db from the blocked connections list. If connection
94 ** db is not currently a part of the list, this function is a no-op.
95 */
96 static void removeFromBlockedList(sqlite3 *db){
97 sqlite3 **pp;
98 assertMutexHeld();
99 for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
100 if( *pp==db ){
101 *pp = (*pp)->pNextBlocked;
102 break;
103 }
104 }
105 }
106  
107 /*
108 ** Add connection db to the blocked connections list. It is assumed
109 ** that it is not already a part of the list.
110 */
111 static void addToBlockedList(sqlite3 *db){
112 sqlite3 **pp;
113 assertMutexHeld();
114 for(
115 pp=&sqlite3BlockedList;
116 *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
117 pp=&(*pp)->pNextBlocked
118 );
119 db->pNextBlocked = *pp;
120 *pp = db;
121 }
122  
123 /*
124 ** Obtain the STATIC_MASTER mutex.
125 */
126 static void enterMutex(){
127 sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
128 checkListProperties(0);
129 }
130  
131 /*
132 ** Release the STATIC_MASTER mutex.
133 */
134 static void leaveMutex(){
135 assertMutexHeld();
136 checkListProperties(0);
137 sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
138 }
139  
140 /*
141 ** Register an unlock-notify callback.
142 **
143 ** This is called after connection "db" has attempted some operation
144 ** but has received an SQLITE_LOCKED error because another connection
145 ** (call it pOther) in the same process was busy using the same shared
146 ** cache. pOther is found by looking at db->pBlockingConnection.
147 **
148 ** If there is no blocking connection, the callback is invoked immediately,
149 ** before this routine returns.
150 **
151 ** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
152 ** a deadlock.
153 **
154 ** Otherwise, make arrangements to invoke xNotify when pOther drops
155 ** its locks.
156 **
157 ** Each call to this routine overrides any prior callbacks registered
158 ** on the same "db". If xNotify==0 then any prior callbacks are immediately
159 ** cancelled.
160 */
161 int sqlite3_unlock_notify(
162 sqlite3 *db,
163 void (*xNotify)(void **, int),
164 void *pArg
165 ){
166 int rc = SQLITE_OK;
167  
168 sqlite3_mutex_enter(db->mutex);
169 enterMutex();
170  
171 if( xNotify==0 ){
172 removeFromBlockedList(db);
173 db->pUnlockConnection = 0;
174 db->xUnlockNotify = 0;
175 db->pUnlockArg = 0;
176 }else if( 0==db->pBlockingConnection ){
177 /* The blocking transaction has been concluded. Or there never was a
178 ** blocking transaction. In either case, invoke the notify callback
179 ** immediately.
180 */
181 xNotify(&pArg, 1);
182 }else{
183 sqlite3 *p;
184  
185 for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
186 if( p ){
187 rc = SQLITE_LOCKED; /* Deadlock detected. */
188 }else{
189 db->pUnlockConnection = db->pBlockingConnection;
190 db->xUnlockNotify = xNotify;
191 db->pUnlockArg = pArg;
192 removeFromBlockedList(db);
193 addToBlockedList(db);
194 }
195 }
196  
197 leaveMutex();
198 assert( !db->mallocFailed );
199 sqlite3Error(db, rc, (rc?"database is deadlocked":0));
200 sqlite3_mutex_leave(db->mutex);
201 return rc;
202 }
203  
204 /*
205 ** This function is called while stepping or preparing a statement
206 ** associated with connection db. The operation will return SQLITE_LOCKED
207 ** to the user because it requires a lock that will not be available
208 ** until connection pBlocker concludes its current transaction.
209 */
210 void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
211 enterMutex();
212 if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
213 addToBlockedList(db);
214 }
215 db->pBlockingConnection = pBlocker;
216 leaveMutex();
217 }
218  
219 /*
220 ** This function is called when
221 ** the transaction opened by database db has just finished. Locks held
222 ** by database connection db have been released.
223 **
224 ** This function loops through each entry in the blocked connections
225 ** list and does the following:
226 **
227 ** 1) If the sqlite3.pBlockingConnection member of a list entry is
228 ** set to db, then set pBlockingConnection=0.
229 **
230 ** 2) If the sqlite3.pUnlockConnection member of a list entry is
231 ** set to db, then invoke the configured unlock-notify callback and
232 ** set pUnlockConnection=0.
233 **
234 ** 3) If the two steps above mean that pBlockingConnection==0 and
235 ** pUnlockConnection==0, remove the entry from the blocked connections
236 ** list.
237 */
238 void sqlite3ConnectionUnlocked(sqlite3 *db){
239 void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
240 int nArg = 0; /* Number of entries in aArg[] */
241 sqlite3 **pp; /* Iterator variable */
242 void **aArg; /* Arguments to the unlock callback */
243 void **aDyn = 0; /* Dynamically allocated space for aArg[] */
244 void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
245  
246 aArg = aStatic;
247 enterMutex(); /* Enter STATIC_MASTER mutex */
248  
249 /* This loop runs once for each entry in the blocked-connections list. */
250 for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
251 sqlite3 *p = *pp;
252  
253 /* Step 1. */
254 if( p->pBlockingConnection==db ){
255 p->pBlockingConnection = 0;
256 }
257  
258 /* Step 2. */
259 if( p->pUnlockConnection==db ){
260 assert( p->xUnlockNotify );
261 if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
262 xUnlockNotify(aArg, nArg);
263 nArg = 0;
264 }
265  
266 sqlite3BeginBenignMalloc();
267 assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
268 assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
269 if( (!aDyn && nArg==(int)ArraySize(aStatic))
270 || (aDyn && nArg==(int)(sqlite3DbMallocSize(db, aDyn)/sizeof(void*)))
271 ){
272 /* The aArg[] array needs to grow. */
273 void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
274 if( pNew ){
275 memcpy(pNew, aArg, nArg*sizeof(void *));
276 //sqlite3_free(aDyn);
277 aDyn = aArg = pNew;
278 }else{
279 /* This occurs when the array of context pointers that need to
280 ** be passed to the unlock-notify callback is larger than the
281 ** aStatic[] array allocated on the stack and the attempt to
282 ** allocate a larger array from the heap has failed.
283 **
284 ** This is a difficult situation to handle. Returning an error
285 ** code to the caller is insufficient, as even if an error code
286 ** is returned the transaction on connection db will still be
287 ** closed and the unlock-notify callbacks on blocked connections
288 ** will go unissued. This might cause the application to wait
289 ** indefinitely for an unlock-notify callback that will never
290 ** arrive.
291 **
292 ** Instead, invoke the unlock-notify callback with the context
293 ** array already accumulated. We can then clear the array and
294 ** begin accumulating any further context pointers without
295 ** requiring any dynamic allocation. This is sub-optimal because
296 ** it means that instead of one callback with a large array of
297 ** context pointers the application will receive two or more
298 ** callbacks with smaller arrays of context pointers, which will
299 ** reduce the applications ability to prioritize multiple
300 ** connections. But it is the best that can be done under the
301 ** circumstances.
302 */
303 xUnlockNotify(aArg, nArg);
304 nArg = 0;
305 }
306 }
307 sqlite3EndBenignMalloc();
308  
309 aArg[nArg++] = p->pUnlockArg;
310 xUnlockNotify = p->xUnlockNotify;
311 p->pUnlockConnection = 0;
312 p->xUnlockNotify = 0;
313 p->pUnlockArg = 0;
314 }
315  
316 /* Step 3. */
317 if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
318 /* Remove connection p from the blocked connections list. */
319 *pp = p->pNextBlocked;
320 p->pNextBlocked = 0;
321 }else{
322 pp = &p->pNextBlocked;
323 }
324 }
325  
326 if( nArg!=0 ){
327 xUnlockNotify(aArg, nArg);
328 }
329 //sqlite3_free(aDyn);
330 leaveMutex(); /* Leave STATIC_MASTER mutex */
331 }
332  
333 /*
334 ** This is called when the database connection passed as an argument is
335 ** being closed. The connection is removed from the blocked list.
336 */
337 void sqlite3ConnectionClosed(sqlite3 *db){
338 sqlite3ConnectionUnlocked(db);
339 enterMutex();
340 removeFromBlockedList(db);
341 checkListProperties(db);
342 leaveMutex();
343 }
344 #endif
345 }
346 }