]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Progress towards a VFS that will support WAL. Locking code is in place
authordrh <drh@noemail.net>
Thu, 29 Apr 2010 15:17:48 +0000 (15:17 +0000)
committerdrh <drh@noemail.net>
Thu, 29 Apr 2010 15:17:48 +0000 (15:17 +0000)
but is untested.  Still no support for the DMS.

FossilOrigin-Name: 1bde41cf081570ad257f927b641e752dff4ed014

manifest
manifest.uuid
src/os_common.h
src/os_unix.c
src/sqlite.h.in

index a96719324c30fb2ac47c7320dadb1538c92d7dda..81762c0b4c0d4f51d14739e423de8ea773f5d0d2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,8 @@
-C Close\sall\sopen\sdatabase\sconnections\sat\sthe\send\sof\swal.test.
-D 2010-04-29T14:58:53
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+C Progress\stowards\sa\sVFS\sthat\swill\ssupport\sWAL.\s\sLocking\scode\sis\sin\splace\nbut\sis\suntested.\s\sStill\sno\ssupport\sfor\sthe\sDMS.
+D 2010-04-29T15:17:48
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in d83a0ffef3dcbfb08b410a6c6dd6c009ec9167fb
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -148,9 +151,9 @@ F src/mutex_w32.c 4cc201c1bfd11d1562810554ff5500e735559d7e
 F src/notify.c cbfa66a836da3a51567209636e6a94059c137930
 F src/os.c 8bc63cf91e9802e2b807198e54e50227fa889306
 F src/os.h 534b082c3cb349ad05fa6fa0b06087e022af282c
-F src/os_common.h 240c88b163b02c21a9f21f87d49678a0aa21ff30
+F src/os_common.h 0d6ee583b6ee3185eb9d951f890c6dd03021a08d
 F src/os_os2.c 8ad77a418630d7dee91d1bb04f79c2096301d3a0
-F src/os_unix.c c8b7d1e0f4315e08cf54324ca706e1a1a80f5fc2
+F src/os_unix.c 340c503705cdb447753a55771911a92aa41d27c8
 F src/os_win.c a8fc01d8483be472e495793c01064fd87e56a5c1
 F src/pager.c b4a41030860229e80295fa1f37addab24d21799c
 F src/pager.h cee4487ab4f0911dd9f22a40e3cd55afdb7ef444
@@ -166,7 +169,7 @@ F src/resolve.c ac5f1a713cd1ae77f08b83cc69581e11bf5ae6f9
 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
 F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b
 F src/shell.c c40427c7245535a04a9cb4a417b6cc05c022e6a4
-F src/sqlite.h.in caf60df0991a14e22cce8243b9caa1c1dbd09d42
+F src/sqlite.h.in 0cc43a0fa5d26cd26d2609b84ecf094a159e9cb9
 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
 F src/sqliteInt.h 700a2df7b8dfe57c3b8d83c52ff40928e026220c
 F src/sqliteLimit.h 3afab2291762b5d09ae20c18feb8e9fa935a60a6
@@ -808,7 +811,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P d1cadeed4eea20d8892726cc8c69f4f3f57d0cd4
-R 606ec50aa0ca99ee2a7fabcf3364b376
-U dan
-Z c947c540d09aca713bd3451e1bec9253
+P 3cc55a7568daa3796483f632e33478969e381cf5
+R 1f2ad602233779dda1e35a2877201d1f
+U drh
+Z 5bb08c68390b552fdeb1ef93301ae98c
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQFL2aMhoxKgR168RlERAntLAKCNwNXdF9LrZZqkEN8f3gsvrSxoXACaA2Ku
+SvIawhrGV2wraxrir5MNan8=
+=Vfpz
+-----END PGP SIGNATURE-----
index 32ff2b16a0022d29a3a14a1e7a5c4da858d10de5..000aaadb5f749d9c7fca01a2874c3daf99f9e0b0 100644 (file)
@@ -1 +1 @@
-3cc55a7568daa3796483f632e33478969e381cf5
\ No newline at end of file
+1bde41cf081570ad257f927b641e752dff4ed014
\ No newline at end of file
index 6a2e2d93be6453592a61a7119f27b97b5cf83d3b..e6de2b25f7508d25f00014b77bf36312482e766f 100644 (file)
@@ -40,6 +40,7 @@ int sqlite3OSTrace = 0;
     if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
 #define OSTRACE7(X,Y,Z,A,B,C,D) \
     if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D)
+#define OSTRACE(X)          if( sqlite3OSTrace ) sqlite3DebugPrintf X
 #else
 #define OSTRACE1(X)
 #define OSTRACE2(X,Y)
@@ -48,6 +49,7 @@ int sqlite3OSTrace = 0;
 #define OSTRACE5(X,Y,Z,A,B)
 #define OSTRACE6(X,Y,Z,A,B,C)
 #define OSTRACE7(X,Y,Z,A,B,C,D)
+#define OSTRACE(X)
 #endif
 
 /*
index 7e3930a1e63123846b23865d0bf5f7b95b82ab2a..634f5ee21859b366faaedceed6c90e87af952512 100644 (file)
@@ -4562,42 +4562,355 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
   return 0;
 }
 
+/* Forward reference */
+typedef struct unixShm unixShm;
+typedef struct unixShmFile unixShmFile;
+
+/*
+** Object used to represent a single file opened and mmapped to provide
+** shared memory.  When multiple threads all reference the same
+** log-summary, each thread has its own unixFile object, but they all
+** point to a single instance of this object.  In other words, each
+** log-summary is opened only once per process.
+**
+** unixMutexHeld() must be true when creating or destroying
+** this object or while reading or writing the following fields:
+**
+**      nRef
+**      pNext 
+**
+** The following fields are read-only after the object is created:
+** 
+**      fid
+**      zFilename
+**
+** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and
+** unixMutexHeld() is true when reading or writing any other field
+** in this structure.
+*/
+struct unixShmFile {
+  struct unixFileId fid;     /* Unique file identifier */
+  sqlite3_mutex *mutex;      /* Mutex to access this object */
+  sqlite3_mutex *mutexBuf;   /* Mutex to access zBuf[] */
+  sqlite3_mutex *mutexRecov; /* The RECOVER mutex */
+  char *zFilename;           /* Name of the file */
+  int size;                  /* Size of the file */
+  int h;                     /* Open file descriptor */
+  char *pMMapBuf;            /* Where currently mmapped() */
+  int nReadPrefix;           /* Number of SQLITE_SHM_READ_PREFIX locks */
+  int nReadFull;             /* Number of SQLITE_SHM_READ_FULL locks */
+  int nRef;                  /* Number of unixShm objects pointing to this */
+  unixShm *pFirst;           /* All unixShm objects pointing to this */
+  unixShmFile *pNext;        /* Next in list of all unixShmFile objects */
+#ifdef SQLITE_DEBUG
+  u8 exclMask;               /* Mask of exclusive locks held */
+  u8 sharedMask;             /* Mask of shared locks held */
+  u8 nextShmId;              /* Next available unixShm.id value */
+#endif
+};
+
+/*
+** A global array of all unixShmFile objects.
+**
+** The unixMutexHeld() must be true while reading or writing this list.
+*/
+static unixShmFile *unixShmFileList = 0;
+
 /*
 ** Structure used internally by this VFS to record the state of an
-** open shared memory segment.
+** open shared memory connection.
+**
+** unixShm.pFile->mutex must be held while reading or writing the
+** unixShm.pNext and unixShm.locks[] elements.
+**
+** The unixShm.pFile element is initialized when the object is created
+** and is read-only thereafter.
 */
 struct unixShm {
-  sqlite3_vfs *pVfs;   /* VFS that opened this shared-memory segment */
-  int size;            /* Size of the shared memory area */
-  char *pBuf;          /* Pointer to the beginning */
-  unixFile fd;         /* The open file descriptor */
+  unixShmFile *pFile;        /* The underlying unixShmFile object */
+  unixShm *pNext;            /* Next unixShm with the same unixShmFile */
+  u8 lockState;              /* Current lock state */
+  u8 readLock;               /* Which of the two read-lock states to use */
+  u8 hasMutex;               /* True if holding the unixShmFile mutex */
+  u8 hasMutexBuf;            /* True if holding pFile->mutexBuf */
+  u8 hasMutexRecov;          /* True if holding pFile->mutexRecov */
+  u8 sharedMask;             /* Mask of shared locks held */
+  u8 exclMask;               /* Mask of exclusive locks held */
+#ifdef SQLITE_DEBUG
+  u8 id;                     /* Id of this connection with its unixShmFile */
+#endif
 };
 
 /*
-** Close a shared-memory segment
+** Size increment by which shared memory grows
 */
-static int unixShmClose(sqlite3_shm *pSharedMem){
-  struct unixShm *p = (struct unixShm*)pSharedMem;
-  if( p && p->pVfs ){
-    if( p->pBuf ){
-      munmap(p->pBuf, p->size);
+#define SQLITE_UNIX_SHM_INCR  4096
+
+/*
+** Constants used for locking
+*/
+#define UNIX_SHM_BASE      32        /* Byte offset of the first lock byte */
+#define UNIX_SHM_MUTEX     0x01      /* Mask for MUTEX lock */
+#define UNIX_SHM_DMS       0x04      /* Mask for Dead-Man-Switch lock */
+#define UNIX_SHM_A         0x10      /* Mask for region locks... */
+#define UNIX_SHM_B         0x20
+#define UNIX_SHM_C         0x40
+#define UNIX_SHM_D         0x80
+
+#ifdef SQLITE_DEBUG
+/*
+** Return a pointer to a nul-terminated string in static memory that
+** describes a locking mask.  The string is of the form "MSABCD" with
+** each character representing a lock.  "M" for MUTEX, "S" for DMS, 
+** and "A" through "D" for the region locks.  If a lock is held, the
+** letter is shown.  If the lock is not held, the letter is converted
+** to ".".
+**
+** This routine is for debugging purposes only and does not appear
+** in a production build.
+*/
+static const char *unixShmLockString(u8 mask){
+  static char zBuf[48];
+  static int iBuf = 0;
+  char *z;
+
+  z = &zBuf[iBuf];
+  iBuf += 8;
+  if( iBuf>=sizeof(zBuf) ) iBuf = 0;
+
+  z[0] = (mask & UNIX_SHM_MUTEX) ? 'M' : '.';
+  z[1] = (mask & UNIX_SHM_DMS)   ? 'S' : '.';
+  z[2] = (mask & UNIX_SHM_A)     ? 'A' : '.';
+  z[3] = (mask & UNIX_SHM_B)     ? 'B' : '.';
+  z[4] = (mask & UNIX_SHM_C)     ? 'C' : '.';
+  z[5] = (mask & UNIX_SHM_D)     ? 'D' : '.';
+  z[6] = 0;
+  return z;
+}
+#endif /* SQLITE_DEBUG */
+
+/*
+** Apply posix advisory locks for all bytes identified in lockMask.
+**
+** lockMask might contain multiple bits but all bits are guaranteed
+** to be contiguous.
+**
+** Locks block if the UNIX_SHM_MUTEX bit is set and are non-blocking
+** otherwise.
+*/
+static int unixShmSystemLocks(
+  unixShmFile *pFile,   /* Apply locks to this open shared-memory segment */
+  int lockType,         /* F_UNLCK, F_RDLCK, or F_WRLCK */
+  u8 lockMask           /* Which bytes to lock or unlock */
+){
+  struct flock f;       /* The posix advisory locking structure */
+  int lockOp;           /* The opcode for fcntl() */
+  int i;                /* Offset into the locking byte range */
+  int rc;               /* Result code form fcntl() */
+  u8 mask;              /* Mask of bits in lockMask */
+
+  /* Initialize the locking parameters */
+  memset(&f, 0, sizeof(f));
+  f.l_type = lockType;
+  f.l_whence = SEEK_SET;
+  if( (lockMask & UNIX_SHM_MUTEX)!=0 && lockType!=F_UNLCK ){
+    lockOp = F_SETLKW;
+  }else{
+    lockOp = F_SETLK;
+  }
+
+  /* Find the first bit in lockMask that is set */
+  for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){}
+  assert( mask!=0 );
+  f.l_start = i+UNIX_SHM_BASE;
+  f.l_len = 1;
+
+  /* Extend the locking range for each additional bit that is set */
+  mask <<= 1;
+  while( mask!=0 && (lockMask & mask)!=0 ){
+    f.l_len++;
+  }
+
+  /* Verify that all bits set in lockMask are contiguous */
+  assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 );
+
+  /* Acquire the system-level lock */
+  rc = (fcntl(pFile->h, lockOp, &f)==0) ? SQLITE_OK : SQLITE_BUSY;
+
+  /* Update the global lock state and do debug tracing */
+#ifdef SQLITE_DEBUG
+  OSTRACE(("SHM-LOCK "));
+  if( rc==SQLITE_OK ){
+    if( lockType==F_UNLCK ){
+      OSTRACE(("unlock ok"));
+      pFile->exclMask &= ~lockMask;
+      pFile->sharedMask &= ~lockMask;
+    }else if( lockType==F_RDLCK ){
+      OSTRACE(("read-lock ok"));
+      pFile->exclMask &= ~lockMask;
+      pFile->sharedMask |= lockMask;
+    }else{
+      assert( lockType==F_WRLCK );
+      OSTRACE(("write-lock ok"));
+      pFile->exclMask |= lockMask;
+      pFile->sharedMask &= ~lockMask;
     }
-    if( p->fd.pMethod ){
-      p->fd.pMethod->xClose((sqlite3_file*)&p->fd);
+  }else{
+    if( lockType==F_UNLCK ){
+      OSTRACE(("unlock failed"));
+    }else if( lockType==F_RDLCK ){
+      OSTRACE(("read-lock failed"));
+    }else{
+      assert( lockType==F_WRLCK );
+      OSTRACE(("write-lock failed"));
     }
-    memset(p, 0, sizeof(*p));
-    sqlite3_free(p);
   }
-  return SQLITE_OK;
+  OSTRACE((" - change requested %s - afterwards %s,%s\n",
+           unixShmLockString(lockMask),
+           unixShmLockString(pFile->sharedMask),
+           unixShmLockString(pFile->exclMask)));
+#endif
+
+  return rc;        
 }
 
 /*
-** Size increment by which shared memory grows
+** For connection p, unlock all of the locks identified by the unlockMask
+** parameter.
 */
-#define SQLITE_UNIX_SHM_INCR  4096
+static int unixShmUnlock(
+  unixShmFile *pFile,   /* The underlying shared-memory file */
+  unixShm *p,           /* The connection to be unlocked */
+  u8 unlockMask         /* Mask of locks to be unlocked */
+){
+  int rc;      /* Result code */
+  unixShm *pX; /* For looping over all sibling connections */
+  u8 allMask;  /* Union of locks held by connections other than "p" */
+
+  /* We never try to unlock locks that we do not hold */
+  assert( ((p->exclMask|p->sharedMask) & unlockMask)==unlockMask );
+
+  /* Compute locks held by sibling connections */
+  for(pX=pFile->pFirst; pX; pX=pX->pNext){
+    if( pX==p ) continue;
+    assert( (pX->exclMask & unlockMask)==0 );
+    allMask |= pX->sharedMask;
+  }
+
+  /* Unlock the system-level locks */
+  if( (unlockMask & allMask)!=unlockMask ){
+    rc = unixShmSystemLocks(pFile, F_UNLCK, unlockMask & ~allMask);
+  }else{
+    rc = SQLITE_OK;
+  }
+
+  /* Undo the local locks */
+  if( rc==SQLITE_OK ){
+    p->exclMask &= ~unlockMask;
+    p->sharedMask &= ~unlockMask;
+  } 
+  return rc;
+}
+
+/*
+** Get reader locks for connection p on all locks in the readMask parameter.
+*/
+static int unixShmSharedLock(
+  unixShmFile *pFile,   /* The underlying shared-memory file */
+  unixShm *p,           /* The connection to get the shared locks */
+  u8 readMask           /* Mask of shared locks to be acquired */
+){
+  int rc;        /* Result code */
+  unixShm *pX;   /* For looping over all sibling connections */
+  u8 allShared;  /* Union of locks held by connections other than "p" */
+
+  /* Find out which shared locks are already held by sibling connections.
+  ** If any sibling already holds an exclusive lock, go ahead and return
+  ** SQLITE_BUSY.
+  */
+  for(pX=pFile->pFirst; pX; pX=pX->pNext){
+    if( pX==p ) continue;
+    if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY;
+    allShared |= pX->sharedMask;
+  }
+
+  /* Get shared locks at the system level, if necessary */
+  if( (~allShared) & readMask ){
+    rc = unixShmSystemLocks(pFile, F_RDLCK, readMask);
+  }else{
+    rc = SQLITE_OK;
+  }
+
+  /* Get the local shared locks */
+  if( rc==SQLITE_OK ){
+    p->sharedMask |= readMask;
+  }
+  return rc;
+}
+
+/*
+** For connection p, get an exclusive lock on all locks identified in
+** the writeMask parameter.
+*/
+static int unixShmExclusiveLock(
+  unixShmFile *pFile,    /* The underlying shared-memory file */
+  unixShm *p,            /* The connection to get the exclusive locks */
+  u8 writeMask           /* Mask of exclusive locks to be acquired */
+){
+  int rc;        /* Result code */
+  unixShm *pX;   /* For looping over all sibling connections */
+
+  /* Make sure no sibling connections hold locks that will block this
+  ** lock.  If any do, return SQLITE_BUSY right away.
+  */
+  for(pX=pFile->pFirst; pX; pX=pX->pNext){
+    if( pX==p ) continue;
+    if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY;
+    if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY;
+  }
+
+  /* Get the exclusive locks at the system level.  Then if successful
+  ** also mark the local connection as being locked.
+  */
+  rc = unixShmSystemLocks(pFile, F_WRLCK, writeMask);
+  if( rc==SQLITE_OK ){
+    p->sharedMask &= ~writeMask;
+    p->exclMask |= writeMask;
+  }
+  return rc;
+}
+
+/*
+** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0.
+**
+** This is not a VFS shared-memory method; it is a utility function called
+** by VFS shared-memory methods.
+*/
+static void unixShmPurge(void){
+  unixShmFile **pp;
+  unixShmFile *p;
+  assert( unixMutexHeld() );
+  pp = &unixShmFileList;
+  while( (p = *pp)!=0 ){
+    if( p->nRef==0 ){
+      if( p->mutex ) sqlite3_mutex_free(p->mutex);
+      if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf);
+      if( p->mutexRecov ) sqlite3_mutex_free(p->mutexRecov);
+      if( p->h>=0 ) close(p->h);
+      *pp = p->pNext;
+      sqlite3_free(p);
+    }else{
+      pp = &p->pNext;
+    }
+  }
+}
 
 /*
 ** Open a shared-memory area.  This implementation uses mmapped files.
+**
+** When opening a new shared-memory file, if no other instances of that
+** file are currently open, in this process or in other processes, then
+** the file must be truncated to zero length or have its header cleared.
 */
 static int unixShmOpen(
   sqlite3_vfs *pVfs,    /* The VFS */
@@ -4605,44 +4918,136 @@ static int unixShmOpen(
   sqlite3_shm **pShm    /* Write the unixShm object created here */
 ){
   struct unixShm *p = 0;
+  struct unixShmFile *pFile = 0;
   int rc;
-  int outFlags;
+  struct unixFileId fid;
   struct stat sStat;
 
   p = sqlite3_malloc( sizeof(*p) );
-  *pShm = (sqlite3_shm*)p;
   if( p==0 ) return SQLITE_NOMEM;
   memset(p, 0, sizeof(*p));
-  p->pVfs = pVfs;
-  rc = pVfs->xOpen(pVfs, zName, (sqlite3_file*)&p->fd,
-                   SQLITE_OPEN_READWRITE | SQLITE_OPEN_MAIN_JOURNAL,
-                   &outFlags);
-  if( rc!=SQLITE_OK ) goto shm_open_err;
-
-  rc = fstat(p->fd.h, &sStat);
-  if( rc!=0 ) goto shm_open_err;
-  if( sStat.st_size<SQLITE_UNIX_SHM_INCR ){
-    rc = ftruncate(p->fd.h, SQLITE_UNIX_SHM_INCR);
-    if( rc!=0 ) goto shm_open_err;
-    p->size = SQLITE_UNIX_SHM_INCR;
-  }else{
-    p->size = sStat.st_size;
+
+  unixEnterMutex();
+  rc = stat(zName, &sStat);
+  if( rc==0 ){
+    memset(&fid, 0, sizeof(fid));
+    fid.dev = sStat.st_dev;
+    fid.ino = sStat.st_ino;
+    for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){
+      if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break;
+    }
   }
+  if( pFile==0 ){
+    int nName = strlen(zName);
+    pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 );
+    if( pFile==0 ){
+      rc = SQLITE_NOMEM;
+      goto shm_open_err;
+    }
+    memset(pFile, 0, sizeof(pFile));
+    pFile->zFilename = (char*)&pFile[1];
+    memcpy(pFile->zFilename, zName, nName+1);
+    pFile->h = -1;
+    pFile->pNext = unixShmFileList;
+    unixShmFileList = pFile;
+
+    pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+    if( pFile->mutex==0 ){
+      rc = SQLITE_NOMEM;
+      goto shm_open_err;
+    }
+    pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+    if( pFile->mutexBuf==0 ){
+      rc = SQLITE_NOMEM;
+      goto shm_open_err;
+    }
+    pFile->mutexRecov = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+    if( pFile->mutexRecov==0 ){
+      rc = SQLITE_NOMEM;
+      goto shm_open_err;
+    }
+
+    pFile->h = open(zName, O_CREAT, 0664);
+    if( pFile->h<0 ){
+      rc = SQLITE_CANTOPEN;
+      goto shm_open_err;
+    }
 
-  /* Map the file. */
-  p->pBuf = mmap(0, p->size, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd.h, 0);
-  if( p->pBuf==MAP_FAILED ){
-    rc = SQLITE_IOERR;
-    goto shm_open_err;
+    rc = fstat(pFile->h, &sStat);
+    if( rc ){
+      rc = SQLITE_CANTOPEN;
+      goto shm_open_err;
+    }
+    pFile->fid.dev = sStat.st_dev;
+    pFile->fid.ino = sStat.st_ino;
+    pFile->size = (int)sStat.st_size;
+    pFile->size = (pFile->size/SQLITE_UNIX_SHM_INCR)*SQLITE_UNIX_SHM_INCR;
+    if( pFile->size==0 ){
+      pFile->size = SQLITE_UNIX_SHM_INCR;
+      rc = ftruncate(pFile->h, pFile->size);
+      if( rc ){
+        rc = SQLITE_FULL;
+        goto shm_open_err;
+      }
+    }
   }
+
+  p->pFile = pFile;
+  p->pNext = pFile->pFirst;
+#ifdef SQLITE_DEBUG
+  p->id = pFile->nextShmId++;
+#endif
+  pFile->pFirst = p;
+  pFile->nRef++;
+  *pShm = (sqlite3_shm*)p;
+  unixLeaveMutex();
   return SQLITE_OK;
 
 shm_open_err:
-  unixShmClose((sqlite3_shm*)p);
+  unixShmPurge();
+  sqlite3_free(p);
+  sqlite3_free(pFile);
   *pShm = 0;
+  unixLeaveMutex();
   return rc;
 }
 
+/*
+** Close a connectioon to shared-memory.
+*/
+static int unixShmClose(sqlite3_shm *pSharedMem){
+  unixShm *p;            /* The connection to be closed */
+  unixShmFile *pFile;    /* The underlying shared-memory file */
+  unixShm **pp;          /* For looping over sibling connections */
+  int nRef;              /* Number of connections to pFile */
+
+  p = (struct unixShm*)pSharedMem;
+  pFile = p->pFile;
+
+  /* Verify that the connection being closed holds no locks */
+  assert( p->exclMask==0 );
+  assert( p->sharedMask==0 );
+
+
+  /* Remove connection p from the set of connections associated with pFile */
+  sqlite3_mutex_enter(pFile->mutex);
+  for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
+  *pp = p->pNext;
+  pFile->nRef--;
+  nRef = pFile->nRef;
+
+  /* Free the connection p */
+  sqlite3_free(p);
+  sqlite3_mutex_leave(pFile->mutex);
+
+  /* If pFile->nRef has reached 0, then close the underlying
+  ** shared-memory file, too */
+  if( nRef==0 ){
+    unixShmPurge();
+  }
+  return SQLITE_OK;
+}
+
 /*
 ** Query and/or changes the size of a shared-memory segment.
 ** The reqSize parameter is the new size of the segment, or -1 to
@@ -4661,26 +5066,31 @@ static int unixShmSize(
   int *pNewSize,            /* Write new size here */
   char **ppBuf              /* Write new buffer origin here */
 ){
-  struct unixShm *p = (struct unixShm*)pSharedMem;
+  unixShm *p = (unixShm*)pSharedMem;
+  unixShmFile *pFile = p->pFile;
   int rc = SQLITE_OK;
 
+  sqlite3_mutex_enter(pFile->mutexBuf);
+  sqlite3_mutex_enter(pFile->mutex);
   if( reqSize>=0 ){
     reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR;
     reqSize *= SQLITE_UNIX_SHM_INCR;
-    if( reqSize!=p->size ){
-      munmap(p->pBuf, p->size);
-      rc = ftruncate(p->fd.h, reqSize);
+    if( reqSize!=pFile->size ){
+      if( pFile->pMMapBuf ) munmap(pFile->pMMapBuf, pFile->size);
+      rc = ftruncate(pFile->h, reqSize);
       if( rc ){
-        p->pBuf = 0;
-        p->size = 0;
+        pFile->pMMapBuf = 0;
+        pFile->size = 0;
       }else{
-        p->pBuf = mmap(0, reqSize, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd.h,0);
-        p->size = p->pBuf ? reqSize : 0;
+        pFile->pMMapBuf = mmap(0, reqSize, PROT_READ|PROT_WRITE, MAP_SHARED,
+                               pFile->h, 0);
+        pFile->size = pFile->pMMapBuf ? reqSize : 0;
       }
     }
   }
-  *pNewSize = p->size;
-  *ppBuf = p->pBuf;
+  *pNewSize = pFile->size;
+  *ppBuf = pFile->pMMapBuf;
+  sqlite3_mutex_leave(pFile->mutex);
   return rc;
 }
 
@@ -4689,19 +5099,138 @@ static int unixShmSize(
 ** threads are free to resize it if necessary.
 */
 static int unixShmRelease(sqlite3_shm *pSharedMem){
+  unixShm *p = (unixShm*)pSharedMem;
+  unixShmFile *pFile = p->pFile;
+  sqlite3_mutex_leave(pFile->mutexBuf);  
   return SQLITE_OK;
 }
 
+
+
 /*
-** Create or release a lock on shared memory.
+** Change the lock state for a shared-memory segment.
 */
 static int unixShmLock(
   sqlite3_shm *pSharedMem,   /* Pointer from unixShmOpen() */
-  int desiredLock,           /* The locking state desired */
-  int *pGotLock,             /* The locking state actually obtained */
-  int shouldBlock            /* Block for the lock if true and possible */
+  int desiredLock,           /* One of SQLITE_SHM_xxxxx locking states */
+  int *pGotLock              /* The lock you actually got */
 ){
-  return SQLITE_OK;
+  unixShm *p = (unixShm*)pSharedMem;
+  unixShmFile *pFile = p->pFile;
+  int rc = SQLITE_PROTOCOL;
+
+  /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never
+  ** directly requested; they are side effects from requesting
+  ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively.
+  */
+  assert( desiredLock==SQLITE_SHM_QUERY
+       || desiredLock==SQLITE_SHM_UNLOCK
+       || desiredLock==SQLITE_SHM_READ
+       || desiredLock==SQLITE_SHM_WRITE
+       || desiredLock==SQLITE_SHM_CHECKPOINT
+       || desiredLock==SQLITE_SHM_RECOVER );
+
+  /* Return directly if this is just a lock state query, or if
+  ** the connection is already in the desired locking state.
+  */
+  if( desiredLock==SQLITE_SHM_QUERY
+   || desiredLock==p->lockState
+   || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL)
+  ){
+    *pGotLock = p->lockState;
+    return SQLITE_OK;
+  }
+
+  sqlite3_mutex_enter(pFile->mutex);
+  switch( desiredLock ){
+    case SQLITE_SHM_UNLOCK: {
+      assert( p->lockState!=SQLITE_SHM_RECOVER );
+      unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D);
+      rc = SQLITE_OK;
+      p->lockState = SQLITE_SHM_UNLOCK;
+      break;
+    }
+    case SQLITE_SHM_READ: {
+      if( p->lockState==SQLITE_SHM_UNLOCK ){
+        int nAttempt;
+        rc = SQLITE_BUSY;
+        assert( p->lockState==SQLITE_SHM_UNLOCK );
+        for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){
+          rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B);
+          if( rc==SQLITE_BUSY ){
+            rc = unixShmSharedLock(pFile, p, UNIX_SHM_D);
+            if( rc==SQLITE_OK ){
+              p->lockState = p->readLock = SQLITE_SHM_READ_FULL;
+            }
+          }else{
+            unixShmUnlock(pFile, p, UNIX_SHM_B);
+            p->lockState = p->readLock = SQLITE_SHM_READ;
+          }
+        }
+      }else if( p->lockState==SQLITE_SHM_WRITE ){
+        unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D);
+        p->lockState = p->readLock;
+        rc = SQLITE_OK;
+      }else{
+        assert( p->lockState==SQLITE_SHM_RECOVER );
+        unixShmUnlock(pFile, p, UNIX_SHM_MUTEX);
+        sqlite3_mutex_leave(pFile->mutexRecov);
+        p->lockState = p->readLock;
+        rc = SQLITE_OK;
+      }
+      break;
+    }
+    case SQLITE_SHM_WRITE: {
+      assert( p->lockState==SQLITE_SHM_READ 
+              || p->lockState==SQLITE_SHM_READ_FULL );
+      rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D);
+      if( rc==SQLITE_OK ){
+        p->lockState = SQLITE_SHM_WRITE;
+      }
+      break;
+    }
+    case SQLITE_SHM_CHECKPOINT: {
+      assert( p->lockState==SQLITE_SHM_UNLOCK
+           || p->lockState==SQLITE_SHM_PENDING
+           || p->lockState==SQLITE_SHM_RECOVER );
+      if( p->lockState==SQLITE_SHM_RECOVER ){
+        unixShmUnlock(pFile, p, UNIX_SHM_MUTEX);
+        sqlite3_mutex_leave(pFile->mutexRecov);
+        p->lockState = SQLITE_SHM_CHECKPOINT;
+        rc = SQLITE_OK;
+      }
+      if( p->lockState==SQLITE_SHM_UNLOCK ){
+        rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C);
+        if( rc==SQLITE_OK ){
+          p->lockState = SQLITE_SHM_PENDING;
+        }
+      }
+      if( p->lockState==SQLITE_SHM_PENDING ){
+        rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A);
+        if( rc==SQLITE_OK ){
+          p->lockState = SQLITE_SHM_CHECKPOINT;
+        }
+      }
+      break;
+    }
+    default: {
+      assert( desiredLock==SQLITE_SHM_RECOVER );
+      assert( p->lockState==SQLITE_SHM_READ
+           || p->lockState==SQLITE_SHM_READ_FULL
+           || p->lockState==SQLITE_SHM_CHECKPOINT );
+      sqlite3_mutex_leave(pFile->mutex);
+      sqlite3_mutex_enter(pFile->mutexRecov);
+      sqlite3_mutex_enter(pFile->mutex);
+      rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_MUTEX);
+      if( rc==SQLITE_OK ){
+        p->lockState = SQLITE_SHM_RECOVER;
+      }
+      break;
+    }
+  }
+  sqlite3_mutex_leave(pFile->mutex);
+  *pGotLock = p->lockState;
+  return rc;
 }
 
 /*
index 9f6f1bc967248a95e8a011247c41da955e152dbc..70e7d26a8ca04d17a108ff5935921eb431d412cc 100644 (file)
@@ -848,7 +848,7 @@ struct sqlite3_vfs {
   int (*xShmRelease)(sqlite3_shm*);
   int (*xShmPush)(sqlite3_shm*);
   int (*xShmPull)(sqlite3_shm*);
-  int (*xShmLock)(sqlite3_shm*, int desiredLock, int *gotLock, int shouldBlock);
+  int (*xShmLock)(sqlite3_shm*, int desiredLock, int *gotLock);
   int (*xShmClose)(sqlite3_shm*);
   int (*xShmDelete)(sqlite3_vfs*, const char *zName);
   int (*xRename)(sqlite3_vfs*, const char *zOld, const char *zNew, int dirSync);
@@ -887,7 +887,7 @@ struct sqlite3_vfs {
 ** state.
 */
 #define SQLITE_SHM_UNLOCK       0
-#define SQLITE_SHM_READ_PREFIX  1
+#define SQLITE_SHM_READ         1
 #define SQLITE_SHM_READ_FULL    2
 #define SQLITE_SHM_WRITE        3
 #define SQLITE_SHM_PENDING      4