Home | History | Annotate | Download | only in src
      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 #include <tcl.h>
    260 
    261 struct InterpAndScript {
    262   Tcl_Interp *interp;
    263   Tcl_Obj *pScript;
    264 };
    265 typedef struct InterpAndScript InterpAndScript;
    266 
    267 static void superunlock_del(ClientData cd){
    268   sqlite3demo_superunlock((void *)cd);
    269 }
    270 
    271 static int superunlock_cmd(
    272   ClientData cd,
    273   Tcl_Interp *interp,
    274   int objc,
    275   Tcl_Obj *CONST objv[]
    276 ){
    277   if( objc!=1 ){
    278     Tcl_WrongNumArgs(interp, 1, objv, "");
    279     return TCL_ERROR;
    280   }
    281   Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
    282   return TCL_OK;
    283 }
    284 
    285 static int superlock_busy(void *pCtx, int nBusy){
    286   InterpAndScript *p = (InterpAndScript *)pCtx;
    287   Tcl_Obj *pEval;                 /* Script to evaluate */
    288   int iVal = 0;                   /* Value to return */
    289 
    290   pEval = Tcl_DuplicateObj(p->pScript);
    291   Tcl_IncrRefCount(pEval);
    292   Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
    293   Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
    294   Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
    295   Tcl_DecrRefCount(pEval);
    296 
    297   return iVal;
    298 }
    299 
    300 /*
    301 ** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
    302 */
    303 static int superlock_cmd(
    304   ClientData cd,
    305   Tcl_Interp *interp,
    306   int objc,
    307   Tcl_Obj *CONST objv[]
    308 ){
    309   void *pLock;                    /* Lock context */
    310   char *zPath;
    311   char *zVfs = 0;
    312   InterpAndScript busy = {0, 0};
    313   int (*xBusy)(void*,int) = 0;    /* Busy handler callback */
    314   int rc;                         /* Return code from sqlite3demo_superlock() */
    315 
    316   if( objc<3 || objc>5 ){
    317     Tcl_WrongNumArgs(
    318         interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
    319     return TCL_ERROR;
    320   }
    321 
    322   zPath = Tcl_GetString(objv[2]);
    323 
    324   if( objc>3 ){
    325     zVfs = Tcl_GetString(objv[3]);
    326     if( strlen(zVfs)==0 ) zVfs = 0;
    327   }
    328   if( objc>4 ){
    329     busy.interp = interp;
    330     busy.pScript = objv[4];
    331     xBusy = superlock_busy;
    332   }
    333 
    334   rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
    335   assert( rc==SQLITE_OK || pLock==0 );
    336   assert( rc!=SQLITE_OK || pLock!=0 );
    337 
    338   if( rc!=SQLITE_OK ){
    339     extern const char *sqlite3ErrStr(int);
    340     Tcl_ResetResult(interp);
    341     Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
    342     return TCL_ERROR;
    343   }
    344 
    345   Tcl_CreateObjCommand(
    346       interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
    347   );
    348   Tcl_SetObjResult(interp, objv[1]);
    349   return TCL_OK;
    350 }
    351 
    352 int SqliteSuperlock_Init(Tcl_Interp *interp){
    353   Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
    354   return TCL_OK;
    355 }
    356 #endif
    357