| Index: third_party/sqlite/src/src/test_superlock.c | 
| diff --git a/third_party/sqlite/src/src/test_superlock.c b/third_party/sqlite/src/src/test_superlock.c | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..936fcad0c56f6bc85809742f6ae1c1eeb8b7d2f1 | 
| --- /dev/null | 
| +++ b/third_party/sqlite/src/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 | 
|  |