Annotation of embedaddon/sqlite3/src/test_superlock.c, revision 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>