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