1 /* 2 ** 2010 November 19 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 ** Example code for obtaining an exclusive lock on an SQLite database 13 ** file. This method is complicated, but works for both WAL and rollback 14 ** mode database files. The interface to the example code in this file 15 ** consists of the following two functions: 16 ** 17 ** sqlite3demo_superlock() 18 ** sqlite3demo_superunlock() 19 */ 20 21 #include "sqlite3.h" 22 #include <string.h> /* memset(), strlen() */ 23 #include <assert.h> /* assert() */ 24 25 /* 26 ** A structure to collect a busy-handler callback and argument and a count 27 ** of the number of times it has been invoked. 28 */ 29 struct SuperlockBusy { 30 int (*xBusy)(void*,int); /* Pointer to busy-handler function */ 31 void *pBusyArg; /* First arg to pass to xBusy */ 32 int nBusy; /* Number of times xBusy has been invoked */ 33 }; 34 typedef struct SuperlockBusy SuperlockBusy; 35 36 /* 37 ** An instance of the following structure is allocated for each active 38 ** superlock. The opaque handle returned by sqlite3demo_superlock() is 39 ** actually a pointer to an instance of this structure. 40 */ 41 struct Superlock { 42 sqlite3 *db; /* Database handle used to lock db */ 43 int bWal; /* True if db is a WAL database */ 44 }; 45 typedef struct Superlock Superlock; 46 47 /* 48 ** The pCtx pointer passed to this function is actually a pointer to a 49 ** SuperlockBusy structure. Invoke the busy-handler function encapsulated 50 ** by the structure and return the result. 51 */ 52 static int superlockBusyHandler(void *pCtx, int UNUSED){ 53 SuperlockBusy *pBusy = (SuperlockBusy *)pCtx; 54 if( pBusy->xBusy==0 ) return 0; 55 return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++); 56 } 57 58 /* 59 ** This function is used to determine if the main database file for 60 ** connection db is open in WAL mode or not. If no error occurs and the 61 ** database file is in WAL mode, set *pbWal to true and return SQLITE_OK. 62 ** If it is not in WAL mode, set *pbWal to false. 63 ** 64 ** If an error occurs, return an SQLite error code. The value of *pbWal 65 ** is undefined in this case. 66 */ 67 static int superlockIsWal(Superlock *pLock){ 68 int rc; /* Return Code */ 69 sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */ 70 71 rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0); 72 if( rc!=SQLITE_OK ) return rc; 73 74 pLock->bWal = 0; 75 if( SQLITE_ROW==sqlite3_step(pStmt) ){ 76 const char *zMode = (const char *)sqlite3_column_text(pStmt, 0); 77 if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){ 78 pLock->bWal = 1; 79 } 80 } 81 82 return sqlite3_finalize(pStmt); 83 } 84 85 /* 86 ** Obtain an exclusive shm-lock on nByte bytes starting at offset idx 87 ** of the file fd. If the lock cannot be obtained immediately, invoke 88 ** the busy-handler until either it is obtained or the busy-handler 89 ** callback returns 0. 90 */ 91 static int superlockShmLock( 92 sqlite3_file *fd, /* Database file handle */ 93 int idx, /* Offset of shm-lock to obtain */ 94 int nByte, /* Number of consective bytes to lock */ 95 SuperlockBusy *pBusy /* Busy-handler wrapper object */ 96 ){ 97 int rc; 98 int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock; 99 do { 100 rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE); 101 }while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) ); 102 return rc; 103 } 104 105 /* 106 ** Obtain the extra locks on the database file required for WAL databases. 107 ** Invoke the supplied busy-handler as required. 108 */ 109 static int superlockWalLock( 110 sqlite3 *db, /* Database handle open on WAL database */ 111 SuperlockBusy *pBusy /* Busy handler wrapper object */ 112 ){ 113 int rc; /* Return code */ 114 sqlite3_file *fd = 0; /* Main database file handle */ 115 void volatile *p = 0; /* Pointer to first page of shared memory */ 116 117 /* Obtain a pointer to the sqlite3_file object open on the main db file. */ 118 rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); 119 if( rc!=SQLITE_OK ) return rc; 120 121 /* Obtain the "recovery" lock. Normally, this lock is only obtained by 122 ** clients running database recovery. 123 */ 124 rc = superlockShmLock(fd, 2, 1, pBusy); 125 if( rc!=SQLITE_OK ) return rc; 126 127 /* Zero the start of the first shared-memory page. This means that any 128 ** clients that open read or write transactions from this point on will 129 ** have to run recovery before proceeding. Since they need the "recovery" 130 ** lock that this process is holding to do that, no new read or write 131 ** transactions may now be opened. Nor can a checkpoint be run, for the 132 ** same reason. 133 */ 134 rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p); 135 if( rc!=SQLITE_OK ) return rc; 136 memset((void *)p, 0, 32); 137 138 /* Obtain exclusive locks on all the "read-lock" slots. Once these locks 139 ** are held, it is guaranteed that there are no active reader, writer or 140 ** checkpointer clients. 141 */ 142 rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy); 143 return rc; 144 } 145 146 /* 147 ** Release a superlock held on a database file. The argument passed to 148 ** this function must have been obtained from a successful call to 149 ** sqlite3demo_superlock(). 150 */ 151 void sqlite3demo_superunlock(void *pLock){ 152 Superlock *p = (Superlock *)pLock; 153 if( p->bWal ){ 154 int rc; /* Return code */ 155 int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE; 156 sqlite3_file *fd = 0; 157 rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); 158 if( rc==SQLITE_OK ){ 159 fd->pMethods->xShmLock(fd, 2, 1, flags); 160 fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags); 161 } 162 } 163 sqlite3_close(p->db); 164 sqlite3_free(p); 165 } 166 167 /* 168 ** Obtain a superlock on the database file identified by zPath, using the 169 ** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is 170 ** returned and output variable *ppLock is populated with an opaque handle 171 ** that may be used with sqlite3demo_superunlock() to release the lock. 172 ** 173 ** If an error occurs, *ppLock is set to 0 and an SQLite error code 174 ** (e.g. SQLITE_BUSY) is returned. 175 ** 176 ** If a required lock cannot be obtained immediately and the xBusy parameter 177 ** to this function is not NULL, then xBusy is invoked in the same way 178 ** as a busy-handler registered with SQLite (using sqlite3_busy_handler()) 179 ** until either the lock can be obtained or the busy-handler function returns 180 ** 0 (indicating "give up"). 181 */ 182 int sqlite3demo_superlock( 183 const char *zPath, /* Path to database file to lock */ 184 const char *zVfs, /* VFS to use to access database file */ 185 int (*xBusy)(void*,int), /* Busy handler callback */ 186 void *pBusyArg, /* Context arg for busy handler */ 187 void **ppLock /* OUT: Context to pass to superunlock() */ 188 ){ 189 SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */ 190 int rc; /* Return code */ 191 Superlock *pLock; 192 193 pLock = sqlite3_malloc(sizeof(Superlock)); 194 if( !pLock ) return SQLITE_NOMEM; 195 memset(pLock, 0, sizeof(Superlock)); 196 197 /* Open a database handle on the file to superlock. */ 198 rc = sqlite3_open_v2( 199 zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs 200 ); 201 202 /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not 203 ** a WAL database, this is all we need to do. 204 ** 205 ** A wrapper function is used to invoke the busy-handler instead of 206 ** registering the busy-handler function supplied by the user directly 207 ** with SQLite. This is because the same busy-handler function may be 208 ** invoked directly later on when attempting to obtain the extra locks 209 ** required in WAL mode. By using the wrapper, we are able to guarantee 210 ** that the "nBusy" integer parameter passed to the users busy-handler 211 ** represents the total number of busy-handler invocations made within 212 ** this call to sqlite3demo_superlock(), including any made during the 213 ** "BEGIN EXCLUSIVE". 214 */ 215 if( rc==SQLITE_OK ){ 216 busy.xBusy = xBusy; 217 busy.pBusyArg = pBusyArg; 218 sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy); 219 rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0); 220 } 221 222 /* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL 223 ** database, call superlockWalLock() to obtain the extra locks required 224 ** to prevent readers, writers and/or checkpointers from accessing the 225 ** db while this process is holding the superlock. 226 ** 227 ** Before attempting any WAL locks, commit the transaction started above 228 ** to drop the WAL read and write locks currently held. Otherwise, the 229 ** new WAL locks may conflict with the old. 230 */ 231 if( rc==SQLITE_OK ){ 232 if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){ 233 rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0); 234 if( rc==SQLITE_OK ){ 235 rc = superlockWalLock(pLock->db, &busy); 236 } 237 } 238 } 239 240 if( rc!=SQLITE_OK ){ 241 sqlite3demo_superunlock(pLock); 242 *ppLock = 0; 243 }else{ 244 *ppLock = pLock; 245 } 246 247 return rc; 248 } 249 250 /* 251 ** End of example code. Everything below here is the test harness. 252 ************************************************************************** 253 ************************************************************************** 254 *************************************************************************/ 255 256 257 #ifdef SQLITE_TEST 258 259 #if defined(INCLUDE_SQLITE_TCL_H) 260 # include "sqlite_tcl.h" 261 #else 262 # include "tcl.h" 263 # ifndef SQLITE_TCLAPI 264 # define SQLITE_TCLAPI 265 # endif 266 #endif 267 268 struct InterpAndScript { 269 Tcl_Interp *interp; 270 Tcl_Obj *pScript; 271 }; 272 typedef struct InterpAndScript InterpAndScript; 273 274 static void SQLITE_TCLAPI superunlock_del(ClientData cd){ 275 sqlite3demo_superunlock((void *)cd); 276 } 277 278 static int SQLITE_TCLAPI superunlock_cmd( 279 ClientData cd, 280 Tcl_Interp *interp, 281 int objc, 282 Tcl_Obj *CONST objv[] 283 ){ 284 if( objc!=1 ){ 285 Tcl_WrongNumArgs(interp, 1, objv, ""); 286 return TCL_ERROR; 287 } 288 Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); 289 return TCL_OK; 290 } 291 292 static int superlock_busy(void *pCtx, int nBusy){ 293 InterpAndScript *p = (InterpAndScript *)pCtx; 294 Tcl_Obj *pEval; /* Script to evaluate */ 295 int iVal = 0; /* Value to return */ 296 297 pEval = Tcl_DuplicateObj(p->pScript); 298 Tcl_IncrRefCount(pEval); 299 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy)); 300 Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); 301 Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal); 302 Tcl_DecrRefCount(pEval); 303 304 return iVal; 305 } 306 307 /* 308 ** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT 309 */ 310 static int SQLITE_TCLAPI superlock_cmd( 311 ClientData cd, 312 Tcl_Interp *interp, 313 int objc, 314 Tcl_Obj *CONST objv[] 315 ){ 316 void *pLock; /* Lock context */ 317 char *zPath; 318 char *zVfs = 0; 319 InterpAndScript busy = {0, 0}; 320 int (*xBusy)(void*,int) = 0; /* Busy handler callback */ 321 int rc; /* Return code from sqlite3demo_superlock() */ 322 323 if( objc<3 || objc>5 ){ 324 Tcl_WrongNumArgs( 325 interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?"); 326 return TCL_ERROR; 327 } 328 329 zPath = Tcl_GetString(objv[2]); 330 331 if( objc>3 ){ 332 zVfs = Tcl_GetString(objv[3]); 333 if( strlen(zVfs)==0 ) zVfs = 0; 334 } 335 if( objc>4 ){ 336 busy.interp = interp; 337 busy.pScript = objv[4]; 338 xBusy = superlock_busy; 339 } 340 341 rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock); 342 assert( rc==SQLITE_OK || pLock==0 ); 343 assert( rc!=SQLITE_OK || pLock!=0 ); 344 345 if( rc!=SQLITE_OK ){ 346 extern const char *sqlite3ErrStr(int); 347 Tcl_ResetResult(interp); 348 Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0); 349 return TCL_ERROR; 350 } 351 352 Tcl_CreateObjCommand( 353 interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del 354 ); 355 Tcl_SetObjResult(interp, objv[1]); 356 return TCL_OK; 357 } 358 359 int SqliteSuperlock_Init(Tcl_Interp *interp){ 360 Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0); 361 return TCL_OK; 362 } 363 #endif 364