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