Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a bug in the demo "superlock" code preventing locks from being released in some circumstances. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
65c393793ff5fdb935d5acfe5bdc3bca |
User & Date: | dan 2010-12-07 16:39:26.000 |
Context
2010-12-07
| ||
17:12 | Work around restriction in Windows file locking. (check-in: fe441df9ba user: shaneh tags: trunk) | |
16:39 | Fix a bug in the demo "superlock" code preventing locks from being released in some circumstances. (check-in: 65c393793f user: dan tags: trunk) | |
14:59 | Version 3.7.4 release candidate 3 (check-in: 11c74c0dae user: drh tags: trunk) | |
Changes
Changes to src/test_superlock.c.
︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | struct SuperlockBusy { int (*xBusy)(void*,int); /* Pointer to busy-handler function */ void *pBusyArg; /* First arg to pass to xBusy */ int nBusy; /* Number of times xBusy has been invoked */ }; typedef struct SuperlockBusy SuperlockBusy; /* ** The pCtx pointer passed to this function is actually a pointer to a ** SuperlockBusy structure. Invoke the busy-handler function encapsulated ** by the structure and return the result. */ static int superlockBusyHandler(void *pCtx, int UNUSED){ SuperlockBusy *pBusy = (SuperlockBusy *)pCtx; if( pBusy->xBusy==0 ) return 0; return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++); } /* ** This function is used to determine if the main database file for ** connection db is open in WAL mode or not. If no error occurs and the ** database file is in WAL mode, set *pbWal to true and return SQLITE_OK. ** If it is not in WAL mode, set *pbWal to false. ** ** If an error occurs, return an SQLite error code. The value of *pbWal ** is undefined in this case. */ | > > > > > > > > > > > | | | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | struct SuperlockBusy { int (*xBusy)(void*,int); /* Pointer to busy-handler function */ void *pBusyArg; /* First arg to pass to xBusy */ int nBusy; /* Number of times xBusy has been invoked */ }; typedef struct SuperlockBusy SuperlockBusy; /* ** An instance of the following structure is allocated for each active ** superlock. The opaque handle returned by sqlite3demo_superlock() is ** actually a pointer to an instance of this structure. */ struct Superlock { sqlite3 *db; /* Database handle used to lock db */ int bWal; /* True if db is a WAL database */ }; typedef struct Superlock Superlock; /* ** The pCtx pointer passed to this function is actually a pointer to a ** SuperlockBusy structure. Invoke the busy-handler function encapsulated ** by the structure and return the result. */ static int superlockBusyHandler(void *pCtx, int UNUSED){ SuperlockBusy *pBusy = (SuperlockBusy *)pCtx; if( pBusy->xBusy==0 ) return 0; return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++); } /* ** This function is used to determine if the main database file for ** connection db is open in WAL mode or not. If no error occurs and the ** database file is in WAL mode, set *pbWal to true and return SQLITE_OK. ** If it is not in WAL mode, set *pbWal to false. ** ** If an error occurs, return an SQLite error code. The value of *pbWal ** is undefined in this case. */ static int superlockIsWal(Superlock *pLock){ int rc; /* Return Code */ sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */ rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0); if( rc!=SQLITE_OK ) return rc; pLock->bWal = 0; if( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zMode = (const char *)sqlite3_column_text(pStmt, 0); if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){ pLock->bWal = 1; } } return sqlite3_finalize(pStmt); } /* |
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | /* Obtain exclusive locks on all the "read-lock" slots. Once these locks ** are held, it is guaranteed that there are no active reader, writer or ** checkpointer clients. */ rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy); return rc; } /* ** Obtain a superlock on the database file identified by zPath, using the ** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is ** returned and output variable *ppLock is populated with an opaque handle ** that may be used with sqlite3demo_superunlock() to release the lock. ** | > > > > > > > > > > > > > > > > > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | /* Obtain exclusive locks on all the "read-lock" slots. Once these locks ** are held, it is guaranteed that there are no active reader, writer or ** checkpointer clients. */ rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy); return rc; } /* ** Release a superlock held on a database file. The argument passed to ** this function must have been obtained from a successful call to ** sqlite3demo_superlock(). */ void sqlite3demo_superunlock(void *pLock){ Superlock *p = (Superlock *)pLock; if( p->bWal ){ int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE; sqlite3_file *fd = 0; sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); fd->pMethods->xShmLock(fd, 2, SQLITE_SHM_NLOCK-2, flags); } sqlite3_close(p->db); sqlite3_free(p); } /* ** Obtain a superlock on the database file identified by zPath, using the ** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is ** returned and output variable *ppLock is populated with an opaque handle ** that may be used with sqlite3demo_superunlock() to release the lock. ** |
︙ | ︙ | |||
150 151 152 153 154 155 156 | int sqlite3demo_superlock( const char *zPath, /* Path to database file to lock */ const char *zVfs, /* VFS to use to access database file */ int (*xBusy)(void*,int), /* Busy handler callback */ void *pBusyArg, /* Context arg for busy handler */ void **ppLock /* OUT: Context to pass to superunlock() */ ){ | < > > > > > | | | < | | | | | < < < < < < < < < | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | int sqlite3demo_superlock( const char *zPath, /* Path to database file to lock */ const char *zVfs, /* VFS to use to access database file */ int (*xBusy)(void*,int), /* Busy handler callback */ void *pBusyArg, /* Context arg for busy handler */ void **ppLock /* OUT: Context to pass to superunlock() */ ){ SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */ int rc; /* Return code */ Superlock *pLock; pLock = sqlite3_malloc(sizeof(Superlock)); if( !pLock ) return SQLITE_NOMEM; memset(pLock, 0, sizeof(Superlock)); /* Open a database handle on the file to superlock. */ rc = sqlite3_open_v2( zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs ); /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not ** a WAL database, this is all we need to do. ** ** A wrapper function is used to invoke the busy-handler instead of ** registering the busy-handler function supplied by the user directly ** with SQLite. This is because the same busy-handler function may be ** invoked directly later on when attempting to obtain the extra locks ** required in WAL mode. By using the wrapper, we are able to guarantee ** that the "nBusy" integer parameter passed to the users busy-handler ** represents the total number of busy-handler invocations made within ** this call to sqlite3demo_superlock(), including any made during the ** "BEGIN EXCLUSIVE". */ if( rc==SQLITE_OK ){ busy.xBusy = xBusy; busy.pBusyArg = pBusyArg; sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy); rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0); } /* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL ** database, call superlockWalLock() to obtain the extra locks required ** to prevent readers, writers and/or checkpointers from accessing the ** db while this process is holding the superlock. ** ** Before attempting any WAL locks, commit the transaction started above ** to drop the WAL read and write locks currently held. Otherwise, the ** new WAL locks may conflict with the old. */ if( rc==SQLITE_OK ){ if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){ rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0); if( rc==SQLITE_OK ){ rc = superlockWalLock(pLock->db, &busy); } } } if( rc!=SQLITE_OK ){ sqlite3demo_superunlock(pLock); *ppLock = 0; }else{ *ppLock = pLock; } return rc; } /* ** End of example code. Everything below here is the test harness. ************************************************************************** ************************************************************************** *************************************************************************/ |
︙ | ︙ |
Changes to test/superlock.test.
︙ | ︙ | |||
139 140 141 142 143 144 145 146 147 148 149 150 151 152 | list [catch {sqlite3demo_superlock unlock test.db} msg] $msg } {1 {database is locked}} do_test 5.$tn.12 { sql3 COMMIT list [catch {sqlite3demo_superlock unlock test.db} msg] $msg } {0 unlock} unlock } proc read_content {file} { if {[file exists $file]==0} {return ""} set fd [open $file] fconfigure $fd -encoding binary -translation binary set content [read $fd] | > > > > > > > > > | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | list [catch {sqlite3demo_superlock unlock test.db} msg] $msg } {1 {database is locked}} do_test 5.$tn.12 { sql3 COMMIT list [catch {sqlite3demo_superlock unlock test.db} msg] $msg } {0 unlock} unlock do_test 5.$tn.13 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6} do_test 5.$tn.14 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6} do_test 5.$tn.15 { sqlite3demo_superlock unlock test.db } {unlock} do_test 5.$tn.16 { unlock } {} do_test 5.$tn.17 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6} do_test 5.$tn.18 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6} do_test 5.$tn.19 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6} } proc read_content {file} { if {[file exists $file]==0} {return ""} set fd [open $file] fconfigure $fd -encoding binary -translation binary set content [read $fd] |
︙ | ︙ |