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_MAIN)) ) |
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_MAIN |
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_MAIN mutex. |
112 | */ |
113 | static void enterMutex(void){ |
114 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); |
115 | checkListProperties(0); |
116 | } |
117 | |
118 | /* |
119 | ** Release the STATIC_MAIN mutex. |
120 | */ |
121 | static void leaveMutex(void){ |
122 | assertMutexHeld(); |
123 | checkListProperties(0); |
124 | sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); |
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_MAIN 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_MAIN 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 |
333 | |