Annotation of embedaddon/sqlite3/src/test_superlock.c, revision 1.1.1.1

1.1       misho       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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>