xref: /sqlite-3.40.0/src/notify.c (revision 404ca075)
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.1 2009/03/16 13:19:36 danielk1977 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(){
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(){
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 int sqlite3_unlock_notify(
133   sqlite3 *db,
134   void (*xNotify)(void **, int),
135   void *pArg
136 ){
137   int rc = SQLITE_OK;
138 
139   sqlite3_mutex_enter(db->mutex);
140   enterMutex();
141 
142   if( 0==db->pBlockingConnection ){
143     /* The blocking transaction has been concluded. Or there never was a
144     ** blocking transaction. In either case, invoke the notify callback
145     ** immediately.
146     */
147     xNotify(&pArg, 1);
148   }else{
149     sqlite3 *p;
150 
151     for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection);
152     if( p ){
153       rc = SQLITE_LOCKED;              /* Deadlock detected. */
154     }else{
155       db->pUnlockConnection = db->pBlockingConnection;
156       db->xUnlockNotify = xNotify;
157       db->pUnlockArg = pArg;
158       removeFromBlockedList(db);
159       addToBlockedList(db);
160     }
161   }
162 
163   leaveMutex();
164   assert( !db->mallocFailed );
165   sqlite3Error(db, rc, (rc?"database is deadlocked":0));
166   sqlite3_mutex_leave(db->mutex);
167   return rc;
168 }
169 
170 /*
171 ** This function is called while stepping or preparing a statement
172 ** associated with connection db. The operation will return SQLITE_LOCKED
173 ** to the user because it requires a lock that will not be available
174 ** until connection pBlocker concludes its current transaction.
175 */
176 void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
177   enterMutex();
178   if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
179     addToBlockedList(db);
180   }
181   db->pBlockingConnection = pBlocker;
182   leaveMutex();
183 }
184 
185 /*
186 ** The transaction opened by database db has just finished. Locks held
187 ** by database connection db have been released.
188 **
189 ** This function loops through each entry in the blocked connections
190 ** list and does the following:
191 **
192 **   1) If the sqlite3.pBlockingConnection member of a list entry is
193 **      set to db, then set pBlockingConnection=0.
194 **
195 **   2) If the sqlite3.pUnlockConnection member of a list entry is
196 **      set to db, then invoke the configured unlock-notify callback and
197 **      set pUnlockConnection=0.
198 **
199 **   3) If the two steps above mean that pBlockingConnection==0 and
200 **      pUnlockConnection==0, remove the entry from the blocked connections
201 **      list.
202 */
203 void sqlite3ConnectionUnlocked(sqlite3 *db){
204   void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
205   int nArg = 0;                            /* Number of entries in aArg[] */
206   sqlite3 **pp;                            /* Iterator variable */
207 
208   void *aStatic[16];
209   void **aArg = aStatic;
210   void **aDyn = 0;
211 
212   enterMutex();         /* Enter STATIC_MASTER mutex */
213 
214   /* This loop runs once for each entry in the blocked-connections list. */
215   for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
216     sqlite3 *p = *pp;
217 
218     /* Step 1. */
219     if( p->pBlockingConnection==db ){
220       p->pBlockingConnection = 0;
221     }
222 
223     /* Step 2. */
224     if( p->pUnlockConnection==db ){
225       assert( p->xUnlockNotify );
226       if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
227         xUnlockNotify(aArg, nArg);
228         nArg = 0;
229       }
230 
231       sqlite3BeginBenignMalloc();
232       assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
233       assert( nArg<=ArraySize(aStatic) || aArg==aDyn );
234       if( (!aDyn && nArg==ArraySize(aStatic))
235        || (aDyn && nArg==(sqlite3DbMallocSize(db, aDyn)/sizeof(void*)))
236       ){
237         /* The aArg[] array needs to grow. */
238         void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
239         if( pNew ){
240           memcpy(pNew, aArg, nArg*sizeof(void *));
241           sqlite3_free(aDyn);
242           aDyn = aArg = pNew;
243         }else{
244           /* This occurs when the array of context pointers that need to
245           ** be passed to the unlock-notify callback is larger than the
246           ** aStatic[] array allocated on the stack and the attempt to
247           ** allocate a larger array from the heap has failed.
248           **
249           ** This is a difficult situation to handle. Returning an error
250           ** code to the caller is insufficient, as even if an error code
251           ** is returned the transaction on connection db will still be
252           ** closed and the unlock-notify callbacks on blocked connections
253           ** will go unissued. This might cause the application to wait
254           ** indefinitely for an unlock-notify callback that will never
255           ** arrive.
256           **
257           ** Instead, invoke the unlock-notify callback with the context
258           ** array already accumulated. We can then clear the array and
259           ** begin accumulating any further context pointers without
260           ** requiring any dynamic allocation. This is sub-optimal because
261           ** it means that instead of one callback with a large array of
262           ** context pointers the application will receive two or more
263           ** callbacks with smaller arrays of context pointers, which will
264           ** reduce the applications ability to prioritize multiple
265           ** connections. But it is the best that can be done under the
266           ** circumstances.
267           */
268           xUnlockNotify(aArg, nArg);
269           nArg = 0;
270         }
271       }
272       sqlite3EndBenignMalloc();
273 
274       aArg[nArg++] = p->pUnlockArg;
275       xUnlockNotify = p->xUnlockNotify;
276       p->pUnlockConnection = 0;
277       p->xUnlockNotify = 0;
278       p->pUnlockArg = 0;
279     }
280 
281     /* Step 3. */
282     if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
283       /* Remove connection p from the blocked connections list. */
284       *pp = p->pNextBlocked;
285       p->pNextBlocked = 0;
286     }else{
287       pp = &p->pNextBlocked;
288     }
289   }
290 
291   if( nArg!=0 ){
292     xUnlockNotify(aArg, nArg);
293   }
294   sqlite3_free(aDyn);
295   leaveMutex();         /* Leave STATIC_MASTER mutex */
296 }
297 
298 /*
299 ** This is called when the database connection passed as an argument is
300 ** being closed. The connection is removed from the blocked list.
301 */
302 void sqlite3ConnectionClosed(sqlite3 *db){
303   sqlite3ConnectionUnlocked(db);
304   enterMutex();
305   removeFromBlockedList(db);
306   checkListProperties(db);
307   leaveMutex();
308 }
309 #endif
310 
311