OLD | NEW |
(Empty) | |
| 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->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 sqlite3ErrorWithMsg(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 */ |
| 198 void 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 */ |
| 226 void 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 */ |
| 325 void sqlite3ConnectionClosed(sqlite3 *db){ |
| 326 sqlite3ConnectionUnlocked(db); |
| 327 enterMutex(); |
| 328 removeFromBlockedList(db); |
| 329 checkListProperties(db); |
| 330 leaveMutex(); |
| 331 } |
| 332 #endif |
OLD | NEW |