]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Initial work on porting the changes on this branch to Win32.
authormistachkin <mistachkin@noemail.net>
Thu, 9 Nov 2017 16:30:55 +0000 (16:30 +0000)
committermistachkin <mistachkin@noemail.net>
Thu, 9 Nov 2017 16:30:55 +0000 (16:30 +0000)
FossilOrigin-Name: 3738bfd0c0eadb10eea58954af5052cb6ce164059f3aacfe65d7da6a400c63c7

manifest
manifest.uuid
src/os_win.c
test/walro2.test
test/walrofault.test

index a22cee602effbaedb265f04624739c87e4567905..dbc82868371563ee9b485f5175691c3b0f252b83 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Extra\scomments\son\sthe\ssqlite3OsShmMap()\scall\sin\swalBeginUnlocked().\s\sNo\nchanges\sto\scode.
-D 2017-11-08T19:26:27.278
+C Initial\swork\son\sporting\sthe\schanges\son\sthis\sbranch\sto\sWin32.
+D 2017-11-09T16:30:55.245
 F Makefile.in 5bae3f2f3d42f2ad52b141562d74872c97ac0fca6c54953c91bb150a0e6427a8
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 3a5cb477ec3ce5274663b693164e349db63348667cd45bad78cc13d580b691e2
@@ -448,7 +448,7 @@ F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
 F src/os_unix.c e87cef0bb894b94d96ee3af210be669549d111c580817d14818101b992640767
-F src/os_win.c 6892c3ff23b7886577e47f13d827ca220c0831bae3ce00eea8c258352692f8c6
+F src/os_win.c 47687775641c97743c228f99813fbcffe7d53602da5cdcf0fe52f6810341a46c
 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
 F src/pager.c 07cf850241667874fcce9d7d924c814305e499b26c804322e2261247b5921903
 F src/pager.h 581698f2177e8bd4008fe4760898ce20b6133d1df22139b9101b5155f900df7a
@@ -1527,8 +1527,8 @@ F test/waloverwrite.test dad2f26567f1b45174e54fbf9a8dc1cb876a7f03
 F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
 F test/walprotocol.test 0b92feb132ccebd855494d917d3f6c2d717ace20
 F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
-F test/walro2.test 2e499d89fa825c9d23b53ed4da8e4dcc7017ea16212d6a4f3aec56d1861eaf8e
-F test/walrofault.test befa889648b2f779e2886f8434d8b44c05c49c130048305977da3e97c33dcb8d
+F test/walro2.test 2c01a3c38ca731df4690cc901e3c0bee21bf9d231fa3d47d81162be10462c40b
+F test/walrofault.test f1c9c361a73faab98ce1bb4588333e42f7dd330f2b4987e962aa5fe68e7982de
 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
 F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
 F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
@@ -1669,7 +1669,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 04974a8b5c0e6748216226006ca9125529c8bb2a7a9df4641217eb1413426a14
-R eba2a72214a6da7d512bda6426cfe5de
-U drh
-Z 493ea33d970c37f0e10aaccf35cd6d47
+P 033ee92bf4d5dc57f5cb8fd02d1154ae06f2d3261d214e7191a82c70c8ffebf7
+R a3370fa9345068426fa8edd8924927fc
+U mistachkin
+Z fbbc6e55aa870fcd83aef8acf0e01375
index a866afde61c9adbedd6d755dbacf51878b4e3bc5..f5c23f13f2f538396357063675a89a2469c770c6 100644 (file)
@@ -1 +1 @@
-033ee92bf4d5dc57f5cb8fd02d1154ae06f2d3261d214e7191a82c70c8ffebf7
\ No newline at end of file
+3738bfd0c0eadb10eea58954af5052cb6ce164059f3aacfe65d7da6a400c63c7
\ No newline at end of file
index 245a86045baeca3bf6bfe4911d53983db19f6720..9bfcfe1c814366822cbc292703ac7f5d8b06cc35 100644 (file)
@@ -2098,6 +2098,17 @@ static int winLogErrorAtLine(
 static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
 static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
 
+/*
+** The "winIsLockingError" macro is used to determine if a particular I/O
+** error code is due to file locking.  It must accept the error code DWORD
+** as its only argument and should return non-zero if the error code is due
+** to file locking.
+*/
+#if !defined(winIsLockingError)
+#define winIsLockingError(a) (((a)==ERROR_LOCK_VIOLATION)       || \
+                              ((a)==ERROR_IO_PENDING))
+#endif
+
 /*
 ** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
 ** error code obtained via GetLastError() is eligible to be retried.  It
@@ -3673,6 +3684,9 @@ struct winShmNode {
 
   int szRegion;              /* Size of shared-memory regions */
   int nRegion;               /* Size of array apRegion */
+  u8 isReadonly;             /* True if read-only */
+  u8 isUnlocked;             /* True if no DMS lock held */
+
   struct ShmRegion {
     HANDLE hMap;             /* File handle from CreateFileMapping */
     void *pMap;
@@ -3820,6 +3834,116 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
   }
 }
 
+/*
+** Query the status of the DMS lock for the specified file.  Returns
+** SQLITE_OK upon success.  Upon success, the integer pointed to by
+** the pLockType argument will be set to the lock type held by the
+** other process, as follows:
+**
+**       WINSHM_UNLCK -- No locks are held on the DMS.
+**       WINSHM_RDLCK -- A SHARED lock is held on the DMS.
+**       WINSHM_WRLCK -- An EXCLUSIVE lock is held on the DMS.
+*/
+static int winGetShmDmsLockType(
+  winFile *pFile, /* File handle object */
+  int *pLockType  /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
+){
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
+  OVERLAPPED overlapped; /* The offset for ReadFile/WriteFile. */
+#endif
+  LPVOID pOverlapped = 0;
+  sqlite3_int64 offset = WIN_SHM_DMS;
+  BYTE notUsed1 = 0;
+  DWORD notUsed2 = 0;
+
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
+  if( winSeekFile(pFile, offset) ){
+    return SQLITE_IOERR_SEEK;
+  }
+#else
+  memset(&overlapped, 0, sizeof(OVERLAPPED));
+  overlapped.Offset = (LONG)(offset & 0xffffffff);
+  overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+  pOverlapped = &overlapped;
+#endif
+  if( !osWriteFile(pFile->h, &notUsed1, 1, &notUsed2, pOverlapped) ){
+    if( !osReadFile(pFile->h, &notUsed1, 1, &notUsed2, pOverlapped) ){
+      if( winIsLockingError(osGetLastError()) ){
+        *pLockType = WINSHM_WRLCK;
+      }else{
+        return SQLITE_IOERR_READ;
+      }
+    }else{
+      if( winIsLockingError(osGetLastError()) ){
+        *pLockType = WINSHM_RDLCK;
+      }else{
+        return SQLITE_IOERR_WRITE;
+      }
+    }
+  }else{
+    *pLockType = WINSHM_UNLCK;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
+** take it now. Return SQLITE_OK if successful, or an SQLite error
+** code otherwise.
+**
+** If the DMS cannot be locked because this is a readonly_shm=1
+** connection and no other process already holds a lock, return
+** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
+*/
+static int winLockSharedMemory(winFile *pDbFd, winShmNode *pShmNode){
+  int lockType;
+  int rc = SQLITE_OK;
+
+  /* Use ReadFile/WriteFile to determine the locks other processes are
+  ** holding on the DMS byte. If it indicates that another process is
+  ** holding a SHARED lock, then this process may also take a SHARED
+  ** lock and proceed with opening the *-shm file.
+  **
+  ** Or, if no other process is holding any lock, then this process
+  ** is the first to open it. In this case take an EXCLUSIVE lock on the
+  ** DMS byte and truncate the *-shm file to zero bytes in size. Then
+  ** downgrade to a SHARED lock on the DMS byte.
+  **
+  ** If another process is holding an EXCLUSIVE lock on the DMS byte,
+  ** return SQLITE_BUSY to the caller (it will try again). An earlier
+  ** version of this code attempted the SHARED lock at this point. But
+  ** this introduced a subtle race condition: if the process holding
+  ** EXCLUSIVE failed just before truncating the *-shm file, then this
+  ** process might open and use the *-shm file without truncating it.
+  ** And if the *-shm file has been corrupted by a power failure or
+  ** system crash, the database itself may also become corrupt.  */
+  if( winGetShmDmsLockType(&pShmNode->hFile, &lockType)!=SQLITE_OK ){
+    rc = SQLITE_IOERR_LOCK;
+  }else if( lockType==WINSHM_UNLCK ){
+    if( pShmNode->isReadonly ){
+      pShmNode->isUnlocked = 1;
+      rc = SQLITE_READONLY_CANTINIT;
+    }else{
+      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
+      rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);
+      if( rc==SQLITE_OK && winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
+        rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
+                         "winLockSharedMemory", pShmNode->zFilename);
+      }
+    }
+  }else if( lockType==WINSHM_WRLCK ){
+    rc = SQLITE_BUSY;
+  }
+
+  if( rc==SQLITE_OK ){
+    assert( lockType==WINSHM_UNLCK || lockType==WINSHM_RDLCK );
+    winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
+    rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
+  }
+
+  return rc;
+}
+
 /*
 ** Open the shared-memory area associated with database file pDbFd.
 **
@@ -3828,11 +3952,12 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
 ** the file must be truncated to zero length or have its header cleared.
 */
 static int winOpenSharedMemory(winFile *pDbFd){
-  struct winShm *p;                  /* The connection to be opened */
-  struct winShmNode *pShmNode = 0;   /* The underlying mmapped file */
-  int rc;                            /* Result code */
-  struct winShmNode *pNew;           /* Newly allocated winShmNode */
-  int nName;                         /* Size of zName in bytes */
+  struct winShm *p;         /* The connection to be opened */
+  winShmNode *pShmNode = 0; /* The underlying mmapped file */
+  int rc = SQLITE_OK;       /* Result code */
+  int rc2 = SQLITE_ERROR;   /* winOpen result code */
+  winShmNode *pNew;         /* Newly allocated winShmNode */
+  int nName;                /* Size of zName in bytes */
 
   assert( pDbFd->pShm==0 );    /* Not previously opened */
 
@@ -3878,30 +4003,29 @@ static int winOpenSharedMemory(winFile *pDbFd){
       }
     }
 
-    rc = winOpen(pDbFd->pVfs,
-                 pShmNode->zFilename,             /* Name of the file (UTF-8) */
-                 (sqlite3_file*)&pShmNode->hFile,  /* File handle here */
-                 SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
-                 0);
-    if( SQLITE_OK!=rc ){
-      goto shm_open_err;
+    if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
+      rc2 = winOpen(pDbFd->pVfs,
+                    pShmNode->zFilename,
+                    (sqlite3_file*)&pShmNode->hFile,
+                    SQLITE_OPEN_WAL|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,
+                    0);
     }
-
-    /* Check to see if another process is holding the dead-man switch.
-    ** If not, truncate the file to zero length.
-    */
-    if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
-      rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
-      if( rc!=SQLITE_OK ){
-        rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
-                         "winOpenShm", pDbFd->zPath);
+    if( rc2!=SQLITE_OK ){
+      rc2 = winOpen(pDbFd->pVfs,
+                    pShmNode->zFilename,
+                    (sqlite3_file*)&pShmNode->hFile,
+                    SQLITE_OPEN_WAL|SQLITE_OPEN_READONLY|SQLITE_OPEN_CREATE,
+                    0);
+      if( rc2!=SQLITE_OK ){
+        rc = winLogError(SQLITE_CANTOPEN_BKPT, osGetLastError(),
+                         "winOpenShm", pShmNode->zFilename);
+        goto shm_open_err;
       }
+      pShmNode->isReadonly = 1;
     }
-    if( rc==SQLITE_OK ){
-      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
-      rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
-    }
-    if( rc ) goto shm_open_err;
+
+    rc = winLockSharedMemory(pDbFd, pShmNode);
+    if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
   }
 
   /* Make the new connection a child of the winShmNode */
@@ -4128,6 +4252,8 @@ static int winShmMap(
   winFile *pDbFd = (winFile*)fd;
   winShm *pShm = pDbFd->pShm;
   winShmNode *pShmNode;
+  DWORD protect = PAGE_READWRITE;
+  DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ;
   int rc = SQLITE_OK;
 
   if( !pShm ){
@@ -4138,6 +4264,11 @@ static int winShmMap(
   pShmNode = pShm->pShmNode;
 
   sqlite3_mutex_enter(pShmNode->mutex);
+  if( pShmNode->isUnlocked ){
+    rc = winLockSharedMemory(pDbFd, pShmNode);
+    if( rc!=SQLITE_OK ) goto shmpage_out;
+    pShmNode->isUnlocked = 0;
+  }
   assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
 
   if( pShmNode->nRegion<=iRegion ){
@@ -4184,21 +4315,26 @@ static int winShmMap(
     }
     pShmNode->aRegion = apNew;
 
+    if( pShmNode->isReadonly ){
+      protect = PAGE_READONLY;
+      flags = FILE_MAP_READ;
+    }
+
     while( pShmNode->nRegion<=iRegion ){
       HANDLE hMap = NULL;         /* file-mapping handle */
       void *pMap = 0;             /* Mapped memory region */
 
 #if SQLITE_OS_WINRT
       hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
-          NULL, PAGE_READWRITE, nByte, NULL
+          NULL, protect, nByte, NULL
       );
 #elif defined(SQLITE_WIN32_HAS_WIDE)
       hMap = osCreateFileMappingW(pShmNode->hFile.h,
-          NULL, PAGE_READWRITE, 0, nByte, NULL
+          NULL, protect, 0, nByte, NULL
       );
 #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
       hMap = osCreateFileMappingA(pShmNode->hFile.h,
-          NULL, PAGE_READWRITE, 0, nByte, NULL
+          NULL, protect, 0, nByte, NULL
       );
 #endif
       OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
@@ -4208,11 +4344,11 @@ static int winShmMap(
         int iOffset = pShmNode->nRegion*szRegion;
         int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
 #if SQLITE_OS_WINRT
-        pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+        pMap = osMapViewOfFileFromApp(hMap, flags,
             iOffset - iOffsetShift, szRegion + iOffsetShift
         );
 #else
-        pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+        pMap = osMapViewOfFile(hMap, flags,
             0, iOffset - iOffsetShift, szRegion + iOffsetShift
         );
 #endif
@@ -4243,6 +4379,7 @@ shmpage_out:
   }else{
     *pp = 0;
   }
+  if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
   sqlite3_mutex_leave(pShmNode->mutex);
   return rc;
 }
index 5a8ce023ebd6f646c1d1262a84a732028c8f3220..4b90618fc6a2ef60e9cb97192e0df07edc7994bd 100644 (file)
@@ -18,13 +18,6 @@ source $testdir/lock_common.tcl
 source $testdir/wal_common.tcl
 set ::testprefix walro2
 
-# These tests are only going to work on unix.
-#
-if {$::tcl_platform(platform) != "unix"} {
-  finish_test
-  return
-}
-
 # And only if the build is WAL-capable.
 #
 ifcapable !wal {
index b396be218ce36ba88e7f2ced8b042b1a7f085ce5..22d4f96bbcbaa380c927c03f04f5b0f9632a3a95 100644 (file)
@@ -17,13 +17,6 @@ source $testdir/tester.tcl
 source $testdir/malloc_common.tcl
 set ::testprefix walro2
 
-# These tests are only going to work on unix.
-#
-if {$::tcl_platform(platform) != "unix"} {
-  finish_test
-  return
-}
-
 # And only if the build is WAL-capable.
 #
 ifcapable !wal {