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*/
40static 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*/
57static 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*/
83static 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*/
98static 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*/
113static 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*/
121static 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*/
148int 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*/
198void 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*/
226void 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*/
325void sqlite3ConnectionClosed(sqlite3 *db){
326 sqlite3ConnectionUnlocked(db);
327 enterMutex();
328 removeFromBlockedList(db);
329 checkListProperties(db);
330 leaveMutex();
331}
332#endif
333