]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance this branch to support page-level-locking (without MVCC) for
authordan <dan@noemail.net>
Wed, 16 Aug 2017 16:52:14 +0000 (16:52 +0000)
committerdan <dan@noemail.net>
Wed, 16 Aug 2017 16:52:14 +0000 (16:52 +0000)
multi-process deployments.

FossilOrigin-Name: 04e0cb571dbed00e269a890a755e252d7e8204d6d2ed5a7cfdb3d78d990a2876

manifest
manifest.uuid
src/os_unix.c
src/pager.c
src/server.c
src/server.h
src/sqlite.h.in
src/wal.h
test/server2.test
test/server3.test
test/server_common.tcl

index 4e44e1a82de7acfd4d980844128445e8f629b0fd..98bbf4396664e9b612e9d5581555cd9e5ce2e5ab 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\scode\sfor\sPLL\ssupport\sin\swal\smode\sfrom\sthis\sbranch.
-D 2017-08-14T07:16:18.805
+C Enhance\sthis\sbranch\sto\ssupport\spage-level-locking\s(without\sMVCC)\sfor\nmulti-process\sdeployments.
+D 2017-08-16T16:52:14.156
 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016
@@ -439,10 +439,10 @@ F src/os.c add02933b1dce7a39a005b00a2f5364b763e9a24
 F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343
 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
-F src/os_unix.c 24e4b7f6f89b742357068959255c4530014254579c09c1b56beff1014eb0c0c1
+F src/os_unix.c 6947c0fccc1b68404def7161553881b2efab6fcac4a58bf9f4ce36a4ad27d325
 F src/os_win.c 964165b66cde03abc72fe948198b01be608436894732eadb94c8720d2467f223
 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
-F src/pager.c 0cfac2557de08ac12ec8d2edd3c4cb69f96ad49b923f1390619c310aa684366e
+F src/pager.c e53f35d61f266c47cc3883e34c7d01eaca38a71451dd72efb3cf21e043b471a7
 F src/pager.h 316dac0671fd7555af9e73d4357febd5f2d3ce6a185ffd8d77b7fc0423ac8b1a
 F src/parse.y 58a2de13e855aece3d7709440e6e86849f4cde97f5227c6a25e6bba2fc5e2976
 F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870
@@ -456,11 +456,11 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c 4324a94573b1e29286f8121e4881db59eaedc014afeb274c8d3e07ed282e0e20
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
 F src/select.c ea8921065512650a9df3f5bf9a9b531c6ef4fb193c0d57e09d7a479ef9b13992
-F src/server.c d69efa7284074e69d43c38d5d649ef3b20add1db31c9d8e7f01cabd95d0ee72b
-F src/server.h d7555e68ee34008ca02705e44f861938a67b85a97ffe64d9326e03bd15dcd06b
+F src/server.c e8d1c1a0e39d508c75e11eafa9a20edc85f5b619b27f7218950f3a77431f5a71
+F src/server.h cf1ede28aaa07a30550228582f211327b5ebe5517d2334e35ec09d00fd6d230d
 F src/shell.c bd6a37cbe8bf64ef6a6a74fdc50f067d3148149b4ce2b4d03154663e66ded55f
 F src/shell.c.in b5725acacba95ccefa57b6d068f710e29ba8239c3aa704628a1902a1f729c175
-F src/sqlite.h.in 0ebbd0299e4bd03841c0e24ea86c74a42082b20533b86b0f360424a3b3c145a5
+F src/sqlite.h.in 9dacf9a0959630bf5d286c0fbdaf2bb4bf408a9477f6d61db8edab3a859714d7
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h a1fd3aa82f967da436164e0728a7d6841651fd0c6e27b9044e0eb9f6c8462e47
 F src/sqliteInt.h eaa2c5e52a29da629bd50521a6b67cce7f952128a95ba4f20aac62c78d41b04d
@@ -537,7 +537,7 @@ F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
 F src/vtab.c f1d5c23132fb0247af3e86146404112283ddedb6c518de0d4edc91cfb36970ef
 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
-F src/wal.h 739d92494eb18b6d8f3e353e66c10eb8f94534bafd336ece9f3f60235317ea08
+F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
 F src/walker.c a7ca64ce08a83a20d32186fbe06bca9234e348cfcf07959ee322fdc3e8a6173a
 F src/where.c cbe8ddffbcec7ce86f7a800fe8fd10aee412c76c87e0dd3732a1682e68d74cd9
 F src/whereInt.h 93bb90b77d39901eda31b44d8e90da1351193ccfe96876f89b58a93a33b84c3d
@@ -1171,11 +1171,11 @@ F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
 F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
 F test/selectG.test e8600e379589e85e9fefd2fe4d44a4cdd63f6982
 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
-F test/server2.test 38a33c71963d72aad64c6f4e89ed8f7de129b3b5bf75c5785e363dee8deabd35
-F test/server3.test ba6428594bbe32eafa015782a039dcacbf0f42db967e4093e4d2f6f74178a02d
+F test/server2.test e7890fb1eb9a11a0f94cd0892279e0f3cd1ba8c3006fa343637ee9ff3c4689f6
+F test/server3.test c3ae4ca7a6e7df870bfcd2450a9815507eaa80b9cdc44ee6c7975d48311505d4
 F test/server4.test 97040670597948a695b1973537d770417589f1998bcbb3959302aaee3c211250
 F test/server5.test 2e554001145170094a19731a8ce2981d040cf44c947542b35d130e6e31256fca
-F test/server_common.tcl 6e0e0348a87381770b548fcf8faf2d8c7b910120b08a1bfb565546f77293fa81
+F test/server_common.tcl 551923cf8d51fefcdb4444bfd72b88ca5c5228fe1525da5b6528ae4edb7a2f2e
 F test/servercrash.test 816c132b26af008067cab2913783f67006d4003e3988f3f3ee1075742f6e0a6c
 F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be
 F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
@@ -1657,7 +1657,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P bc2498d60fa22b587ad463ec697abe82d1c87abb7fd0d2cb60cc7316cfd7cec7
-R 97b8be53027d87f1df1370ba323e39a9
+P 8e1b28ed3e83eba0b2113316a40ed1165e0e051220b68863cb70734c95a82c2a
+R 0a18526bebbb88ed2fe197ff12475de8
 U dan
-Z cc211de634879a22eac42d92c4a3bb35
+Z 121a6646ee66bffdec0d5750ccf213ab
index a6a976c08a02c8fa499db29e9e407f42960779ad..521845bf4e43952078af4c7e5b2ef8472c4ce8e4 100644 (file)
@@ -1 +1 @@
-8e1b28ed3e83eba0b2113316a40ed1165e0e051220b68863cb70734c95a82c2a
\ No newline at end of file
+04e0cb571dbed00e269a890a755e252d7e8204d6d2ed5a7cfdb3d78d990a2876
\ No newline at end of file
index f7cbd7e299ebf007587283625d98ccf1b1a8b573..00873ba12111cc4e1ab9f19f86120e502798276d 100644 (file)
@@ -3787,38 +3787,247 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
 /* Forward declaration */
 static int unixGetTempname(int nBuf, char *zBuf);
 
+#ifdef SQLITE_SERVER_EDITION
+
 /*
-** Information and control of an open file handle.
+** Structure passed by SQLite through the (void*) argument to various 
+** fcntl operations.
 */
-static int unixFileControl(sqlite3_file *id, int op, void *pArg){
-  unixFile *pFile = (unixFile*)id;
-  switch( op ){
-    case SQLITE_FCNTL_SERVER_MODE: {
-      int rc = SQLITE_OK;
-      int eServer = 0;
-      if( pFile->ctrlFlags | UNIXFILE_EXCL ){
-        char *zJrnl = sqlite3_mprintf("%s-journal", pFile->zPath);
-        if( zJrnl==0 ){
-          rc = SQLITE_NOMEM;
-        }else{
-          struct stat buf;        /* Used to hold return values of stat() */
-          if( osStat(zJrnl, &buf) ){
-            rc = SQLITE_IOERR_FSTAT;
-          }else{
-            eServer = ((buf.st_mode & S_IFDIR) ? 1 : 0);
-          }
+struct UnixServerArg {
+  void *h;                        /* Handle from SHMOPEN */
+  void *p;                        /* Mapping */
+  int i1;                         /* Integer value 1 */
+  int i2;                         /* Integer value 2 */
+};
+typedef struct UnixServerArg UnixServerArg;
+
+/*
+** Structure used as a server-shm handle.
+*/
+struct UnixServerShm {
+  void *pMap;                     /* Pointer to mapping */
+  int nMap;                       /* Size of mapping in bytes */
+  int fd;                         /* File descriptor open on *-hma file */
+};
+typedef struct UnixServerShm UnixServerShm;
+
+/*
+** Implementation of SQLITE_FCNTL_FILEID
+*/
+static int unixFcntlServerFileid(unixFile *pFile, void *pArg){
+  i64 *aId = (i64*)pArg;
+  aId[0] = (i64)(pFile->pInode->fileId.dev);
+  aId[1] = (i64)(pFile->pInode->fileId.ino);
+  return SQLITE_OK;
+}
+
+/*
+** Implementation of SQLITE_FCNTL_SERVER_MODE
+*/
+static int unixFcntlServerMode(unixFile *pFile, void *pArg){
+  int rc = SQLITE_OK;
+  int eServer = 0;
+  char *zJrnl = sqlite3_mprintf("%s-journal", pFile->zPath);
+  if( zJrnl==0 ){
+    rc = SQLITE_NOMEM;
+  }else{
+    struct stat buf;        /* Used to hold return values of stat() */
+    if( osStat(zJrnl, &buf) ){
+      rc = SQLITE_IOERR_FSTAT;
+    }else if( buf.st_mode & S_IFDIR ){
+      eServer = (pFile->ctrlFlags & UNIXFILE_EXCL) ? 1 : 2;
+    }
+  }
+  sqlite3_free(zJrnl);
+  *((int*)pArg) = eServer;
+  return rc;
+}
+
+/*
+** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN.
+**
+** The (void*) argument passed to this file control should actually be
+** a pointer to a UnixServerArg or equivalent structure. Arguments are
+** interpreted as follows:
+**
+**   UnixServerArg.h  - OUT: New server shm handle.
+**   UnixServerArg.p  - OUT: New server shm mapping.
+**   UnixServerArg.i1 - Size of requested mapping in bytes.
+**   UnixServerArg.i2 - OUT: True if journal rollback + SHMOPEN2 are required.
+*/
+static int unixFcntlServerShmopen(unixFile *pFd, void *pArg){
+  int rc = SQLITE_OK;
+  UnixServerArg *pSArg = (UnixServerArg*)pArg;
+  UnixServerShm *p;
+  char *zHma;
+
+  p = sqlite3_malloc(sizeof(UnixServerShm));
+  if( p==0 ) return SQLITE_NOMEM;
+  memset(p, 0, sizeof(UnixServerShm));
+  p->fd = -1;
+
+  zHma = sqlite3_mprintf("%s-journal/hma", pFd->zPath);
+  if( zHma==0 ){
+    rc = SQLITE_NOMEM;
+  }else{
+    p->fd = osOpen(zHma, O_RDWR|O_CREAT, 0644);
+    p->nMap = pSArg->i1;
+
+    if( p->fd<0 ){
+      rc = SQLITE_CANTOPEN;
+    }else{
+      int res = ftruncate(p->fd, p->nMap);
+      if( res!=0 ){
+        rc = SQLITE_IOERR_TRUNCATE;
+      }else{
+        p->pMap = osMmap(0, p->nMap, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd,0);
+        if( p->pMap==0 ){
+          rc = SQLITE_IOERR_MMAP;
         }
-        sqlite3_free(zJrnl);
       }
-      *((int*)pArg) = eServer;
-      return rc;
     }
-    case SQLITE_FCNTL_FILEID: {
-      i64 *aId = (i64*)pArg;
-      aId[0] = (i64)(pFile->pInode->fileId.dev);
-      aId[1] = (i64)(pFile->pInode->fileId.ino);
-      return SQLITE_OK;
+    sqlite3_free(zHma);
+  }
+
+  if( rc==SQLITE_OK ){
+    int res;
+    struct flock lock;
+    memset(&lock, 0, sizeof(struct flock));
+    lock.l_type = F_WRLCK;
+    lock.l_whence = SEEK_SET;
+    lock.l_start = p->nMap;
+    lock.l_len = 1;
+
+    res = osFcntl(p->fd, F_SETLK, &lock);
+    if( res==0 ){
+      pSArg->i2 = 1;
+      memset(p->pMap, 0, p->nMap);
+    }else{
+      pSArg->i2 = 0;
+      lock.l_type = F_RDLCK;
+      res = osFcntl(p->fd, F_SETLKW, &lock);
+      if( res!=0 ){
+        rc = SQLITE_IOERR_LOCK;
+      }
     }
+  }
+
+  if( rc!=SQLITE_OK ){
+    if( p->pMap ) osMunmap(p->pMap, p->nMap);
+    if( p->fd>=0 ) close(p->fd);
+    sqlite3_free(p);
+    pSArg->h = pSArg->p = 0;
+  }else{
+    pSArg->h = (void*)p;
+    pSArg->p = (void*)(p->pMap);
+  }
+
+  return rc;
+}
+
+/*
+** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN2.
+**
+** The (void*) argument passed to this file control should actually be
+** a pointer to a UnixServerArg or equivalent structure. Arguments are
+** interpreted as follows:
+**
+**   UnixServerArg.h  - Server shm handle (from SHMOPEN).
+**   UnixServerArg.p  - unused.
+**   UnixServerArg.i1 - unused.
+**   UnixServerArg.i2 - unused.
+*/
+static int unixFcntlServerShmopen2(unixFile *pFd, void *pArg){
+  UnixServerArg *pSArg = (UnixServerArg*)pArg;
+  UnixServerShm *p = (UnixServerShm*)pSArg->h;
+  int res;
+  struct flock lock;
+
+  memset(&lock, 0, sizeof(struct flock));
+  lock.l_type = F_RDLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = p->nMap;
+  lock.l_len = 1;
+  res = osFcntl(p->fd, F_SETLK, &lock);
+
+  return res ? SQLITE_IOERR_LOCK : SQLITE_OK;
+}
+
+/*
+** Implementation of SQLITE_FCNTL_SERVER_SHMCLOSE.
+**
+** The (void*) argument passed to this file control should actually be
+** a pointer to a UnixServerArg or equivalent structure. Arguments are
+** interpreted as follows:
+**
+**   UnixServerArg.h  - Server shm handle (from SHMOPEN).
+**   UnixServerArg.p  - unused.
+**   UnixServerArg.i1 - unused.
+**   UnixServerArg.i2 - unused.
+*/
+static int unixFcntlServerShmclose(unixFile *pFd, void *pArg){
+  UnixServerArg *pSArg = (UnixServerArg*)pArg;
+  UnixServerShm *p = (UnixServerShm*)pSArg->h;
+
+  if( p->pMap ) osMunmap(p->pMap, p->nMap);
+  if( p->fd>=0 ) close(p->fd);
+  sqlite3_free(p);
+
+  return SQLITE_OK;
+}
+
+/*
+** Implementation of SQLITE_FCNTL_SERVER_SHMLOCK.
+**
+** The (void*) argument passed to this file control should actually be
+** a pointer to a UnixServerArg or equivalent structure. Arguments are
+** interpreted as follows:
+**
+**   UnixServerArg.h  - Server shm handle (from SHMOPEN).
+**   UnixServerArg.p  - unused.
+**   UnixServerArg.i1 - slot to lock.
+**   UnixServerArg.i2 - true to take the lock, false to release it.
+*/
+static int unixFcntlServerShmlock(unixFile *pFd, void *pArg){
+  UnixServerArg *pSArg = (UnixServerArg*)pArg;
+  UnixServerShm *p = (UnixServerShm*)pSArg->h;
+  int res;
+
+  struct flock lock;
+  memset(&lock, 0, sizeof(struct flock));
+  lock.l_type = pSArg->i2 ? F_WRLCK : F_UNLCK;
+  lock.l_whence = SEEK_SET;
+  lock.l_start = p->nMap + pSArg->i1 + 1;
+  lock.l_len = 1;
+
+  res = osFcntl(p->fd, F_SETLK, &lock);
+
+  return (res==0 ? SQLITE_OK : SQLITE_BUSY);
+}
+#endif
+
+/*
+** Information and control of an open file handle.
+*/
+static int unixFileControl(sqlite3_file *id, int op, void *pArg){
+  unixFile *pFile = (unixFile*)id;
+  switch( op ){
+
+#ifdef SQLITE_SERVER_EDITION
+    case SQLITE_FCNTL_FILEID:
+      return unixFcntlServerFileid(pFile, pArg);
+    case SQLITE_FCNTL_SERVER_MODE: 
+      return unixFcntlServerMode(pFile, pArg);
+    case SQLITE_FCNTL_SERVER_SHMOPEN:
+      return unixFcntlServerShmopen(pFile, pArg);
+    case SQLITE_FCNTL_SERVER_SHMOPEN2:
+      return unixFcntlServerShmopen2(pFile, pArg);
+    case SQLITE_FCNTL_SERVER_SHMCLOSE:
+      return unixFcntlServerShmclose(pFile, pArg);
+    case SQLITE_FCNTL_SERVER_SHMLOCK:
+      return unixFcntlServerShmlock(pFile, pArg);
+#endif
+
 #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
     case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: {
       int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE);
index c45fe29c744c0ee48d9e19dfbc8f54d11a0b1e5c..00bceddcf8b79a28b79b877d5a53682e71dd5316 100644 (file)
@@ -842,6 +842,7 @@ int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
 
 #ifdef SQLITE_SERVER_EDITION
 # define pagerIsServer(x) ((x)->pServer!=0)
+# define pagerIsProcessServer(x) sqlite3ServerIsSingleProcess((x)->pServer)
 #else
 # define pagerIsServer(x) 0
 #endif
@@ -1807,6 +1808,9 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
 }
 
 #ifdef SQLITE_SERVER_EDITION
+/*
+** Free the linked list of ServerPage objects headed at Pager.pServerPage.
+*/
 static void pagerFreeServerPage(Pager *pPager){
   ServerPage *pPg;
   ServerPage *pNext;
@@ -4434,7 +4438,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
   assert( isOpen(pPager->fd) || pList->pDirty==0 );
 
 #ifdef SQLITE_SERVER_EDITION
-  if( pagerIsServer(pPager) ){
+  if( pagerIsProcessServer(pPager) ){
     rc = sqlite3ServerPreCommit(pPager->pServer, pPager->pServerPage);
     pPager->pServerPage = 0;
     if( rc!=SQLITE_OK ) return rc;
@@ -5192,12 +5196,12 @@ static int hasHotJournal(Pager *pPager, int *pExists, int *peServer){
 }
 
 #ifdef SQLITE_SERVER_EDITION
-static int pagerServerConnect(Pager *pPager){
+static int pagerServerConnect(Pager *pPager, int eServer){
   int rc = SQLITE_OK;
   if( pPager->tempFile==0 ){
     pPager->noLock = 1;
     pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
-    rc = sqlite3ServerConnect(pPager, &pPager->pServer);
+    rc = sqlite3ServerConnect(pPager, eServer, &pPager->pServer);
   }
   return rc;
 }
@@ -5453,7 +5457,7 @@ int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){
 
 #ifdef SQLITE_SERVER_EDITION
     if( eServer ){
-      rc = pagerServerConnect(pPager);
+      rc = pagerServerConnect(pPager, eServer);
     }
 #endif
 
@@ -6016,7 +6020,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
   i64 iOff = pPager->journalOff;
 
 #ifdef SQLITE_SERVER_EDITION
-  if( pagerIsServer(pPager) ){
+  if( pagerIsProcessServer(pPager) ){
     ServerPage *p = sqlite3ServerBuffer(pPager->pServer);
     if( p==0 ){
       int nByte = sizeof(ServerPage) + pPager->pageSize;
index 26193640abae4cd46343e1df13e454025428eefc..beeffa5e3a6db89810c8b926bd36db61a9934118 100644 (file)
@@ -36,7 +36,6 @@
 
 #define HMA_SLOT_RLWL_BITS (HMA_SLOT_RL_BITS + HMA_SLOT_WL_BITS)
 
-
 #define HMA_SLOT_RL_MASK ((1 << HMA_SLOT_RL_BITS)-1)
 #define HMA_SLOT_WL_MASK (((1 << HMA_SLOT_WL_BITS)-1) << HMA_SLOT_RL_BITS)
 #define HMA_SLOT_TR_MASK (((1 << HMA_SLOT_TR_BITS)-1) << HMA_SLOT_RLWL_BITS)
@@ -48,7 +47,7 @@
 /* Maximum concurrent read/write transactions */
 #define HMA_MAX_TRANSACTIONID 16
 
-
+/* Number of buckets in hash table used for MVCC in single-process mode */
 #define HMA_HASH_SIZE 512
 
 /*
 
 #define slotReaderMask(v) ((v) & HMA_SLOT_RL_MASK)
 
-#include "unistd.h"
-#include "fcntl.h"
-#include "sys/mman.h"
-#include "sys/types.h"
-#include "sys/stat.h"
-#include "errno.h"
+
+/* 
+** Atomic CAS primitive used in multi-process mode. Equivalent to:
+**
+**   int serverCompareAndSwap(u32 *ptr, u32 oldval, u32 newval){
+**     if( *ptr==oldval ){
+**       *ptr = newval;
+**       return 1;
+**     }
+**     return 0;
+**   }
+*/
+#define serverCompareAndSwap(ptr,oldval,newval) \
+  __sync_bool_compare_and_swap(ptr,oldval,newval)
+
 
 typedef struct ServerDb ServerDb;
 typedef struct ServerJournal ServerJournal;
 
 struct ServerGlobal {
-  ServerDb *pDb;                  /* Linked list of all ServerHMA objects */
+  ServerDb *pDb;                  /* Linked list of all ServerDb objects */
 };
 static struct ServerGlobal g_server;
 
@@ -103,13 +111,14 @@ struct ServerDb {
   ServerJournal aJrnl[HMA_MAX_TRANSACTIONID];
   u8 *aJrnlFdSpace;
 
+  void *pServerShm;
+
   int iNextCommit;                /* Commit id for next pre-commit call */ 
   Server *pCommit;                /* List of connections currently commiting */
   Server *pReader;                /* Connections in slower-reader transaction */
   ServerPage *pPgFirst;           /* First (oldest) in list of pages */
   ServerPage *pPgLast;            /* Last (newest) in list of pages */
-  ServerPage *apPg[HMA_HASH_SIZE];
-
+  ServerPage *apPg[HMA_HASH_SIZE];/* Hash table of "old" page data */
   ServerPage *pFree;              /* List of free page buffers */
 };
 
@@ -125,10 +134,18 @@ struct Server {
   int iCommitId;                  /* Current commit id (or 0) */
   int nAlloc;                     /* Allocated size of aLock[] array */
   int nLock;                      /* Number of entries in aLock[] */
-  u32 *aLock;                     /* Mapped lock file */
+  u32 *aLock;                     /* Array of held locks */
   Server *pNext;                  /* Next in pCommit or pReader list */
 };
 
+struct ServerFcntlArg {
+  void *h;                        /* Handle from SHMOPEN */
+  void *p;                        /* Mapping */
+  int i1;                         /* Integer value 1 */
+  int i2;                         /* Integer value 2 */
+};
+typedef struct ServerFcntlArg ServerFcntlArg;
+
 /*
 ** Possible values for Server.eTrans.
 */
@@ -153,6 +170,12 @@ static void serverAssertMutexHeld(void){
   assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1)) );
 }
 
+/*
+** Locate the ServerDb object shared by all connections to the db identified
+** by aFileId[2], increment its ref count and set pNew->pDb to point to it. 
+** In this context "locate" may mean to find an existing object or to
+** allocate a new one.
+*/
 static int serverFindDatabase(Server *pNew, i64 *aFileId){
   ServerDb *p;
   int rc = SQLITE_OK;
@@ -165,18 +188,11 @@ static int serverFindDatabase(Server *pNew, i64 *aFileId){
   if( p==0 ){
     p = (ServerDb*)sqlite3MallocZero(sizeof(ServerDb));
     if( p ){
-      p->aSlot = (u32*)sqlite3MallocZero(sizeof(u32)*HMA_PAGELOCK_SLOTS);
-      if( p->aSlot==0 ){
-        rc = SQLITE_NOMEM_BKPT;
-      }else{
-        p->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+      p->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
 #if SQLITE_THREADSAFE!=0
-        if( p->mutex==0 ) rc = SQLITE_NOMEM_BKPT;
+      if( p->mutex==0 ) rc = SQLITE_NOMEM_BKPT;
 #endif
-      }
-
       if( rc==SQLITE_NOMEM ){
-        sqlite3_free(p->aSlot);
         sqlite3_free(p);
         p = 0;
       }else{
@@ -202,14 +218,18 @@ static int serverFindDatabase(Server *pNew, i64 *aFileId){
 ** Free all resources allocated by serverInitDatabase() associated with the
 ** object passed as the only argument.
 */
-static void serverShutdownDatabase(ServerDb *pDb){
+static void serverShutdownDatabase(
+  ServerDb *pDb, 
+  sqlite3_file *dbfd, 
+  int bDelete
+){
   int i;
 
   for(i=0; i<HMA_MAX_TRANSACTIONID; i++){
     ServerJournal *pJ = &pDb->aJrnl[i];
     if( pJ->jfd ){
       sqlite3OsClose(pJ->jfd);
-      sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0);
+      if( bDelete ) sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0);
     }
     sqlite3_free(pJ->zJournal);
   }
@@ -220,7 +240,15 @@ static void serverShutdownDatabase(ServerDb *pDb){
     pDb->aJrnlFdSpace = 0;
   }
 
-  sqlite3_free(pDb->aSlot);
+  if( pDb->pServerShm ){
+    ServerFcntlArg arg;
+    memset(&arg, 0, sizeof(ServerFcntlArg));
+    arg.h = pDb->pServerShm;
+    sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMCLOSE, (void*)&arg);
+  }else{
+    sqlite3_free(pDb->aSlot);
+  }
+  pDb->aSlot = 0;
   pDb->bInit = 0;
 }
 
@@ -229,24 +257,48 @@ static void serverShutdownDatabase(ServerDb *pDb){
 ** is established. It is responsible for rolling back any hot journal
 ** files found in the file-system.
 */
-static int serverInitDatabase(Server *pNew){
+static int serverInitDatabase(Server *pNew, int eServer){
   int nByte;
   int rc = SQLITE_OK;
   ServerDb *pDb = pNew->pDb;
   sqlite3_vfs *pVfs;
+  sqlite3_file *dbfd = sqlite3PagerFile(pNew->pPager);
   const char *zFilename = sqlite3PagerFilename(pNew->pPager, 0);
+  int bRollback = 0;
 
   assert( zFilename );
+  assert( eServer==1 || eServer==2 );
+
   pVfs = pDb->pVfs = sqlite3PagerVfs(pNew->pPager);
   nByte = ROUND8(pVfs->szOsFile) * HMA_MAX_TRANSACTIONID;
   pDb->aJrnlFdSpace = (u8*)sqlite3MallocZero(nByte);
   if( pDb->aJrnlFdSpace==0 ){
     rc = SQLITE_NOMEM_BKPT;
   }else{
+    if( eServer==2 ){
+      ServerFcntlArg arg;
+      arg.h = 0;
+      arg.p = 0;
+      arg.i1 = sizeof(u32)*HMA_PAGELOCK_SLOTS;
+      arg.i2 = 0;
+
+      rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMOPEN, (void*)&arg);
+      if( rc==SQLITE_OK ){
+        pDb->aSlot = (u32*)arg.p;
+        pDb->pServerShm = arg.h;
+        bRollback = arg.i2;
+      }
+    }else{
+      pDb->aSlot = (u32*)sqlite3MallocZero(sizeof(u32)*HMA_PAGELOCK_SLOTS);
+      if( pDb->aSlot==0 ) rc = SQLITE_NOMEM_BKPT;
+      bRollback = 1;
+    }
+  }
+
+  if( rc==SQLITE_OK ){
     u8 *a = pDb->aJrnlFdSpace;
     int i;
     for(i=0; rc==SQLITE_OK && i<HMA_MAX_TRANSACTIONID; i++){
-      int bExists = 0;
       ServerJournal *pJ = &pDb->aJrnl[i];
       pJ->jfd = (sqlite3_file*)&a[ROUND8(pVfs->szOsFile)*i];
       pJ->zJournal = sqlite3_mprintf("%s-journal/%d-journal", zFilename, i);
@@ -255,37 +307,92 @@ static int serverInitDatabase(Server *pNew){
         break;
       }
 
-      rc = sqlite3OsAccess(pVfs, pJ->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
-      if( rc==SQLITE_OK && bExists ){
-        int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
-        rc = sqlite3OsOpen(pVfs, pJ->zJournal, pJ->jfd, flags, &flags);
-        if( rc==SQLITE_OK ){
-          rc = sqlite3PagerRollbackJournal(pNew->pPager, pJ->jfd);
+      if( bRollback ){
+        int bExist = 0;
+        rc = sqlite3OsAccess(pVfs, pJ->zJournal, SQLITE_ACCESS_EXISTS, &bExist);
+        if( rc==SQLITE_OK && bExist ){
+          int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
+          rc = sqlite3OsOpen(pVfs, pJ->zJournal, pJ->jfd, flags, &flags);
+          if( rc==SQLITE_OK ){
+            rc = sqlite3PagerRollbackJournal(pNew->pPager, pJ->jfd);
+          }
         }
       }
     }
   }
 
+  if( rc==SQLITE_OK && pDb->pServerShm && bRollback ){
+    ServerFcntlArg arg;
+    arg.h = pDb->pServerShm;
+    arg.p = 0;
+    arg.p = 0;
+    arg.i2 = 0;
+    rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMOPEN2, (void*)&arg);
+  }
+
   if( rc==SQLITE_OK ){
     pDb->bInit = 1;
   }else{
-    serverShutdownDatabase(pNew->pDb);
+    serverShutdownDatabase(pNew->pDb, dbfd, eServer==1);
   }
   return rc;
 }
 
+/*
+** Take (bLock==1) or release (bLock==0) a server shmlock on slot iSlot.
+** Return SQLITE_OK if successful, or SQLITE_BUSY if the lock cannot be
+** obtained. 
+*/
+static int serverFcntlLock(Server *p, int iSlot, int bLock){
+  sqlite3_file *dbfd = sqlite3PagerFile(p->pPager);
+  int rc;
+  ServerFcntlArg arg;
+  arg.h = p->pDb->pServerShm;
+  arg.p = 0;
+  arg.i1 = iSlot;
+  arg.i2 = bLock;
+  rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMLOCK, (void*)&arg);
+  return rc;
+}
+
 /*
 ** Close the connection.
 */
 void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){
   ServerDb *pDb = p->pDb;
 
+  /* In a multi-process setup, release the lock on the client slot and
+  ** clear the bit in the ServerDb.transmask bitmask. */
+  if( pDb->pServerShm && p->iTransId>=0 ){
+    sqlite3_mutex_enter(pDb->mutex);
+    pDb->transmask &= ~((u32)1 << p->iTransId);
+    sqlite3_mutex_leave(pDb->mutex);
+    serverFcntlLock(p, p->iTransId, 0);
+  }
+
   serverEnterMutex();
   pDb->nClient--;
   if( pDb->nClient==0 ){
+    sqlite3_file *dbfd = sqlite3PagerFile(p->pPager);
     ServerPage *pFree;
     ServerDb **pp;
-    serverShutdownDatabase(pDb);
+
+    /* Delete the journal files on shutdown if an EXCLUSIVE lock is already
+    ** held (single process mode) or can be obtained (multi process mode)
+    ** on the database file. 
+    **
+    ** TODO: Need to account for disk-full errors and the like here. It
+    ** is not necessarily safe to delete journal files here. */
+    int bDelete = 0;
+    if( pDb->pServerShm ){
+      int res;
+      res = sqlite3OsLock(dbfd, EXCLUSIVE_LOCK);
+      if( res==SQLITE_OK ) bDelete = 1;
+    }else{
+      bDelete = 1;
+    }
+    serverShutdownDatabase(pDb, dbfd, bDelete);
+
     for(pp=&g_server.pDb; *pp!=pDb; pp=&((*pp)->pNext));
     *pp = pDb->pNext;
     sqlite3_mutex_free(pDb->mutex);
@@ -305,7 +412,8 @@ void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){
 ** Connect to the system.
 */
 int sqlite3ServerConnect(
-  Pager *pPager,
+  Pager *pPager,                  /* Pager object */
+  int eServer,                    /* 1 -> single process, 2 -> multi process */
   Server **ppOut                  /* OUT: Server handle */
 ){
   Server *pNew = 0;
@@ -324,9 +432,26 @@ int sqlite3ServerConnect(
         sqlite3_free(pNew);
         pNew = 0;
       }else{
+        ServerDb *pDb = pNew->pDb;
         sqlite3_mutex_enter(pNew->pDb->mutex);
-        if( pNew->pDb->bInit==0 ){
-          rc = serverInitDatabase(pNew);
+        if( pDb->bInit==0 ){
+          rc = serverInitDatabase(pNew, eServer);
+        }
+
+        /* If this is a multi-process connection, need to lock a 
+        ** client locking-slot before continuing. */
+        if( rc==SQLITE_OK && pDb->pServerShm ){
+          int i;
+          rc = SQLITE_BUSY;
+          for(i=0; rc==SQLITE_BUSY && i<HMA_MAX_TRANSACTIONID; i++){
+            if( 0==(pDb->transmask & ((u32)1 << i)) ){
+              rc = serverFcntlLock(pNew, i, 1);
+              if( rc==SQLITE_OK ){
+                pNew->iTransId = i;
+                pDb->transmask |= ((u32)1 << i);
+              }
+            }
+          }
         }
         sqlite3_mutex_leave(pNew->pDb->mutex);
       }
@@ -335,6 +460,11 @@ int sqlite3ServerConnect(
     }
   }
 
+  if( rc!=SQLITE_OK && pNew ){
+    sqlite3ServerDisconnect(pNew, dbfd);
+    pNew = 0;
+  }
+
   *ppOut = pNew;
   return rc;
 }
@@ -346,148 +476,176 @@ int sqlite3ServerBegin(Server *p, int bReadonly){
   int rc = SQLITE_OK;
 
   if( p->eTrans==SERVER_TRANS_NONE ){
-    int id;
     ServerDb *pDb = p->pDb;
     u32 t;
 
-    assert( p->iTransId<0 );
     assert( p->pNext==0 );
-    sqlite3_mutex_enter(pDb->mutex);
-
-    if( bReadonly ){
-      Server *pIter;
-      p->iCommitId = pDb->iNextCommit;
-      for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
-        if( pIter->iCommitId<p->iCommitId ){
-          p->iCommitId = pIter->iCommitId;
-        }
-      }
-      p->pNext = pDb->pReader;
-      pDb->pReader = p;
-      p->eTrans = SERVER_TRANS_READONLY;
+    if( pDb->pServerShm ){
+      p->eTrans = SERVER_TRANS_READWRITE;
     }else{
-      /* Find a transaction id to use */
-      rc = SQLITE_BUSY;
-      t = pDb->transmask;
-      for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
-        if( (t & (1 << id))==0 ){
-          t = t | (1 << id);
-          rc = SQLITE_OK;
-          break;
+      assert( p->iTransId<0 );
+      sqlite3_mutex_enter(pDb->mutex);
+      if( bReadonly ){
+        Server *pIter;
+        p->iCommitId = pDb->iNextCommit;
+        for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
+          if( pIter->iCommitId<p->iCommitId ){
+            p->iCommitId = pIter->iCommitId;
+          }
+        }
+        p->pNext = pDb->pReader;
+        pDb->pReader = p;
+        p->eTrans = SERVER_TRANS_READONLY;
+      }else{
+        int id;
+
+        /* Find a transaction id to use */
+        rc = SQLITE_BUSY;
+        t = pDb->transmask;
+        for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
+          if( (t & (1 << id))==0 ){
+            t = t | (1 << id);
+            rc = SQLITE_OK;
+            break;
+          }
+        }
+        pDb->transmask = t;
+        p->eTrans = SERVER_TRANS_READWRITE;
+        if( rc==SQLITE_OK ){
+          p->iTransId = id;
         }
       }
-      pDb->transmask = t;
-      p->eTrans = SERVER_TRANS_READWRITE;
+      sqlite3_mutex_leave(pDb->mutex);
     }
 
-    sqlite3_mutex_leave(pDb->mutex);
-
-    if( rc==SQLITE_OK && bReadonly==0 ){
-      ServerJournal *pJrnl = &pDb->aJrnl[id];
+    if( rc==SQLITE_OK && p->eTrans==SERVER_TRANS_READWRITE ){
+      ServerJournal *pJrnl = &pDb->aJrnl[p->iTransId];
       sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal);
-      p->iTransId = id;
     }
   }
 
   return rc;
 }
 
+static u32 *serverLockingSlot(ServerDb *pDb, u32 pgno){
+  return &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
+}
+
 static void serverReleaseLocks(Server *p){
   ServerDb *pDb = p->pDb;
   int i;
-  assert( sqlite3_mutex_held(pDb->mutex) );
+
+  assert( pDb->pServerShm || sqlite3_mutex_held(pDb->mutex) );
 
   for(i=0; i<p->nLock; i++){
-    u32 *pSlot = &pDb->aSlot[p->aLock[i] % HMA_PAGELOCK_SLOTS];
-    if( slotGetWriter(*pSlot)==p->iTransId ){
-      *pSlot -= ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
+    while( 1 ){
+      u32 *pSlot = serverLockingSlot(pDb, p->aLock[i]);
+      u32 o = *pSlot;
+      u32 n = o & ~((u32)1 << p->iTransId);
+      if( slotGetWriter(n)==p->iTransId ){
+        n -= ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
+      }
+      if( serverCompareAndSwap(pSlot, o, n) ) break;
     }
-    *pSlot &= ~((u32)1 << p->iTransId);
   }
 
   p->nLock = 0;
 }
 
 /*
-** End a transaction (and release all locks).
+** End a transaction (and release all locks). This version runs in
+** single process mode only.
 */
-int sqlite3ServerEnd(Server *p){
-  int rc = SQLITE_OK;
-  if( p->eTrans!=SERVER_TRANS_NONE ){
-    Server **pp;
-    ServerDb *pDb = p->pDb;
-    ServerPage *pPg = 0;
+static void serverEndSingle(Server *p){
+  Server **pp;
+  ServerDb *pDb = p->pDb;
+  ServerPage *pPg = 0;
 
-    sqlite3_mutex_enter(pDb->mutex);
+  assert( p->eTrans!=SERVER_TRANS_NONE );
+  assert( pDb->pServerShm==0 );
 
-    if( p->eTrans==SERVER_TRANS_READONLY ){
-      /* Remove the connection from the readers list */
-      for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext));
-      *pp = p->pNext;
-    }else{
-      serverReleaseLocks(p);
+  sqlite3_mutex_enter(pDb->mutex);
 
-      /* Clear the bit in the transaction mask. */
-      pDb->transmask &= ~((u32)1 << p->iTransId);
+  if( p->eTrans==SERVER_TRANS_READONLY ){
+    /* Remove the connection from the readers list */
+    for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext));
+    *pp = p->pNext;
+  }else{
+    serverReleaseLocks(p);
 
-      /* If this connection is in the committers list, remove it. */
-      for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){
-        if( *pp==p ){
-          *pp = p->pNext;
-          break;
-        }
+    /* Clear the bit in the transaction mask. */
+    pDb->transmask &= ~((u32)1 << p->iTransId);
+
+    /* If this connection is in the committers list, remove it. */
+    for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){
+      if( *pp==p ){
+        *pp = p->pNext;
+        break;
       }
     }
+  }
 
-    /* See if it is possible to free any ServerPage records. If so, remove
-    ** them from the linked list and hash table, but do not call sqlite3_free()
-    ** on them until the mutex has been released.  */
-    if( pDb->pPgFirst ){
-      ServerPage *pLast = 0;
-      Server *pIter;
-      int iOldest = 0x7FFFFFFF;
-      for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){
-        iOldest = MIN(iOldest, pIter->iCommitId);
-      }
-      for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
-        iOldest = MIN(iOldest, pIter->iCommitId);
-      }
+  /* See if it is possible to free any ServerPage records. If so, remove
+  ** them from the linked list and hash table, but do not call sqlite3_free()
+  ** on them until the mutex has been released.  */
+  if( pDb->pPgFirst ){
+    ServerPage *pLast = 0;
+    Server *pIter;
+    int iOldest = 0x7FFFFFFF;
+    for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){
+      iOldest = MIN(iOldest, pIter->iCommitId);
+    }
+    for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
+      iOldest = MIN(iOldest, pIter->iCommitId);
+    }
 
-      for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){
-        if( pPg->pHashPrev ){
-          pPg->pHashPrev->pHashNext = pPg->pHashNext;
-        }else{
-          int iHash = pPg->pgno % HMA_HASH_SIZE;
-          assert( pDb->apPg[iHash]==pPg );
-          pDb->apPg[iHash] = pPg->pHashNext;
-        }
-        if( pPg->pHashNext ){
-          pPg->pHashNext->pHashPrev = pPg->pHashPrev;
-        }
-        pLast = pPg;
+    for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){
+      if( pPg->pHashPrev ){
+        pPg->pHashPrev->pHashNext = pPg->pHashNext;
+      }else{
+        int iHash = pPg->pgno % HMA_HASH_SIZE;
+        assert( pDb->apPg[iHash]==pPg );
+        pDb->apPg[iHash] = pPg->pHashNext;
       }
-
-      if( pLast ){
-        assert( pLast->pNext==pPg );
-        pLast->pNext = pDb->pFree;
-        pDb->pFree = pDb->pPgFirst;
+      if( pPg->pHashNext ){
+        pPg->pHashNext->pHashPrev = pPg->pHashPrev;
       }
+      pLast = pPg;
+    }
 
-      if( pPg==0 ){
-        pDb->pPgFirst = pDb->pPgLast = 0;
-      }else{
-        pDb->pPgFirst = pPg;
-      }
+    if( pLast ){
+      assert( pLast->pNext==pPg );
+      pLast->pNext = pDb->pFree;
+      pDb->pFree = pDb->pPgFirst;
     }
 
-    sqlite3_mutex_leave(pDb->mutex);
+    if( pPg==0 ){
+      pDb->pPgFirst = pDb->pPgLast = 0;
+    }else{
+      pDb->pPgFirst = pPg;
+    }
+  }
 
-    p->pNext = 0;
-    p->eTrans = SERVER_TRANS_NONE;
-    p->iTransId = -1;
-    p->iCommitId = 0;
+  sqlite3_mutex_leave(pDb->mutex);
+
+  p->pNext = 0;
+  p->eTrans = SERVER_TRANS_NONE;
+  p->iTransId = -1;
+  p->iCommitId = 0;
+}
+
+/*
+** End a transaction (and release all locks).
+*/
+int sqlite3ServerEnd(Server *p){
+  if( p->eTrans!=SERVER_TRANS_NONE ){
+    if( p->pDb->pServerShm ){
+      serverReleaseLocks(p);
+    }else{
+      serverEndSingle(p);
+    }
   }
-  return rc;
+  return SQLITE_OK;
 }
 
 int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){
@@ -495,6 +653,8 @@ int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){
   int rc = SQLITE_OK;
   ServerPage *pIter;
 
+  /* This should never be called in multi-process mode */
+  assert( pDb->pServerShm==0 );
   if( pPg==0 ) return SQLITE_OK;
 
   sqlite3_mutex_enter(pDb->mutex);
@@ -569,7 +729,7 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
   int rc = SQLITE_OK;
 
   assert( p->eTrans==SERVER_TRANS_READWRITE 
-       || p->eTrans==SERVER_TRANS_READONLY 
+       || (p->eTrans==SERVER_TRANS_READONLY && p->pDb->pServerShm==0)
   );
   if( p->eTrans==SERVER_TRANS_READWRITE ){
     ServerDb *pDb = p->pDb;
@@ -577,6 +737,7 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
     int bSkip = 0;
     u32 *pSlot;
 
+    /* Grow the aLock[] array if required */
     assert( p->iTransId>=0 );
     assert( p->nLock<=p->nAlloc );
     if( p->nLock==p->nAlloc ){
@@ -588,37 +749,48 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
       p->aLock = aNew;
     }
 
-    sqlite3_mutex_enter(pDb->mutex);
+    /* Find the locking slot for the page in question */
+    pSlot = serverLockingSlot(pDb, pgno);
 
-    pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
-    assert( slotGetWriter(*pSlot)<0 
-        || slotReaderMask(*pSlot)==0 
-        || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
-        );
-
-    iWriter = slotGetWriter(*pSlot);
-    if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
-      bSkip = 1;
-    }else if( iWriter>=0 ){
-      rc = SQLITE_BUSY_DEADLOCK;
-    }else if( bWrite ){
-      if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
-        *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
-      }else{
+    if( pDb->pServerShm==0 ) sqlite3_mutex_enter(pDb->mutex);
+
+    while( 1 ){
+      u32 o = *pSlot;
+      u32 n = o;
+
+      assert( slotGetWriter(o)<0 
+          || slotReaderMask(o)==0 
+          || slotReaderMask(o)==(1 << slotGetWriter(o))
+      );
+
+      iWriter = slotGetWriter(o);
+      if( iWriter==p->iTransId || (bWrite==0 && (o & (1<<p->iTransId))) ){
+        bSkip = 1;
+        break;
+      }else if( iWriter>=0 ){
         rc = SQLITE_BUSY_DEADLOCK;
+      }else if( bWrite ){
+        if( (slotReaderMask(o) & ~(1 << p->iTransId))==0 ){
+          n += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
+        }else{
+          rc = SQLITE_BUSY_DEADLOCK;
+        }
+      }else{
+        n |= (1 << p->iTransId);
       }
-    }else{
-      *pSlot |= (1 << p->iTransId);
-    }
 
-    assert( slotGetWriter(*pSlot)<0 
-        || slotReaderMask(*pSlot)==0 
-        || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
-        );
+      assert( slotGetWriter(n)<0 
+          || slotReaderMask(n)==0 
+          || slotReaderMask(n)==(1 << slotGetWriter(n))
+      );
+      if( rc!=SQLITE_OK || serverCompareAndSwap(pSlot, o, n) ) break;
+    }
 
-    sqlite3_mutex_leave(pDb->mutex);
+    if( pDb->pServerShm==0 ){
+      sqlite3_mutex_leave(pDb->mutex);
+    }
 
-    if( bSkip==0 ){
+    if( bSkip==0 && rc==SQLITE_OK ){
       p->aLock[p->nLock++] = pgno;
     }
   }
@@ -643,6 +815,8 @@ void sqlite3ServerReadPage(Server *p, Pgno pgno, u8 **ppData){
     ServerPage *pBest = 0;
     int iHash = pgno % HMA_HASH_SIZE;
 
+    /* There are no READONLY transactions in a multi process system */
+    assert( pDb->pServerShm==0 );
     sqlite3_mutex_enter(pDb->mutex);
 
     /* Search the hash table for the oldest version of page pgno with
@@ -671,6 +845,7 @@ void sqlite3ServerEndReadPage(Server *p, Pgno pgno){
   if( p->eTrans==SERVER_TRANS_READONLY ){
     ServerDb *pDb = p->pDb;
     u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
+    assert( pDb->pServerShm==0 );
     sqlite3_mutex_enter(pDb->mutex);
     serverIncrSlowReader(pSlot, -1);
     assert( slotGetSlowReaders(*pSlot)>=0 );
@@ -681,6 +856,7 @@ void sqlite3ServerEndReadPage(Server *p, Pgno pgno){
 ServerPage *sqlite3ServerBuffer(Server *p){
   ServerDb *pDb = p->pDb;
   ServerPage *pRet = 0;
+  assert( pDb->pServerShm==0 );
   sqlite3_mutex_enter(pDb->mutex);
   if( pDb->pFree ){
     pRet = pDb->pFree;
@@ -701,4 +877,13 @@ int sqlite3ServerIsReadonly(Server *p){
   return (p && p->eTrans==SERVER_TRANS_READONLY);
 }
 
+/*
+** Return true if the argument is non-NULL and connects to a single-process
+** server system. Return false if the argument is NULL or the system supports
+** multiple processes.
+*/
+int sqlite3ServerIsSingleProcess(Server *p){
+  return (p && p->pDb->pServerShm==0);
+}
+
 #endif /* ifdef SQLITE_SERVER_EDITION */
index 30bdf5dd3a9ccff3fb38b8ce9eb3312996dcb7bb..17fdc8ce8125356b576fd58de24076a80bef00a6 100644 (file)
@@ -31,7 +31,7 @@ struct ServerPage {
   ServerPage *pHashPrev;
 };
 
-int sqlite3ServerConnect(Pager *pPager, Server **ppOut);
+int sqlite3ServerConnect(Pager *pPager, int eServer, Server **ppOut);
 void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);
 
 int sqlite3ServerBegin(Server *p, int bReadonly);
@@ -46,6 +46,8 @@ int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite);
 
 ServerPage *sqlite3ServerBuffer(Server*);
 
+int sqlite3ServerIsSingleProcess(Server*);
+
 /* For "BEGIN READONLY" clients. */
 int sqlite3ServerIsReadonly(Server*);
 void sqlite3ServerReadPage(Server*, Pgno, u8**);
index ed01ad51c575d34f01decb9286575315ab23a55e..89b41175a7d9b1359c9a4820ea5a99b0705a988c 100644 (file)
@@ -1093,6 +1093,10 @@ struct sqlite3_io_methods {
 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE  33
 #define SQLITE_FCNTL_FILEID                 34
 #define SQLITE_FCNTL_SERVER_MODE            35
+#define SQLITE_FCNTL_SERVER_SHMOPEN         36
+#define SQLITE_FCNTL_SERVER_SHMOPEN2        37
+#define SQLITE_FCNTL_SERVER_SHMLOCK         38
+#define SQLITE_FCNTL_SERVER_SHMCLOSE        39
 
 /* deprecated names */
 #define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
index 4eea6ef65d715ebe9541dbcb419335cf4cf19f7d..4f6d01dad6291da039036918a3b4c31296ebc89c 100644 (file)
--- a/src/wal.h
+++ b/src/wal.h
@@ -144,9 +144,5 @@ int sqlite3WalFramesize(Wal *pWal);
 /* Return the sqlite3_file object for the WAL file */
 sqlite3_file *sqlite3WalFile(Wal *pWal);
 
-#ifdef SQLITE_SERVER_EDITION
-int sqlite3WalServer(Wal *pWal, Server *pServer);
-#endif
-
 #endif /* ifndef SQLITE_OMIT_WAL */
 #endif /* SQLITE_WAL_H */
index a1d796985535417084bd612dbcf298cb3048f85e..d0888369d18a84ce9a8458a83bfa8a41dc947a94 100644 (file)
@@ -19,125 +19,130 @@ set testprefix server2
 
 source $testdir/server_common.tcl
 return_if_no_server
-
 db close
-foreach f [glob -nocomplain test.db*] {
-  forcedelete $f
-}
-
-#-------------------------------------------------------------------------
-# Check that the *-journal* files are deleted correctly.
-#
-server_reset_db
-do_execsql_test 1.0 {
-  CREATE TABLE t1(a, b);
-} {}
-
-do_test 1.1 {
-  lsort [glob -nocomplain test.db-journal/*]
-} {test.db-journal/0-journal}
-
-do_test 1.2 {
-  db close
-  lsort [glob -nocomplain test.db-journal/*]
-} {}
 
-server_sqlite3 db test.db
-do_execsql_test 1.3 {
-  CREATE TABLE t2(a, b);
-} {}
+foreach {tn vfs} {1 unix-excl 2 unix} {
+  server_set_vfs $vfs
 
-server_sqlite3 db2 test.db
-do_test 1.4 {
-  db eval {
-    BEGIN;
-      INSERT INTO t1 VALUES(1, 2);
+  foreach f [glob -nocomplain test.db*] {
+    forcedelete $f
   }
-  db2 eval {
-    BEGIN;
-      INSERT INTO t2 VALUES(3, 4);
-  }
-} {}
-
-do_test 1.5 {
-  db2 eval COMMIT
-  db eval COMMIT
-  lsort [glob -nocomplain test.db-journal/*]
-} {test.db-journal/0-journal test.db-journal/1-journal}
-
-do_test 1.6 {
-  db close
-  lsort [glob -nocomplain test.db-journal/*]
-} {test.db-journal/0-journal test.db-journal/1-journal}
-
-do_test 1.7 {
-  db2 close
-  lsort [glob -nocomplain test.db-journal/*]
-} {}
 
-#-------------------------------------------------------------------------
-#
-server_reset_db
-server_sqlite3 db2 test.db
-
-do_execsql_test 2.0 {
-  CREATE TABLE t1(a, b);
-  CREATE TABLE t2(c, d);
-}
-
-# Two concurrent transactions committed.
-#
-do_test 2.1 {
-  db eval {
-    BEGIN;
-      INSERT INTO t1 VALUES(1, 2);
+  #-------------------------------------------------------------------------
+  # Check that the *-journal* files are deleted correctly.
+  #
+  server_reset_db
+  do_execsql_test 1.0 {
+    CREATE TABLE t1(a, b);
+  } {}
+  
+  do_test $tn.1.1 {
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {test.db-journal/0-journal}
+  
+  do_test $tn.1.2 {
+    db close
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {}
+  
+  server_sqlite3 db test.db
+  do_execsql_test $tn.1.3 {
+    CREATE TABLE t2(a, b);
+  } {}
+  
+  server_sqlite3 db2 test.db
+  do_test $tn.1.4 {
+    db eval {
+      BEGIN;
+        INSERT INTO t1 VALUES(1, 2);
+    }
+    db2 eval {
+      BEGIN;
+        INSERT INTO t2 VALUES(3, 4);
+    }
+  } {}
+  
+  do_test $tn.1.5 {
+    db2 eval COMMIT
+    db eval COMMIT
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {test.db-journal/0-journal test.db-journal/1-journal}
+  
+  do_test $tn.1.6 {
+    db close
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {test.db-journal/0-journal test.db-journal/1-journal}
+  
+  do_test $tn.1.7 {
+    db2 close
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {}
+  
+  #-------------------------------------------------------------------------
+  #
+  server_reset_db
+  server_sqlite3 db2 test.db
+  
+  do_execsql_test $tn.2.0 {
+    CREATE TABLE t1(a, b);
+    CREATE TABLE t2(c, d);
   }
-  db2 eval {
-    BEGIN;
-      INSERT INTO t2 VALUES(3, 4);
+  
+  # Two concurrent transactions committed.
+  #
+  do_test $tn.2.1 {
+    db eval {
+      BEGIN;
+        INSERT INTO t1 VALUES(1, 2);
+    }
+    db2 eval {
+      BEGIN;
+        INSERT INTO t2 VALUES(3, 4);
+    }
+  } {}
+  do_test $tn.2.2 {
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {test.db-journal/0-journal test.db-journal/1-journal}
+  do_test $tn.2.3.1 { db eval COMMIT  } {}
+  do_test $tn.2.3.2 { db2 eval COMMIT } {}
+  do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4}
+  do_test $tn.2.5 {
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {test.db-journal/0-journal test.db-journal/1-journal}
+  
+  do_test $tn.2.6 {
+    execsql {BEGIN}
+    execsql {INSERT INTO t1 VALUES(5, 6)}
+  
+    execsql {BEGIN} db2
+    catchsql {INSERT INTO t1 VALUES(7, 8)} db2
+  } {1 {database is locked}}
+  do_test $tn.2.7 {
+    # Transaction is automatically rolled back in this case.
+    sqlite3_get_autocommit db2
+  } {1}
+  do_test $tn.2.8 {
+    execsql COMMIT
+    execsql { SELECT * FROM t1 } db2
+  } {1 2 5 6}
+  db2 close
+  
+  #-------------------------------------------------------------------------
+  #
+  server_reset_db
+  do_execsql_test $tn.3.0 {
+    CREATE TABLE t1(a, b);
   }
-} {}
-do_test 2.2 {
-  lsort [glob -nocomplain test.db-journal/*]
-} {test.db-journal/0-journal test.db-journal/1-journal}
-do_test 2.3.1 { db eval COMMIT  } {}
-do_test 2.3.2 { db2 eval COMMIT } {}
-do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4}
-do_test 2.5 {
-  lsort [glob -nocomplain test.db-journal/*]
-} {test.db-journal/0-journal test.db-journal/1-journal}
-
-do_test 2.6 {
-  execsql {BEGIN}
-  execsql {INSERT INTO t1 VALUES(5, 6)}
-
-  execsql {BEGIN} db2
-  catchsql {INSERT INTO t1 VALUES(7, 8)} db2
-} {1 {database is locked}}
-do_test 2.7 {
-  # Transaction is automatically rolled back in this case.
-  sqlite3_get_autocommit db2
-} {1}
-do_test 2.8 {
-  execsql COMMIT
-  execsql { SELECT * FROM t1 } db2
-} {1 2 5 6}
-db2 close
-
-#-------------------------------------------------------------------------
-#
-server_reset_db
-do_execsql_test 3.0 {
-  CREATE TABLE t1(a, b);
+  
+  do_test $tn.3.1 {
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {test.db-journal/0-journal}
+  
+  do_test $tn.3.2 {
+    db close
+    lsort [glob -nocomplain test.db-journal/*-journal]
+  } {}
 }
 
-do_test 3.1 {
-  lsort [glob -nocomplain test.db-journal/*]
-} {test.db-journal/0-journal}
-
-do_test 3.2 {
-  db close
-  lsort [glob -nocomplain test.db-journal/*]
-} {}
 finish_test
 
index 19b72a54a08df23a9240f2d8135864e04018a0e3..484d2e96e95e9df1a62cd74008f033c986587c18 100644 (file)
@@ -21,32 +21,37 @@ set testprefix server3
 source $testdir/server_common.tcl
 return_if_no_server
 
-server_reset_db
-server_sqlite3 db2 test.db
-
-do_test 1.1 {
-  db eval { CREATE TABLE t1(a, b) }
-  db2 eval { CREATE TABLE t2(a, b) }
-} {}
-
-do_test 1.2 {
-  db eval {
-    INSERT INTO t2 VALUES(1, 2);
-    BEGIN;
-    INSERT INTO t1 VALUES(1, 2);
-  }
-} {}
-
-do_test 1.3 { 
-  list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
-} {1 {database is locked}}
-do_test 1.4 { 
-  list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
-} {1 {database is locked}}
-
-do_test 1.4 { 
-  db2 eval { SELECT * FROM t2 }
-} {1 2}
+foreach {tn vfs} {1 unix-excl 2 unix} {
+  server_set_vfs $vfs
+
+  server_reset_db
+  server_sqlite3 db2 test.db
+
+  do_test 1.1 {
+    db eval { CREATE TABLE t1(a, b) }
+    db2 eval { CREATE TABLE t2(a, b) }
+  } {}
+  
+  do_test 1.2 {
+    db eval {
+      INSERT INTO t2 VALUES(1, 2);
+      BEGIN;
+      INSERT INTO t1 VALUES(1, 2);
+    }
+  } {}
+  
+  do_test 1.3 { 
+    list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
+  } {1 {database is locked}}
+  do_test 1.4 { 
+    list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
+  } {1 {database is locked}}
+  
+  do_test 1.4 { 
+    db2 eval { SELECT * FROM t2 }
+  } {1 2}
+}
+
 
 finish_test
 
index 84cd6b27e4b3f1ead6d91b4b4c7804753d132205..d21c60085d193cf485b403885f896f1cd7e083b5 100644 (file)
@@ -22,7 +22,7 @@ ifcapable !server {
 }
 
 proc server_sqlite3 {cmd file} {
-  sqlite3 $cmd $file -vfs unix-excl
+  sqlite3 $cmd $file -vfs $::server_vfs
 }
 
 proc server_reset_db {} {
@@ -32,4 +32,8 @@ proc server_reset_db {} {
   server_sqlite3 db test.db 
 }
 
+set ::server_vfs unix-excl
+proc server_set_vfs {vfs} {
+  set ::server_vfs $vfs
+}