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