test_superlock.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. #include <sqlite3.h>
  21. #include <string.h> /* memset(), strlen() */
  22. #include <assert.h> /* assert() */
  23. /*
  24. ** A structure to collect a busy-handler callback and argument and a count
  25. ** of the number of times it has been invoked.
  26. */
  27. struct SuperlockBusy {
  28. int (*xBusy)(void*,int); /* Pointer to busy-handler function */
  29. void *pBusyArg; /* First arg to pass to xBusy */
  30. int nBusy; /* Number of times xBusy has been invoked */
  31. };
  32. typedef struct SuperlockBusy SuperlockBusy;
  33. /*
  34. ** An instance of the following structure is allocated for each active
  35. ** superlock. The opaque handle returned by sqlite3demo_superlock() is
  36. ** actually a pointer to an instance of this structure.
  37. */
  38. struct Superlock {
  39. sqlite3 *db; /* Database handle used to lock db */
  40. int bWal; /* True if db is a WAL database */
  41. };
  42. typedef struct Superlock Superlock;
  43. /*
  44. ** The pCtx pointer passed to this function is actually a pointer to a
  45. ** SuperlockBusy structure. Invoke the busy-handler function encapsulated
  46. ** by the structure and return the result.
  47. */
  48. static int superlockBusyHandler(void *pCtx, int UNUSED){
  49. SuperlockBusy *pBusy = (SuperlockBusy *)pCtx;
  50. if( pBusy->xBusy==0 ) return 0;
  51. return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++);
  52. }
  53. /*
  54. ** This function is used to determine if the main database file for
  55. ** connection db is open in WAL mode or not. If no error occurs and the
  56. ** database file is in WAL mode, set *pbWal to true and return SQLITE_OK.
  57. ** If it is not in WAL mode, set *pbWal to false.
  58. **
  59. ** If an error occurs, return an SQLite error code. The value of *pbWal
  60. ** is undefined in this case.
  61. */
  62. static int superlockIsWal(Superlock *pLock){
  63. int rc; /* Return Code */
  64. sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */
  65. rc = sqlite3_prepare(pLock->db, "PRAGMA main.journal_mode", -1, &pStmt, 0);
  66. if( rc!=SQLITE_OK ) return rc;
  67. pLock->bWal = 0;
  68. if( SQLITE_ROW==sqlite3_step(pStmt) ){
  69. const char *zMode = (const char *)sqlite3_column_text(pStmt, 0);
  70. if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){
  71. pLock->bWal = 1;
  72. }
  73. }
  74. return sqlite3_finalize(pStmt);
  75. }
  76. /*
  77. ** Obtain an exclusive shm-lock on nByte bytes starting at offset idx
  78. ** of the file fd. If the lock cannot be obtained immediately, invoke
  79. ** the busy-handler until either it is obtained or the busy-handler
  80. ** callback returns 0.
  81. */
  82. static int superlockShmLock(
  83. sqlite3_file *fd, /* Database file handle */
  84. int idx, /* Offset of shm-lock to obtain */
  85. int nByte, /* Number of consective bytes to lock */
  86. SuperlockBusy *pBusy /* Busy-handler wrapper object */
  87. ){
  88. int rc;
  89. int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock;
  90. do {
  91. rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE);
  92. }while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) );
  93. return rc;
  94. }
  95. /*
  96. ** Obtain the extra locks on the database file required for WAL databases.
  97. ** Invoke the supplied busy-handler as required.
  98. */
  99. static int superlockWalLock(
  100. sqlite3 *db, /* Database handle open on WAL database */
  101. SuperlockBusy *pBusy /* Busy handler wrapper object */
  102. ){
  103. int rc; /* Return code */
  104. sqlite3_file *fd = 0; /* Main database file handle */
  105. void volatile *p = 0; /* Pointer to first page of shared memory */
  106. /* Obtain a pointer to the sqlite3_file object open on the main db file. */
  107. rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
  108. if( rc!=SQLITE_OK ) return rc;
  109. /* Obtain the "recovery" lock. Normally, this lock is only obtained by
  110. ** clients running database recovery.
  111. */
  112. rc = superlockShmLock(fd, 2, 1, pBusy);
  113. if( rc!=SQLITE_OK ) return rc;
  114. /* Zero the start of the first shared-memory page. This means that any
  115. ** clients that open read or write transactions from this point on will
  116. ** have to run recovery before proceeding. Since they need the "recovery"
  117. ** lock that this process is holding to do that, no new read or write
  118. ** transactions may now be opened. Nor can a checkpoint be run, for the
  119. ** same reason.
  120. */
  121. rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p);
  122. if( rc!=SQLITE_OK ) return rc;
  123. memset((void *)p, 0, 32);
  124. /* Obtain exclusive locks on all the "read-lock" slots. Once these locks
  125. ** are held, it is guaranteed that there are no active reader, writer or
  126. ** checkpointer clients.
  127. */
  128. rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
  129. return rc;
  130. }
  131. /*
  132. ** Release a superlock held on a database file. The argument passed to
  133. ** this function must have been obtained from a successful call to
  134. ** sqlite3demo_superlock().
  135. */
  136. void sqlite3demo_superunlock(void *pLock){
  137. Superlock *p = (Superlock *)pLock;
  138. if( p->bWal ){
  139. int rc; /* Return code */
  140. int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
  141. sqlite3_file *fd = 0;
  142. rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
  143. if( rc==SQLITE_OK ){
  144. fd->pMethods->xShmLock(fd, 2, 1, flags);
  145. fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
  146. }
  147. }
  148. sqlite3_close(p->db);
  149. sqlite3_free(p);
  150. }
  151. /*
  152. ** Obtain a superlock on the database file identified by zPath, using the
  153. ** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is
  154. ** returned and output variable *ppLock is populated with an opaque handle
  155. ** that may be used with sqlite3demo_superunlock() to release the lock.
  156. **
  157. ** If an error occurs, *ppLock is set to 0 and an SQLite error code
  158. ** (e.g. SQLITE_BUSY) is returned.
  159. **
  160. ** If a required lock cannot be obtained immediately and the xBusy parameter
  161. ** to this function is not NULL, then xBusy is invoked in the same way
  162. ** as a busy-handler registered with SQLite (using sqlite3_busy_handler())
  163. ** until either the lock can be obtained or the busy-handler function returns
  164. ** 0 (indicating "give up").
  165. */
  166. int sqlite3demo_superlock(
  167. const char *zPath, /* Path to database file to lock */
  168. const char *zVfs, /* VFS to use to access database file */
  169. int (*xBusy)(void*,int), /* Busy handler callback */
  170. void *pBusyArg, /* Context arg for busy handler */
  171. void **ppLock /* OUT: Context to pass to superunlock() */
  172. ){
  173. SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */
  174. int rc; /* Return code */
  175. Superlock *pLock;
  176. pLock = sqlite3_malloc(sizeof(Superlock));
  177. if( !pLock ) return SQLITE_NOMEM;
  178. memset(pLock, 0, sizeof(Superlock));
  179. /* Open a database handle on the file to superlock. */
  180. rc = sqlite3_open_v2(
  181. zPath, &pLock->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs
  182. );
  183. /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not
  184. ** a WAL database, this is all we need to do.
  185. **
  186. ** A wrapper function is used to invoke the busy-handler instead of
  187. ** registering the busy-handler function supplied by the user directly
  188. ** with SQLite. This is because the same busy-handler function may be
  189. ** invoked directly later on when attempting to obtain the extra locks
  190. ** required in WAL mode. By using the wrapper, we are able to guarantee
  191. ** that the "nBusy" integer parameter passed to the users busy-handler
  192. ** represents the total number of busy-handler invocations made within
  193. ** this call to sqlite3demo_superlock(), including any made during the
  194. ** "BEGIN EXCLUSIVE".
  195. */
  196. if( rc==SQLITE_OK ){
  197. busy.xBusy = xBusy;
  198. busy.pBusyArg = pBusyArg;
  199. sqlite3_busy_handler(pLock->db, superlockBusyHandler, (void *)&busy);
  200. rc = sqlite3_exec(pLock->db, "BEGIN EXCLUSIVE", 0, 0, 0);
  201. }
  202. /* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL
  203. ** database, call superlockWalLock() to obtain the extra locks required
  204. ** to prevent readers, writers and/or checkpointers from accessing the
  205. ** db while this process is holding the superlock.
  206. **
  207. ** Before attempting any WAL locks, commit the transaction started above
  208. ** to drop the WAL read and write locks currently held. Otherwise, the
  209. ** new WAL locks may conflict with the old.
  210. */
  211. if( rc==SQLITE_OK ){
  212. if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
  213. rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
  214. if( rc==SQLITE_OK ){
  215. rc = superlockWalLock(pLock->db, &busy);
  216. }
  217. }
  218. }
  219. if( rc!=SQLITE_OK ){
  220. sqlite3demo_superunlock(pLock);
  221. *ppLock = 0;
  222. }else{
  223. *ppLock = pLock;
  224. }
  225. return rc;
  226. }
  227. /*
  228. ** End of example code. Everything below here is the test harness.
  229. **************************************************************************
  230. **************************************************************************
  231. *************************************************************************/
  232. #ifdef SQLITE_TEST
  233. #include <tcl.h>
  234. struct InterpAndScript {
  235. Tcl_Interp *interp;
  236. Tcl_Obj *pScript;
  237. };
  238. typedef struct InterpAndScript InterpAndScript;
  239. static void superunlock_del(ClientData cd){
  240. sqlite3demo_superunlock((void *)cd);
  241. }
  242. static int superunlock_cmd(
  243. ClientData cd,
  244. Tcl_Interp *interp,
  245. int objc,
  246. Tcl_Obj *CONST objv[]
  247. ){
  248. if( objc!=1 ){
  249. Tcl_WrongNumArgs(interp, 1, objv, "");
  250. return TCL_ERROR;
  251. }
  252. Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
  253. return TCL_OK;
  254. }
  255. static int superlock_busy(void *pCtx, int nBusy){
  256. InterpAndScript *p = (InterpAndScript *)pCtx;
  257. Tcl_Obj *pEval; /* Script to evaluate */
  258. int iVal = 0; /* Value to return */
  259. pEval = Tcl_DuplicateObj(p->pScript);
  260. Tcl_IncrRefCount(pEval);
  261. Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy));
  262. Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
  263. Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal);
  264. Tcl_DecrRefCount(pEval);
  265. return iVal;
  266. }
  267. /*
  268. ** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT
  269. */
  270. static int superlock_cmd(
  271. ClientData cd,
  272. Tcl_Interp *interp,
  273. int objc,
  274. Tcl_Obj *CONST objv[]
  275. ){
  276. void *pLock; /* Lock context */
  277. char *zPath;
  278. char *zVfs = 0;
  279. InterpAndScript busy = {0, 0};
  280. int (*xBusy)(void*,int) = 0; /* Busy handler callback */
  281. int rc; /* Return code from sqlite3demo_superlock() */
  282. if( objc<3 || objc>5 ){
  283. Tcl_WrongNumArgs(
  284. interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?");
  285. return TCL_ERROR;
  286. }
  287. zPath = Tcl_GetString(objv[2]);
  288. if( objc>3 ){
  289. zVfs = Tcl_GetString(objv[3]);
  290. if( strlen(zVfs)==0 ) zVfs = 0;
  291. }
  292. if( objc>4 ){
  293. busy.interp = interp;
  294. busy.pScript = objv[4];
  295. xBusy = superlock_busy;
  296. }
  297. rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock);
  298. assert( rc==SQLITE_OK || pLock==0 );
  299. assert( rc!=SQLITE_OK || pLock!=0 );
  300. if( rc!=SQLITE_OK ){
  301. extern const char *sqlite3ErrStr(int);
  302. Tcl_ResetResult(interp);
  303. Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0);
  304. return TCL_ERROR;
  305. }
  306. Tcl_CreateObjCommand(
  307. interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del
  308. );
  309. Tcl_SetObjResult(interp, objv[1]);
  310. return TCL_OK;
  311. }
  312. int SqliteSuperlock_Init(Tcl_Interp *interp){
  313. Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0);
  314. return TCL_OK;
  315. }
  316. #endif