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