Merge sqlite-release(3.44.2) into prerelease-integration
[sqlcipher.git] / src / notify.c
blob6a4cab8755017c3e7f95c314d3673f6252fcbe10
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.
11 *************************************************************************
13 ** This file contains the implementation of the sqlite3_unlock_notify()
14 ** API method and its associated functionality.
16 #include "sqliteInt.h"
17 #include "btreeInt.h"
19 /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
20 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
23 ** Public interfaces:
25 ** sqlite3ConnectionBlocked()
26 ** sqlite3ConnectionUnlocked()
27 ** sqlite3ConnectionClosed()
28 ** sqlite3_unlock_notify()
31 #define assertMutexHeld() \
32 assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) )
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.
40 static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
42 #ifndef NDEBUG
44 ** This function is a complex assert() that verifies the following
45 ** properties of the blocked connections list:
47 ** 1) Each entry in the list has a non-NULL value for either
48 ** pUnlockConnection or pBlockingConnection, or both.
50 ** 2) All entries in the list that share a common value for
51 ** xUnlockNotify are grouped together.
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.
57 static void checkListProperties(sqlite3 *db){
58 sqlite3 *p;
59 for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
60 int seen = 0;
61 sqlite3 *p2;
63 /* Verify property (1) */
64 assert( p->pUnlockConnection || p->pBlockingConnection );
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 );
75 #else
76 # define checkListProperties(x)
77 #endif
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.
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;
95 ** Add connection db to the blocked connections list. It is assumed
96 ** that it is not already a part of the list.
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
106 db->pNextBlocked = *pp;
107 *pp = db;
111 ** Obtain the STATIC_MAIN mutex.
113 static void enterMutex(void){
114 sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
115 checkListProperties(0);
119 ** Release the STATIC_MAIN mutex.
121 static void leaveMutex(void){
122 assertMutexHeld();
123 checkListProperties(0);
124 sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
128 ** Register an unlock-notify callback.
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.
135 ** If there is no blocking connection, the callback is invoked immediately,
136 ** before this routine returns.
138 ** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
139 ** a deadlock.
141 ** Otherwise, make arrangements to invoke xNotify when pOther drops
142 ** its locks.
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.
148 int sqlite3_unlock_notify(
149 sqlite3 *db,
150 void (*xNotify)(void **, int),
151 void *pArg
153 int rc = SQLITE_OK;
155 #ifdef SQLITE_ENABLE_API_ARMOR
156 if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
157 #endif
158 sqlite3_mutex_enter(db->mutex);
159 enterMutex();
161 if( xNotify==0 ){
162 removeFromBlockedList(db);
163 db->pBlockingConnection = 0;
164 db->pUnlockConnection = 0;
165 db->xUnlockNotify = 0;
166 db->pUnlockArg = 0;
167 }else if( 0==db->pBlockingConnection ){
168 /* The blocking transaction has been concluded. Or there never was a
169 ** blocking transaction. In either case, invoke the notify callback
170 ** immediately.
172 xNotify(&pArg, 1);
173 }else{
174 sqlite3 *p;
176 for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
177 if( p ){
178 rc = SQLITE_LOCKED; /* Deadlock detected. */
179 }else{
180 db->pUnlockConnection = db->pBlockingConnection;
181 db->xUnlockNotify = xNotify;
182 db->pUnlockArg = pArg;
183 removeFromBlockedList(db);
184 addToBlockedList(db);
188 leaveMutex();
189 assert( !db->mallocFailed );
190 sqlite3ErrorWithMsg(db, rc, (rc?"database is deadlocked":0));
191 sqlite3_mutex_leave(db->mutex);
192 return rc;
196 ** This function is called while stepping or preparing a statement
197 ** associated with connection db. The operation will return SQLITE_LOCKED
198 ** to the user because it requires a lock that will not be available
199 ** until connection pBlocker concludes its current transaction.
201 void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
202 enterMutex();
203 if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
204 addToBlockedList(db);
206 db->pBlockingConnection = pBlocker;
207 leaveMutex();
211 ** This function is called when
212 ** the transaction opened by database db has just finished. Locks held
213 ** by database connection db have been released.
215 ** This function loops through each entry in the blocked connections
216 ** list and does the following:
218 ** 1) If the sqlite3.pBlockingConnection member of a list entry is
219 ** set to db, then set pBlockingConnection=0.
221 ** 2) If the sqlite3.pUnlockConnection member of a list entry is
222 ** set to db, then invoke the configured unlock-notify callback and
223 ** set pUnlockConnection=0.
225 ** 3) If the two steps above mean that pBlockingConnection==0 and
226 ** pUnlockConnection==0, remove the entry from the blocked connections
227 ** list.
229 void sqlite3ConnectionUnlocked(sqlite3 *db){
230 void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
231 int nArg = 0; /* Number of entries in aArg[] */
232 sqlite3 **pp; /* Iterator variable */
233 void **aArg; /* Arguments to the unlock callback */
234 void **aDyn = 0; /* Dynamically allocated space for aArg[] */
235 void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
237 aArg = aStatic;
238 enterMutex(); /* Enter STATIC_MAIN mutex */
240 /* This loop runs once for each entry in the blocked-connections list. */
241 for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
242 sqlite3 *p = *pp;
244 /* Step 1. */
245 if( p->pBlockingConnection==db ){
246 p->pBlockingConnection = 0;
249 /* Step 2. */
250 if( p->pUnlockConnection==db ){
251 assert( p->xUnlockNotify );
252 if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
253 xUnlockNotify(aArg, nArg);
254 nArg = 0;
257 sqlite3BeginBenignMalloc();
258 assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
259 assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
260 if( (!aDyn && nArg==(int)ArraySize(aStatic))
261 || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*)))
263 /* The aArg[] array needs to grow. */
264 void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
265 if( pNew ){
266 memcpy(pNew, aArg, nArg*sizeof(void *));
267 sqlite3_free(aDyn);
268 aDyn = aArg = pNew;
269 }else{
270 /* This occurs when the array of context pointers that need to
271 ** be passed to the unlock-notify callback is larger than the
272 ** aStatic[] array allocated on the stack and the attempt to
273 ** allocate a larger array from the heap has failed.
275 ** This is a difficult situation to handle. Returning an error
276 ** code to the caller is insufficient, as even if an error code
277 ** is returned the transaction on connection db will still be
278 ** closed and the unlock-notify callbacks on blocked connections
279 ** will go unissued. This might cause the application to wait
280 ** indefinitely for an unlock-notify callback that will never
281 ** arrive.
283 ** Instead, invoke the unlock-notify callback with the context
284 ** array already accumulated. We can then clear the array and
285 ** begin accumulating any further context pointers without
286 ** requiring any dynamic allocation. This is sub-optimal because
287 ** it means that instead of one callback with a large array of
288 ** context pointers the application will receive two or more
289 ** callbacks with smaller arrays of context pointers, which will
290 ** reduce the applications ability to prioritize multiple
291 ** connections. But it is the best that can be done under the
292 ** circumstances.
294 xUnlockNotify(aArg, nArg);
295 nArg = 0;
298 sqlite3EndBenignMalloc();
300 aArg[nArg++] = p->pUnlockArg;
301 xUnlockNotify = p->xUnlockNotify;
302 p->pUnlockConnection = 0;
303 p->xUnlockNotify = 0;
304 p->pUnlockArg = 0;
307 /* Step 3. */
308 if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
309 /* Remove connection p from the blocked connections list. */
310 *pp = p->pNextBlocked;
311 p->pNextBlocked = 0;
312 }else{
313 pp = &p->pNextBlocked;
317 if( nArg!=0 ){
318 xUnlockNotify(aArg, nArg);
320 sqlite3_free(aDyn);
321 leaveMutex(); /* Leave STATIC_MAIN mutex */
325 ** This is called when the database connection passed as an argument is
326 ** being closed. The connection is removed from the blocked list.
328 void sqlite3ConnectionClosed(sqlite3 *db){
329 sqlite3ConnectionUnlocked(db);
330 enterMutex();
331 removeFromBlockedList(db);
332 checkListProperties(db);
333 leaveMutex();
335 #endif