xref: /sqlite-3.40.0/src/notify.c (revision 4b93f6bd)
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   sqlite3Error(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 ){
201     /*
202     ** We can not register an unlock callback unless we think we are
203     ** blocked.
204     */
205     assert( db->pUnlockConnection==0 );
206     addToBlockedList(db);
207   }
208   db->pBlockingConnection = pBlocker;
209   leaveMutex();
210 }
211 
212 /*
213 ** This function is called when
214 ** the transaction opened by database db has just finished. Locks held
215 ** by database connection db have been released.
216 **
217 ** This function loops through each entry in the blocked connections
218 ** list and does the following:
219 **
220 **   1) If the sqlite3.pBlockingConnection member of a list entry is
221 **      set to db, then set pBlockingConnection=0.
222 **
223 **   2) If the sqlite3.pUnlockConnection member of a list entry is
224 **      set to db, then invoke the configured unlock-notify callback and
225 **      set pUnlockConnection=0.
226 **
227 **   3) If the two steps above mean that pBlockingConnection==0 and
228 **      pUnlockConnection==0, remove the entry from the blocked connections
229 **      list.
230 */
231 void sqlite3ConnectionUnlocked(sqlite3 *db){
232   void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
233   int nArg = 0;                            /* Number of entries in aArg[] */
234   sqlite3 **pp;                            /* Iterator variable */
235   void **aArg;               /* Arguments to the unlock callback */
236   void **aDyn = 0;           /* Dynamically allocated space for aArg[] */
237   void *aStatic[16];         /* Starter space for aArg[].  No malloc required */
238 
239   aArg = aStatic;
240   enterMutex();         /* Enter STATIC_MASTER mutex */
241 
242   /* This loop runs once for each entry in the blocked-connections list. */
243   for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
244     sqlite3 *p = *pp;
245 
246     /* Step 1. */
247     if( p->pBlockingConnection==db ){
248       p->pBlockingConnection = 0;
249     }
250 
251     /* Step 2. */
252     if( p->pUnlockConnection==db ){
253       assert( p->xUnlockNotify );
254       if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
255         xUnlockNotify(aArg, nArg);
256         nArg = 0;
257       }
258 
259       sqlite3BeginBenignMalloc();
260       assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
261       assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
262       if( (!aDyn && nArg==(int)ArraySize(aStatic))
263        || (aDyn && nArg==(int)(sqlite3DbMallocSize(db, aDyn)/sizeof(void*)))
264       ){
265         /* The aArg[] array needs to grow. */
266         void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
267         if( pNew ){
268           memcpy(pNew, aArg, nArg*sizeof(void *));
269           sqlite3_free(aDyn);
270           aDyn = aArg = pNew;
271         }else{
272           /* This occurs when the array of context pointers that need to
273           ** be passed to the unlock-notify callback is larger than the
274           ** aStatic[] array allocated on the stack and the attempt to
275           ** allocate a larger array from the heap has failed.
276           **
277           ** This is a difficult situation to handle. Returning an error
278           ** code to the caller is insufficient, as even if an error code
279           ** is returned the transaction on connection db will still be
280           ** closed and the unlock-notify callbacks on blocked connections
281           ** will go unissued. This might cause the application to wait
282           ** indefinitely for an unlock-notify callback that will never
283           ** arrive.
284           **
285           ** Instead, invoke the unlock-notify callback with the context
286           ** array already accumulated. We can then clear the array and
287           ** begin accumulating any further context pointers without
288           ** requiring any dynamic allocation. This is sub-optimal because
289           ** it means that instead of one callback with a large array of
290           ** context pointers the application will receive two or more
291           ** callbacks with smaller arrays of context pointers, which will
292           ** reduce the applications ability to prioritize multiple
293           ** connections. But it is the best that can be done under the
294           ** circumstances.
295           */
296           xUnlockNotify(aArg, nArg);
297           nArg = 0;
298         }
299       }
300       sqlite3EndBenignMalloc();
301 
302       aArg[nArg++] = p->pUnlockArg;
303       xUnlockNotify = p->xUnlockNotify;
304       p->pUnlockConnection = 0;
305       p->xUnlockNotify = 0;
306       p->pUnlockArg = 0;
307     }
308 
309     /* Step 3. */
310     if( p->pBlockingConnection==0 ){
311       /*
312       ** If we were blocked on db, we would set
313       ** pBlockingConnection to 0 above.  And we can
314       ** only wait on a connection we are blocked on.
315       ** So if we were waiting on db (pUnlockConnection==db)
316       ** then it would have been set to 0 above as well.
317       */
318       assert( p->pUnlockConnection==0 );
319       /* Remove connection p from the blocked connections list. */
320       *pp = p->pNextBlocked;
321       p->pNextBlocked = 0;
322     }else{
323       pp = &p->pNextBlocked;
324     }
325   }
326 
327   if( nArg!=0 ){
328     xUnlockNotify(aArg, nArg);
329   }
330   sqlite3_free(aDyn);
331   leaveMutex();         /* Leave STATIC_MASTER mutex */
332 }
333 
334 /*
335 ** This is called when the database connection passed as an argument is
336 ** being closed. The connection is removed from the blocked list.
337 */
338 void sqlite3ConnectionClosed(sqlite3 *db){
339   sqlite3ConnectionUnlocked(db);
340   enterMutex();
341   removeFromBlockedList(db);
342   checkListProperties(db);
343   leaveMutex();
344 }
345 #endif
346