| Index: third_party/sqlite/sqlite-src-3070603/src/test_superlock.c
|
| diff --git a/third_party/sqlite/sqlite-src-3070603/src/test_superlock.c b/third_party/sqlite/sqlite-src-3070603/src/test_superlock.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..936fcad0c56f6bc85809742f6ae1c1eeb8b7d2f1
|
| --- /dev/null
|
| +++ b/third_party/sqlite/sqlite-src-3070603/src/test_superlock.c
|
| @@ -0,0 +1,356 @@
|
| +/*
|
| +** 2010 November 19
|
| +**
|
| +** The author disclaims copyright to this source code. In place of
|
| +** a legal notice, here is a blessing:
|
| +**
|
| +** May you do good and not evil.
|
| +** May you find forgiveness for yourself and forgive others.
|
| +** May you share freely, never taking more than you give.
|
| +**
|
| +*************************************************************************
|
| +** Example code for obtaining an exclusive lock on an SQLite database
|
| +** file. This method is complicated, but works for both WAL and rollback
|
| +** mode database files. The interface to the example code in this file
|
| +** consists of the following two functions:
|
| +**
|
| +** sqlite3demo_superlock()
|
| +** sqlite3demo_superunlock()
|
| +*/
|
| +
|
| +#include <sqlite3.h>
|
| +#include <string.h> /* memset(), strlen() */
|
| +#include <assert.h> /* assert() */
|
| +
|
| +/*
|
| +** A structure to collect a busy-handler callback and argument and a count
|
| +** of the number of times it has been invoked.
|
| +*/
|
| +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);
|
| +}
|
| +
|
| +/*
|
| +** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
|
| +** of the file fd. If the lock cannot be obtained immediately, invoke
|
| +** the busy-handler until either it is obtained or the busy-handler
|
| +** callback returns 0.
|
| +*/
|
| +static int superlockShmLock(
|
| + sqlite3_file *fd, /* Database file handle */
|
| + int idx, /* Offset of shm-lock to obtain */
|
| + int nByte, /* Number of consective bytes to lock */
|
| + SuperlockBusy *pBusy /* Busy-handler wrapper object */
|
| +){
|
| + int rc;
|
| + int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
|
| + do {
|
| + rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
|
| + }while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
|
| + return rc;
|
| +}
|
| +
|
| +/*
|
| +** Obtain the extra locks on the database file required for WAL databases.
|
| +** Invoke the supplied busy-handler as required.
|
| +*/
|
| +static int superlockWalLock(
|
| + sqlite3 *db, /* Database handle open on WAL database */
|
| + SuperlockBusy *pBusy /* Busy handler wrapper object */
|
| +){
|
| + int rc; /* Return code */
|
| + sqlite3_file *fd = 0; /* Main database file handle */
|
| + void volatile *p = 0; /* Pointer to first page of shared memory */
|
| +
|
| + /* Obtain a pointer to the sqlite3_file object open on the main db file. */
|
| + rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| +
|
| + /* Obtain the "recovery" lock. Normally, this lock is only obtained by
|
| + ** clients running database recovery.
|
| + */
|
| + rc = superlockShmLock(fd, 2, 1, pBusy);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| +
|
| + /* Zero the start of the first shared-memory page. This means that any
|
| + ** clients that open read or write transactions from this point on will
|
| + ** have to run recovery before proceeding. Since they need the "recovery"
|
| + ** lock that this process is holding to do that, no new read or write
|
| + ** transactions may now be opened. Nor can a checkpoint be run, for the
|
| + ** same reason.
|
| + */
|
| + rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
|
| + if( rc!=SQLITE_OK ) return rc;
|
| + memset((void *)p, 0, 32);
|
| +
|
| + /* 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 rc; /* Return code */
|
| + int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
|
| + sqlite3_file *fd = 0;
|
| + rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
|
| + if( rc==SQLITE_OK ){
|
| + fd->pMethods->xShmLock(fd, 2, 1, flags);
|
| + fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, 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.
|
| +**
|
| +** If an error occurs, *ppLock is set to 0 and an SQLite error code
|
| +** (e.g. SQLITE_BUSY) is returned.
|
| +**
|
| +** If a required lock cannot be obtained immediately and the xBusy parameter
|
| +** to this function is not NULL, then xBusy is invoked in the same way
|
| +** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
|
| +** until either the lock can be obtained or the busy-handler function returns
|
| +** 0 (indicating "give up").
|
| +*/
|
| +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.
|
| +**************************************************************************
|
| +**************************************************************************
|
| +*************************************************************************/
|
| +
|
| +
|
| +#ifdef SQLITE_TEST
|
| +
|
| +#include <tcl.h>
|
| +
|
| +struct InterpAndScript {
|
| + Tcl_Interp *interp;
|
| + Tcl_Obj *pScript;
|
| +};
|
| +typedef struct InterpAndScript InterpAndScript;
|
| +
|
| +static void superunlock_del(ClientData cd){
|
| + sqlite3demo_superunlock((void *)cd);
|
| +}
|
| +
|
| +static int superunlock_cmd(
|
| + ClientData cd,
|
| + Tcl_Interp *interp,
|
| + int objc,
|
| + Tcl_Obj *CONST objv[]
|
| +){
|
| + if( objc!=1 ){
|
| + Tcl_WrongNumArgs(interp, 1, objv, "");
|
| + return TCL_ERROR;
|
| + }
|
| + Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
| + return TCL_OK;
|
| +}
|
| +
|
| +static int superlock_busy(void *pCtx, int nBusy){
|
| + InterpAndScript *p = (InterpAndScript *)pCtx;
|
| + Tcl_Obj *pEval; /* Script to evaluate */
|
| + int iVal = 0; /* Value to return */
|
| +
|
| + pEval = Tcl_DuplicateObj(p->pScript);
|
| + Tcl_IncrRefCount(pEval);
|
| + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
|
| + Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
|
| + Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
|
| + Tcl_DecrRefCount(pEval);
|
| +
|
| + return iVal;
|
| +}
|
| +
|
| +/*
|
| +** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
|
| +*/
|
| +static int superlock_cmd(
|
| + ClientData cd,
|
| + Tcl_Interp *interp,
|
| + int objc,
|
| + Tcl_Obj *CONST objv[]
|
| +){
|
| + void *pLock; /* Lock context */
|
| + char *zPath;
|
| + char *zVfs = 0;
|
| + InterpAndScript busy = {0, 0};
|
| + int (*xBusy)(void*,int) = 0; /* Busy handler callback */
|
| + int rc; /* Return code from sqlite3demo_superlock() */
|
| +
|
| + if( objc<3 || objc>5 ){
|
| + Tcl_WrongNumArgs(
|
| + interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
|
| + return TCL_ERROR;
|
| + }
|
| +
|
| + zPath = Tcl_GetString(objv[2]);
|
| +
|
| + if( objc>3 ){
|
| + zVfs = Tcl_GetString(objv[3]);
|
| + if( strlen(zVfs)==0 ) zVfs = 0;
|
| + }
|
| + if( objc>4 ){
|
| + busy.interp = interp;
|
| + busy.pScript = objv[4];
|
| + xBusy = superlock_busy;
|
| + }
|
| +
|
| + rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
|
| + assert( rc==SQLITE_OK || pLock==0 );
|
| + assert( rc!=SQLITE_OK || pLock!=0 );
|
| +
|
| + if( rc!=SQLITE_OK ){
|
| + extern const char *sqlite3ErrStr(int);
|
| + Tcl_ResetResult(interp);
|
| + Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
|
| + return TCL_ERROR;
|
| + }
|
| +
|
| + Tcl_CreateObjCommand(
|
| + interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
|
| + );
|
| + Tcl_SetObjResult(interp, objv[1]);
|
| + return TCL_OK;
|
| +}
|
| +
|
| +int SqliteSuperlock_Init(Tcl_Interp *interp){
|
| + Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
|
| + return TCL_OK;
|
| +}
|
| +#endif
|
|
|