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