1404ca075Sdanielk1977 /*
2404ca075Sdanielk1977 ** 2009 March 3
3404ca075Sdanielk1977 **
4404ca075Sdanielk1977 ** The author disclaims copyright to this source code. In place of
5404ca075Sdanielk1977 ** a legal notice, here is a blessing:
6404ca075Sdanielk1977 **
7404ca075Sdanielk1977 ** May you do good and not evil.
8404ca075Sdanielk1977 ** May you find forgiveness for yourself and forgive others.
9404ca075Sdanielk1977 ** May you share freely, never taking more than you give.
10404ca075Sdanielk1977 **
11404ca075Sdanielk1977 *************************************************************************
12404ca075Sdanielk1977 **
13404ca075Sdanielk1977 ** This file contains the implementation of the sqlite3_unlock_notify()
14404ca075Sdanielk1977 ** API method and its associated functionality.
15404ca075Sdanielk1977 */
16404ca075Sdanielk1977 #include "sqliteInt.h"
17404ca075Sdanielk1977 #include "btreeInt.h"
18404ca075Sdanielk1977
19404ca075Sdanielk1977 /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
20404ca075Sdanielk1977 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
21404ca075Sdanielk1977
22404ca075Sdanielk1977 /*
23404ca075Sdanielk1977 ** Public interfaces:
24404ca075Sdanielk1977 **
25404ca075Sdanielk1977 ** sqlite3ConnectionBlocked()
26404ca075Sdanielk1977 ** sqlite3ConnectionUnlocked()
27404ca075Sdanielk1977 ** sqlite3ConnectionClosed()
28404ca075Sdanielk1977 ** sqlite3_unlock_notify()
29404ca075Sdanielk1977 */
30404ca075Sdanielk1977
31404ca075Sdanielk1977 #define assertMutexHeld() \
32*ccb2113aSdrh assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) )
33404ca075Sdanielk1977
34404ca075Sdanielk1977 /*
35404ca075Sdanielk1977 ** Head of a linked list of all sqlite3 objects created by this process
36404ca075Sdanielk1977 ** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
37*ccb2113aSdrh ** is not NULL. This variable may only accessed while the STATIC_MAIN
38404ca075Sdanielk1977 ** mutex is held.
39404ca075Sdanielk1977 */
40404ca075Sdanielk1977 static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
41404ca075Sdanielk1977
42404ca075Sdanielk1977 #ifndef NDEBUG
43404ca075Sdanielk1977 /*
44404ca075Sdanielk1977 ** This function is a complex assert() that verifies the following
45404ca075Sdanielk1977 ** properties of the blocked connections list:
46404ca075Sdanielk1977 **
47404ca075Sdanielk1977 ** 1) Each entry in the list has a non-NULL value for either
48404ca075Sdanielk1977 ** pUnlockConnection or pBlockingConnection, or both.
49404ca075Sdanielk1977 **
50404ca075Sdanielk1977 ** 2) All entries in the list that share a common value for
51404ca075Sdanielk1977 ** xUnlockNotify are grouped together.
52404ca075Sdanielk1977 **
53404ca075Sdanielk1977 ** 3) If the argument db is not NULL, then none of the entries in the
54404ca075Sdanielk1977 ** blocked connections list have pUnlockConnection or pBlockingConnection
55404ca075Sdanielk1977 ** set to db. This is used when closing connection db.
56404ca075Sdanielk1977 */
checkListProperties(sqlite3 * db)57404ca075Sdanielk1977 static void checkListProperties(sqlite3 *db){
58404ca075Sdanielk1977 sqlite3 *p;
59404ca075Sdanielk1977 for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
60404ca075Sdanielk1977 int seen = 0;
61404ca075Sdanielk1977 sqlite3 *p2;
62404ca075Sdanielk1977
63404ca075Sdanielk1977 /* Verify property (1) */
64404ca075Sdanielk1977 assert( p->pUnlockConnection || p->pBlockingConnection );
65404ca075Sdanielk1977
66404ca075Sdanielk1977 /* Verify property (2) */
67404ca075Sdanielk1977 for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
68404ca075Sdanielk1977 if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
69404ca075Sdanielk1977 assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
70404ca075Sdanielk1977 assert( db==0 || p->pUnlockConnection!=db );
71404ca075Sdanielk1977 assert( db==0 || p->pBlockingConnection!=db );
72404ca075Sdanielk1977 }
73404ca075Sdanielk1977 }
74404ca075Sdanielk1977 }
75404ca075Sdanielk1977 #else
76404ca075Sdanielk1977 # define checkListProperties(x)
77404ca075Sdanielk1977 #endif
78404ca075Sdanielk1977
79404ca075Sdanielk1977 /*
80404ca075Sdanielk1977 ** Remove connection db from the blocked connections list. If connection
81404ca075Sdanielk1977 ** db is not currently a part of the list, this function is a no-op.
82404ca075Sdanielk1977 */
removeFromBlockedList(sqlite3 * db)83404ca075Sdanielk1977 static void removeFromBlockedList(sqlite3 *db){
84404ca075Sdanielk1977 sqlite3 **pp;
85404ca075Sdanielk1977 assertMutexHeld();
86404ca075Sdanielk1977 for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
87404ca075Sdanielk1977 if( *pp==db ){
88404ca075Sdanielk1977 *pp = (*pp)->pNextBlocked;
89404ca075Sdanielk1977 break;
90404ca075Sdanielk1977 }
91404ca075Sdanielk1977 }
92404ca075Sdanielk1977 }
93404ca075Sdanielk1977
94404ca075Sdanielk1977 /*
95404ca075Sdanielk1977 ** Add connection db to the blocked connections list. It is assumed
96404ca075Sdanielk1977 ** that it is not already a part of the list.
97404ca075Sdanielk1977 */
addToBlockedList(sqlite3 * db)98404ca075Sdanielk1977 static void addToBlockedList(sqlite3 *db){
99404ca075Sdanielk1977 sqlite3 **pp;
100404ca075Sdanielk1977 assertMutexHeld();
101404ca075Sdanielk1977 for(
102404ca075Sdanielk1977 pp=&sqlite3BlockedList;
103404ca075Sdanielk1977 *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
104404ca075Sdanielk1977 pp=&(*pp)->pNextBlocked
105404ca075Sdanielk1977 );
106404ca075Sdanielk1977 db->pNextBlocked = *pp;
107404ca075Sdanielk1977 *pp = db;
108404ca075Sdanielk1977 }
109404ca075Sdanielk1977
110404ca075Sdanielk1977 /*
111*ccb2113aSdrh ** Obtain the STATIC_MAIN mutex.
112404ca075Sdanielk1977 */
enterMutex(void)11364aca191Sdanielk1977 static void enterMutex(void){
114*ccb2113aSdrh sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
115404ca075Sdanielk1977 checkListProperties(0);
116404ca075Sdanielk1977 }
117404ca075Sdanielk1977
118404ca075Sdanielk1977 /*
119*ccb2113aSdrh ** Release the STATIC_MAIN mutex.
120404ca075Sdanielk1977 */
leaveMutex(void)12164aca191Sdanielk1977 static void leaveMutex(void){
122404ca075Sdanielk1977 assertMutexHeld();
123404ca075Sdanielk1977 checkListProperties(0);
124*ccb2113aSdrh sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
125404ca075Sdanielk1977 }
126404ca075Sdanielk1977
127404ca075Sdanielk1977 /*
128404ca075Sdanielk1977 ** Register an unlock-notify callback.
12965a73badSdrh **
13065a73badSdrh ** This is called after connection "db" has attempted some operation
13165a73badSdrh ** but has received an SQLITE_LOCKED error because another connection
13265a73badSdrh ** (call it pOther) in the same process was busy using the same shared
13365a73badSdrh ** cache. pOther is found by looking at db->pBlockingConnection.
13465a73badSdrh **
13565a73badSdrh ** If there is no blocking connection, the callback is invoked immediately,
13665a73badSdrh ** before this routine returns.
13765a73badSdrh **
13865a73badSdrh ** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
13965a73badSdrh ** a deadlock.
14065a73badSdrh **
14165a73badSdrh ** Otherwise, make arrangements to invoke xNotify when pOther drops
14265a73badSdrh ** its locks.
14365a73badSdrh **
14465a73badSdrh ** Each call to this routine overrides any prior callbacks registered
14565a73badSdrh ** on the same "db". If xNotify==0 then any prior callbacks are immediately
14665a73badSdrh ** cancelled.
147404ca075Sdanielk1977 */
sqlite3_unlock_notify(sqlite3 * db,void (* xNotify)(void **,int),void * pArg)148404ca075Sdanielk1977 int sqlite3_unlock_notify(
149404ca075Sdanielk1977 sqlite3 *db,
150404ca075Sdanielk1977 void (*xNotify)(void **, int),
151404ca075Sdanielk1977 void *pArg
152404ca075Sdanielk1977 ){
153404ca075Sdanielk1977 int rc = SQLITE_OK;
154404ca075Sdanielk1977
155404ca075Sdanielk1977 sqlite3_mutex_enter(db->mutex);
156404ca075Sdanielk1977 enterMutex();
157404ca075Sdanielk1977
15865a73badSdrh if( xNotify==0 ){
15965a73badSdrh removeFromBlockedList(db);
1604b93f6bdSshaneh db->pBlockingConnection = 0;
16165a73badSdrh db->pUnlockConnection = 0;
16265a73badSdrh db->xUnlockNotify = 0;
16365a73badSdrh db->pUnlockArg = 0;
16465a73badSdrh }else if( 0==db->pBlockingConnection ){
165404ca075Sdanielk1977 /* The blocking transaction has been concluded. Or there never was a
166404ca075Sdanielk1977 ** blocking transaction. In either case, invoke the notify callback
167404ca075Sdanielk1977 ** immediately.
168404ca075Sdanielk1977 */
169404ca075Sdanielk1977 xNotify(&pArg, 1);
170404ca075Sdanielk1977 }else{
171404ca075Sdanielk1977 sqlite3 *p;
172404ca075Sdanielk1977
17365a73badSdrh for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
174404ca075Sdanielk1977 if( p ){
175404ca075Sdanielk1977 rc = SQLITE_LOCKED; /* Deadlock detected. */
176404ca075Sdanielk1977 }else{
177404ca075Sdanielk1977 db->pUnlockConnection = db->pBlockingConnection;
178404ca075Sdanielk1977 db->xUnlockNotify = xNotify;
179404ca075Sdanielk1977 db->pUnlockArg = pArg;
180404ca075Sdanielk1977 removeFromBlockedList(db);
181404ca075Sdanielk1977 addToBlockedList(db);
182404ca075Sdanielk1977 }
183404ca075Sdanielk1977 }
184404ca075Sdanielk1977
185404ca075Sdanielk1977 leaveMutex();
186404ca075Sdanielk1977 assert( !db->mallocFailed );
18713f40da3Sdrh sqlite3ErrorWithMsg(db, rc, (rc?"database is deadlocked":0));
188404ca075Sdanielk1977 sqlite3_mutex_leave(db->mutex);
189404ca075Sdanielk1977 return rc;
190404ca075Sdanielk1977 }
191404ca075Sdanielk1977
192404ca075Sdanielk1977 /*
193404ca075Sdanielk1977 ** This function is called while stepping or preparing a statement
194404ca075Sdanielk1977 ** associated with connection db. The operation will return SQLITE_LOCKED
195404ca075Sdanielk1977 ** to the user because it requires a lock that will not be available
196404ca075Sdanielk1977 ** until connection pBlocker concludes its current transaction.
197404ca075Sdanielk1977 */
sqlite3ConnectionBlocked(sqlite3 * db,sqlite3 * pBlocker)198404ca075Sdanielk1977 void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
199404ca075Sdanielk1977 enterMutex();
200335c0faaSdrh if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
201404ca075Sdanielk1977 addToBlockedList(db);
202404ca075Sdanielk1977 }
203404ca075Sdanielk1977 db->pBlockingConnection = pBlocker;
204404ca075Sdanielk1977 leaveMutex();
205404ca075Sdanielk1977 }
206404ca075Sdanielk1977
207404ca075Sdanielk1977 /*
20865a73badSdrh ** This function is called when
20965a73badSdrh ** the transaction opened by database db has just finished. Locks held
210404ca075Sdanielk1977 ** by database connection db have been released.
211404ca075Sdanielk1977 **
212404ca075Sdanielk1977 ** This function loops through each entry in the blocked connections
213404ca075Sdanielk1977 ** list and does the following:
214404ca075Sdanielk1977 **
215404ca075Sdanielk1977 ** 1) If the sqlite3.pBlockingConnection member of a list entry is
216404ca075Sdanielk1977 ** set to db, then set pBlockingConnection=0.
217404ca075Sdanielk1977 **
218404ca075Sdanielk1977 ** 2) If the sqlite3.pUnlockConnection member of a list entry is
219404ca075Sdanielk1977 ** set to db, then invoke the configured unlock-notify callback and
220404ca075Sdanielk1977 ** set pUnlockConnection=0.
221404ca075Sdanielk1977 **
222404ca075Sdanielk1977 ** 3) If the two steps above mean that pBlockingConnection==0 and
223404ca075Sdanielk1977 ** pUnlockConnection==0, remove the entry from the blocked connections
224404ca075Sdanielk1977 ** list.
225404ca075Sdanielk1977 */
sqlite3ConnectionUnlocked(sqlite3 * db)226404ca075Sdanielk1977 void sqlite3ConnectionUnlocked(sqlite3 *db){
227404ca075Sdanielk1977 void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
228404ca075Sdanielk1977 int nArg = 0; /* Number of entries in aArg[] */
229404ca075Sdanielk1977 sqlite3 **pp; /* Iterator variable */
23065a73badSdrh void **aArg; /* Arguments to the unlock callback */
23165a73badSdrh void **aDyn = 0; /* Dynamically allocated space for aArg[] */
23265a73badSdrh void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
233404ca075Sdanielk1977
23465a73badSdrh aArg = aStatic;
235*ccb2113aSdrh enterMutex(); /* Enter STATIC_MAIN mutex */
236404ca075Sdanielk1977
237404ca075Sdanielk1977 /* This loop runs once for each entry in the blocked-connections list. */
238404ca075Sdanielk1977 for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
239404ca075Sdanielk1977 sqlite3 *p = *pp;
240404ca075Sdanielk1977
241404ca075Sdanielk1977 /* Step 1. */
242404ca075Sdanielk1977 if( p->pBlockingConnection==db ){
243404ca075Sdanielk1977 p->pBlockingConnection = 0;
244404ca075Sdanielk1977 }
245404ca075Sdanielk1977
246404ca075Sdanielk1977 /* Step 2. */
247404ca075Sdanielk1977 if( p->pUnlockConnection==db ){
248404ca075Sdanielk1977 assert( p->xUnlockNotify );
249404ca075Sdanielk1977 if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
250404ca075Sdanielk1977 xUnlockNotify(aArg, nArg);
251404ca075Sdanielk1977 nArg = 0;
252404ca075Sdanielk1977 }
253404ca075Sdanielk1977
254404ca075Sdanielk1977 sqlite3BeginBenignMalloc();
255404ca075Sdanielk1977 assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
2567c01f1d7Sdrh assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
2577c01f1d7Sdrh if( (!aDyn && nArg==(int)ArraySize(aStatic))
258b975598eSdrh || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*)))
259404ca075Sdanielk1977 ){
260404ca075Sdanielk1977 /* The aArg[] array needs to grow. */
261404ca075Sdanielk1977 void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
262404ca075Sdanielk1977 if( pNew ){
263404ca075Sdanielk1977 memcpy(pNew, aArg, nArg*sizeof(void *));
264404ca075Sdanielk1977 sqlite3_free(aDyn);
265404ca075Sdanielk1977 aDyn = aArg = pNew;
266404ca075Sdanielk1977 }else{
267404ca075Sdanielk1977 /* This occurs when the array of context pointers that need to
268404ca075Sdanielk1977 ** be passed to the unlock-notify callback is larger than the
269404ca075Sdanielk1977 ** aStatic[] array allocated on the stack and the attempt to
270404ca075Sdanielk1977 ** allocate a larger array from the heap has failed.
271404ca075Sdanielk1977 **
272404ca075Sdanielk1977 ** This is a difficult situation to handle. Returning an error
273404ca075Sdanielk1977 ** code to the caller is insufficient, as even if an error code
274404ca075Sdanielk1977 ** is returned the transaction on connection db will still be
275404ca075Sdanielk1977 ** closed and the unlock-notify callbacks on blocked connections
276404ca075Sdanielk1977 ** will go unissued. This might cause the application to wait
277404ca075Sdanielk1977 ** indefinitely for an unlock-notify callback that will never
278404ca075Sdanielk1977 ** arrive.
279404ca075Sdanielk1977 **
280404ca075Sdanielk1977 ** Instead, invoke the unlock-notify callback with the context
281404ca075Sdanielk1977 ** array already accumulated. We can then clear the array and
282404ca075Sdanielk1977 ** begin accumulating any further context pointers without
283404ca075Sdanielk1977 ** requiring any dynamic allocation. This is sub-optimal because
284404ca075Sdanielk1977 ** it means that instead of one callback with a large array of
285404ca075Sdanielk1977 ** context pointers the application will receive two or more
286404ca075Sdanielk1977 ** callbacks with smaller arrays of context pointers, which will
287404ca075Sdanielk1977 ** reduce the applications ability to prioritize multiple
288404ca075Sdanielk1977 ** connections. But it is the best that can be done under the
289404ca075Sdanielk1977 ** circumstances.
290404ca075Sdanielk1977 */
291404ca075Sdanielk1977 xUnlockNotify(aArg, nArg);
292404ca075Sdanielk1977 nArg = 0;
293404ca075Sdanielk1977 }
294404ca075Sdanielk1977 }
295404ca075Sdanielk1977 sqlite3EndBenignMalloc();
296404ca075Sdanielk1977
297404ca075Sdanielk1977 aArg[nArg++] = p->pUnlockArg;
298404ca075Sdanielk1977 xUnlockNotify = p->xUnlockNotify;
299404ca075Sdanielk1977 p->pUnlockConnection = 0;
300404ca075Sdanielk1977 p->xUnlockNotify = 0;
301404ca075Sdanielk1977 p->pUnlockArg = 0;
302404ca075Sdanielk1977 }
303404ca075Sdanielk1977
304404ca075Sdanielk1977 /* Step 3. */
305335c0faaSdrh if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
306404ca075Sdanielk1977 /* Remove connection p from the blocked connections list. */
307404ca075Sdanielk1977 *pp = p->pNextBlocked;
308404ca075Sdanielk1977 p->pNextBlocked = 0;
309404ca075Sdanielk1977 }else{
310404ca075Sdanielk1977 pp = &p->pNextBlocked;
311404ca075Sdanielk1977 }
312404ca075Sdanielk1977 }
313404ca075Sdanielk1977
314404ca075Sdanielk1977 if( nArg!=0 ){
315404ca075Sdanielk1977 xUnlockNotify(aArg, nArg);
316404ca075Sdanielk1977 }
317404ca075Sdanielk1977 sqlite3_free(aDyn);
318*ccb2113aSdrh leaveMutex(); /* Leave STATIC_MAIN mutex */
319404ca075Sdanielk1977 }
320404ca075Sdanielk1977
321404ca075Sdanielk1977 /*
322404ca075Sdanielk1977 ** This is called when the database connection passed as an argument is
323404ca075Sdanielk1977 ** being closed. The connection is removed from the blocked list.
324404ca075Sdanielk1977 */
sqlite3ConnectionClosed(sqlite3 * db)325404ca075Sdanielk1977 void sqlite3ConnectionClosed(sqlite3 *db){
326404ca075Sdanielk1977 sqlite3ConnectionUnlocked(db);
327404ca075Sdanielk1977 enterMutex();
328404ca075Sdanielk1977 removeFromBlockedList(db);
329404ca075Sdanielk1977 checkListProperties(db);
330404ca075Sdanielk1977 leaveMutex();
331404ca075Sdanielk1977 }
332404ca075Sdanielk1977 #endif
333