aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <Dan Kennedy>2024-11-30 20:00:54 +0000
committerdan <Dan Kennedy>2024-11-30 20:00:54 +0000
commita1801314540a16f9cbf64f66f078d5065227b876 (patch)
tree95e4e1a9f6591540524ebfa584ac80d9ec6efe0f /src
parentf6d267491cc0d453b7c6dddc4fdfadaf1285f40b (diff)
downloadsqlite-a1801314540a16f9cbf64f66f078d5065227b876.tar.gz
sqlite-a1801314540a16f9cbf64f66f078d5065227b876.zip
On windows, use a separate handle for each connection for xShmLock() locks.
FossilOrigin-Name: 272d552f126357d7bc16d84f13a4bea823abc6ef7faf90e3cffcedb68210f52a
Diffstat (limited to 'src')
-rw-r--r--src/os_win.c707
1 files changed, 294 insertions, 413 deletions
diff --git a/src/os_win.c b/src/os_win.c
index e7ed66858..82ec15f3b 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -16,11 +16,6 @@
#if SQLITE_OS_WIN /* This file is used for Windows only */
/*
-** TODO: This must not clash with any other SQLITE_OPEN_XXX flag in sqlite3.h.
-*/
-#define WIN32_OPEN_SHM 0x80000000
-
-/*
** Include code that is common to all os_*.c files
*/
#include "os_common.h"
@@ -2552,20 +2547,17 @@ static BOOL winLockFile(
}
/*
-** Lock a region of nByte bytes starting at offset offset of file phFile.
+** Lock a region of nByte bytes starting at offset offset of file hFile.
** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock
** otherwise. If nMs is greater than zero and the lock cannot be obtained
** immediately, block for that many ms before giving up.
**
-** If parameter pMutex is not NULL, then
-**
** This function returns SQLITE_OK if the lock is obtained successfully. If
** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or
** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR.
*/
static int winLockFileTimeout(
HANDLE hFile,
- sqlite3_mutex *pMutex,
DWORD offset,
DWORD nByte,
int bExcl,
@@ -2579,9 +2571,7 @@ static int winLockFileTimeout(
ret = winLockFile(&hFile, flags, offset, 0, nByte, 0);
#else
if( !osIsNT() ){
- sqlite3_mutex_enter(pMutex);
ret = winLockFile(&hFile, flags, offset, 0, nByte, 0);
- sqlite3_mutex_leave(pMutex);
}else{
OVERLAPPED ovlp;
memset(&ovlp, 0, sizeof(OVERLAPPED));
@@ -2595,9 +2585,7 @@ static int winLockFileTimeout(
flags &= ~LOCKFILE_FAIL_IMMEDIATELY;
}
- sqlite3_mutex_enter(pMutex);
ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp);
- sqlite3_mutex_leave(pMutex);
if( !ret && nMs>0 && GetLastError()==ERROR_IO_PENDING ){
DWORD res = WaitForSingleObject(ovlp.hEvent, (DWORD)nMs);
@@ -2618,7 +2606,7 @@ static int winLockFileTimeout(
}
if( nMs>0 ){
- CloseHandle(ovlp.hEvent);
+ osCloseHandle(ovlp.hEvent);
}
}
#endif /* defined(SQLITE_ENABLE_SETLK_TIMEOUT) */
@@ -2660,6 +2648,11 @@ static BOOL winUnlockFile(
#endif
}
+static int winHandleUnlock(HANDLE h, int iOff, int nByte){
+ BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0);
+ return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK);
+}
+
/*****************************************************************************
** The next group of routines implement the I/O methods specified
** by the sqlite3_io_methods object.
@@ -2673,66 +2666,70 @@ static BOOL winUnlockFile(
#endif
/*
-** Move the current position of the file handle passed as the first
-** argument to offset iOffset within the file. If successful, return 0.
-** Otherwise, set pFile->lastErrno and return non-zero.
+** Seek the file handle h to offset nByte of the file.
+**
+** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite
+** error code.
*/
-static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
+static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){
+ int rc = SQLITE_OK; /* Return value */
+
#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
- DWORD lastErrno; /* Value returned by GetLastError() */
-
- OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
+ dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN);
+
/* API oddity: If successful, SetFilePointer() returns a dword
** containing the lower 32-bits of the new file-offset. Or, if it fails,
** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
** whether an error has actually occurred, it is also necessary to call
- ** GetLastError().
- */
- dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
-
- if( (dwRet==INVALID_SET_FILE_POINTER
- && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
- pFile->lastErrno = lastErrno;
- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "winSeekFile", pFile->zPath);
- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
- return 1;
+ ** GetLastError(). */
+ if( dwRet==INVALID_SET_FILE_POINTER ){
+ DWORD lastErrno = osGetLastError();
+ if( lastErrno!=NO_ERROR ){
+ rc = SQLITE_IOERR_SEEK;
+ }
}
-
- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
- return 0;
#else
- /*
- ** Same as above, except that this implementation works for WinRT.
- */
-
+ /* This implementation works for WinRT. */
LARGE_INTEGER x; /* The new offset */
BOOL bRet; /* Value returned by SetFilePointerEx() */
x.QuadPart = iOffset;
- bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
+ bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN);
if(!bRet){
- pFile->lastErrno = osGetLastError();
- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "winSeekFile", pFile->zPath);
- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
- return 1;
+ rc = SQLITE_IOERR_SEEK;
}
-
- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
- return 0;
#endif
+
+ OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc)));
+ return rc;
+}
+
+/*
+** Move the current position of the file handle passed as the first
+** argument to offset iOffset within the file. If successful, return 0.
+** Otherwise, set pFile->lastErrno and return non-zero.
+*/
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
+ int rc;
+
+ rc = winHandleSeek(pFile->h, iOffset);
+ if( rc!=SQLITE_OK ){
+ pFile->lastErrno = osGetLastError();
+ winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath);
+ }
+ return rc;
}
+
#if SQLITE_MAX_MMAP_SIZE>0
/* Forward references to VFS helper methods used for memory mapped files */
static int winMapfile(winFile*, sqlite3_int64);
@@ -2993,6 +2990,52 @@ static int winWrite(
}
/*
+** Truncate the file opened by handle h to nByte bytes in size.
+*/
+static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){
+ int rc = SQLITE_OK; /* Return code */
+ rc = winHandleSeek(h, nByte);
+ if( rc==SQLITE_OK ){
+ if( 0==osSetEndOfFile(h) ){
+ rc = SQLITE_IOERR_TRUNCATE;
+ }
+ }
+ return rc;
+}
+
+/*
+** Determine the size in bytes of the file opened by the handle passed as
+** the first argument.
+*/
+static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){
+ int rc = SQLITE_OK;
+
+#if SQLITE_OS_WINRT
+ FILE_STANDARD_INFO info;
+ BOOL b;
+ b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info));
+ if( b ){
+ *pnByte = info.EndOfFile.QuadPart;
+ }else{
+ rc = SQLITE_IOERR_FSTAT;
+ }
+#else
+ DWORD upperBits = 0;
+ DWORD lowerBits = 0;
+ DWORD lastErrno = 0;
+
+ lowerBits = osGetFileSize(h, &upperBits);
+ *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits;
+
+ if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){
+ rc = SQLITE_IOERR_FSTAT;
+ }
+#endif
+
+ return rc;
+}
+
+/*
** Truncate an open file to a specified size
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
@@ -3854,12 +3897,12 @@ static int winShmMutexHeld(void) {
struct winShmNode {
sqlite3_mutex *mutex; /* Mutex to access this object */
char *zFilename; /* Name of the file */
- winFile hFile; /* File handle from winOpen */
+ HANDLE hSharedShm; /* File handle open on zFilename */
+ int isUnlocked; /* DMS lock has not yet been obtained */
+ int isReadonly; /* True if read-only */
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 */
@@ -3870,38 +3913,12 @@ struct winShmNode {
int nRef; /* Number of winShm objects pointing to this */
winShm *pFirst; /* All winShm objects pointing to this */
winShmNode *pNext; /* Next in list of all winShmNode objects */
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
-#endif
+
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 nextShmId; /* Next available winShm.id value */
#endif
};
-/*
-** Enter/leave the mutex required to modify the winShmNode.pFirst list.
-*/
-static void winShmListMutexEnter(winShmNode *pShmNode){
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- int ii;
- for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
- sqlite3_mutex_enter(pShmNode->aMutex[ii]);
- }
-#else
- sqlite3_mutex_enter(pShmNode->mutex);
-#endif
-}
-static void winShmListMutexLeave(winShmNode *pShmNode){
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- int ii;
- for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
- sqlite3_mutex_leave(pShmNode->aMutex[ii]);
- }
-#else
- sqlite3_mutex_leave(pShmNode->mutex);
-#endif
-}
-
/*
** A global array of all winShmNode objects.
**
@@ -3925,9 +3942,10 @@ static winShmNode *winShmNodeList = 0;
struct winShm {
winShmNode *pShmNode; /* The underlying winShmNode object */
winShm *pNext; /* Next winShm with the same winShmNode */
- u8 hasMutex; /* True if holding the winShmNode mutex */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
+ HANDLE hShm; /* File-handle on *-shm file. For locking. */
+ int bReadonly; /* True if hShm is opened read-only */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 id; /* Id of this connection with its winShmNode */
#endif
@@ -3939,59 +3957,6 @@ struct winShm {
#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
-/*
-** Apply advisory locks for all n bytes beginning at ofst.
-*/
-#define WINSHM_UNLCK 1
-#define WINSHM_RDLCK 2
-#define WINSHM_WRLCK 3
-static int winShmSystemLock(
- winFile *pDbFd, /* Apply locks to this open shared-memory segment */
- int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
- int ofst, /* Offset to first byte to be locked/unlocked */
- int nByte /* Number of bytes to lock or unlock */
-){
- winShmNode *pShmNode = pDbFd->pShm->pShmNode;
- int rc = 0; /* Result code form Lock/UnlockFileEx() */
-
- /* Access to the winShmNode object is serialized by the caller */
- /* assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->mutex) ); */
-
- OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
- pShmNode->hFile.h, lockType, ofst, nByte));
-
- /* Release/Acquire the system-level lock */
- if( lockType==WINSHM_UNLCK ){
- int ret = winUnlockFile(&pShmNode->hFile.h, ofst, 0, nByte, 0);
- if( ret==0 ){
- pShmNode->lastErrno = osGetLastError();
- rc = SQLITE_ERROR;
- }
- }else{
- /* Initialize the locking parameters */
-#if SQLITE_ENABLE_SETLK_TIMEOUT
- rc = winLockFileTimeout(pShmNode->hFile.h, pShmNode->mutex, ofst, nByte,
- (lockType==WINSHM_WRLCK), pDbFd->iBusyTimeout);
-#else
- DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
- if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
- rc = winLockFile(&pShmNode->hFile.h, dwFlags, ofst, 0, nByte, 0);
- if( rc!=0 ){
- rc = SQLITE_OK;
- }else{
- pShmNode->lastErrno = osGetLastError();
- rc = SQLITE_BUSY;
- }
-#endif
- }
-
- OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
- pShmNode->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
- "winLockFile", pShmNode->lastErrno, sqlite3ErrName(rc)));
-
- return rc;
-}
-
/* Forward references to VFS methods */
static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
static int winDelete(sqlite3_vfs *,const char*,int);
@@ -4013,12 +3978,6 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
if( p->nRef==0 ){
int i;
if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- /* Free the contents of the winShmNode.aMutex[] array */
- for(i=0; i<SQLITE_SHM_NLOCK; i++){
- sqlite3_mutex_free(p->aMutex[i]);
- }
-#endif
for(i=0; i<p->nRegion; i++){
BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
@@ -4029,10 +3988,8 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
UNUSED_VARIABLE_VALUE(bRc);
}
- if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
- SimulateIOErrorBenign(1);
- winClose((sqlite3_file *)&p->hFile);
- SimulateIOErrorBenign(0);
+ if( p->hSharedShm!=NULL && p->hSharedShm!=INVALID_HANDLE_VALUE ){
+ osCloseHandle(p->hSharedShm);
}
if( deleteFlag ){
SimulateIOErrorBenign(1);
@@ -4051,43 +4008,118 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
}
/*
-** The DMS lock has not yet been taken on the shm file attached to
-** pDbFd. 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.
+** The DMS lock has not yet been taken on the shm file associated with
+** pShmNode. Take the lock. Truncate the *-shm file if required.
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
*/
-static int winLockSharedMemory(winFile *pDbFd){
- winShmNode *pShmNode = pDbFd->pShm->pShmNode;
- int rc = winShmSystemLock(pDbFd, WINSHM_WRLCK, WIN_SHM_DMS, 1);
+static int winLockSharedMemory(winShmNode *pShmNode){
+ HANDLE h = pShmNode->hSharedShm;
+ int rc = SQLITE_OK;
+ rc = winLockFileTimeout(h, WIN_SHM_DMS, 1, 1, 0);
if( rc==SQLITE_OK ){
+ /* We have an EXCLUSIVE lock on the DMS byte. This means that this
+ ** is the first process to open the file. Truncate it to zero bytes
+ ** in this case. */
if( pShmNode->isReadonly ){
- pShmNode->isUnlocked = 1;
- winShmSystemLock(pDbFd, WINSHM_UNLCK, WIN_SHM_DMS, 1);
- return SQLITE_READONLY_CANTINIT;
- }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
- winShmSystemLock(pDbFd, WINSHM_UNLCK, WIN_SHM_DMS, 1);
- return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
- "winLockSharedMemory", pShmNode->zFilename);
+ rc = SQLITE_READONLY_CANTINIT;
+ }else{
+ rc = winHandleTruncate(h, 0);
}
+
+ /* Release the EXCLUSIVE lock acquired above. */
+ winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
+ }else if( (rc & 0xFF)==SQLITE_BUSY ){
+ rc = SQLITE_OK;
}
if( rc==SQLITE_OK ){
- winShmSystemLock(pDbFd, WINSHM_UNLCK, WIN_SHM_DMS, 1);
+ /* Take a SHARED lock on the DMS byte. */
+ rc = winLockFileTimeout(h, WIN_SHM_DMS, 1, 0, 0);
+ if( rc==SQLITE_OK ){
+ pShmNode->isUnlocked = 0;
+ }
}
- return winShmSystemLock(pDbFd, WINSHM_RDLCK, WIN_SHM_DMS, 1);
+ return rc;
}
+
+/*
+** Convert a UTF-8 filename into whatever form the underlying
+** operating system wants filenames in. Space to hold the result
+** is obtained from malloc and must be freed by the calling
+** function.
+*/
+static void *winConvertFromUtf8Filename(const char *zFilename){
+ void *zConverted = 0;
+ if( osIsNT() ){
+ zConverted = winUtf8ToUnicode(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+
+static int winOpenFile(
+ const char *zUtf8,
+ int *pbReadonly,
+ HANDLE *ph
+){
+ int rc = SQLITE_OK;
+ void *zConverted = 0;
+ int bReadonly = *pbReadonly;
+ HANDLE h = INVALID_HANDLE_VALUE;
+
+ /* Convert the filename to the system encoding. */
+ zConverted = winConvertFromUtf8Filename(zUtf8);
+ if( zConverted==0 ){
+ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8));
+ rc = SQLITE_IOERR_NOMEM_BKPT;
+ goto winopenfile_out;
+ }
+
+ /* Ensure the file we are trying to open is not actually a directory. */
+ if( winIsDir(zConverted) ){
+ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8));
+ rc = SQLITE_CANTOPEN_ISDIR;
+ goto winopenfile_out;
+ }
+
+ /* TODO: platforms.
+ ** TODO: retry-on-ioerr.
+ */
+ h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */
+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */
+ NULL, /* lpSecurityAttributes */
+ OPEN_ALWAYS, /* dwCreationDisposition */
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
+ NULL
+ );
+ if( h==INVALID_HANDLE_VALUE ){
+ if( bReadonly==0 ){
+ bReadonly = 1;
+ rc = winOpenFile(zUtf8, &bReadonly, &h);
+ }else{
+ rc = SQLITE_CANTOPEN_BKPT;
+ }
+ }
+
+ winopenfile_out:
+ sqlite3_free(zConverted);
+ *pbReadonly = bReadonly;
+ *ph = h;
+ return rc;
+}
+
+
/*
** Open the shared-memory area associated with database file pDbFd.
-**
-** 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 winOpenSharedMemory(winFile *pDbFd){
struct winShm *p; /* The connection to be opened */
@@ -4099,8 +4131,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
assert( pDbFd->pShm==0 ); /* Not previously opened */
/* Allocate space for the new sqlite3_shm object. Also speculatively
- ** allocate space for a new winShmNode and filename.
- */
+ ** allocate space for a new winShmNode and filename. */
p = sqlite3MallocZero( sizeof(*p) );
if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
nName = sqlite3Strlen30(pDbFd->zPath);
@@ -4110,105 +4141,74 @@ static int winOpenSharedMemory(winFile *pDbFd){
return SQLITE_IOERR_NOMEM_BKPT;
}
pNew->zFilename = (char*)&pNew[1];
+ pNew->hSharedShm = INVALID_HANDLE_VALUE;
+ pNew->isUnlocked = 1;
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
+ /* Open a file-handle on the *-shm file for this connection. This file-handle
+ ** is only used for locking. The mapping of the *-shm file is created using the
+ ** shared file handle in winShmNode.hSharedShm. */
+ p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
+ rc = winOpenFile(pNew->zFilename, &p->bReadonly, &p->hShm);
+
/* Look to see if there is an existing winShmNode that can be used.
- ** If no matching winShmNode currently exists, create a new one.
- */
+ ** If no matching winShmNode currently exists, then create a new one. */
winShmEnterMutex();
for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
/* TBD need to come up with better match here. Perhaps
- ** use FILE_ID_BOTH_DIR_INFO Structure.
- */
+ ** use FILE_ID_BOTH_DIR_INFO Structure. */
if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
}
- if( pShmNode ){
- sqlite3_free(pNew);
- }else{
- int inFlags = SQLITE_OPEN_WAL | WIN32_OPEN_SHM;
- int outFlags = 0;
-
+ if( pShmNode==0 ){
pShmNode = pNew;
- pNew = 0;
- ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
- pShmNode->pNext = winShmNodeList;
- winShmNodeList = pShmNode;
+ /* Allocate a mutex for this winShmNode object, if one is required. */
if( sqlite3GlobalConfig.bCoreMutex ){
pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
- rc = SQLITE_IOERR_NOMEM_BKPT;
- goto shm_open_err;
- }
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- /* If SETLK_TIMEOUT is defined, also allocate the array of mutexes
- ** stored in pShmNode->aMutex[]. */
- {
- int ii;
- for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
- pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->aMutex[ii]==0 ){
- rc = SQLITE_IOERR_NOMEM_BKPT;
- goto shm_open_err;
- }
- }
- }
-#endif
+ if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT;
}
- if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
- inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
- }else{
- inFlags |= SQLITE_OPEN_READONLY;
+ /* Open a file-handle to use for mappings, and for the DMS lock. */
+ if( rc==SQLITE_OK ){
+ HANDLE h = INVALID_HANDLE_VALUE;
+ pShmNode->isReadonly = p->bReadonly;
+ rc = winOpenFile(pNew->zFilename, &pShmNode->isReadonly, &h);
+ pShmNode->hSharedShm = h;
}
- rc = winOpen(pDbFd->pVfs, pShmNode->zFilename,
- (sqlite3_file*)&pShmNode->hFile,
- inFlags, &outFlags);
- if( rc!=SQLITE_OK ){
- rc = winLogError(rc, osGetLastError(), "winOpenShm",
- pShmNode->zFilename);
- goto shm_open_err;
+
+ /* If successful, link the new winShmNode into the global list. If an
+ ** error occurred, free the object. */
+ if( rc==SQLITE_OK ){
+ pShmNode->pNext = winShmNodeList;
+ winShmNodeList = pShmNode;
+ pNew = 0;
+ }else{
+ sqlite3_mutex_free(pShmNode->mutex);
+ if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){
+ osCloseHandle(pShmNode->hSharedShm);
+ }
}
- if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1;
+ }
+ /* If no error has occurred, link the winShm object to the winShmNode and
+ ** the winShm to pDbFd. */
+ if( rc==SQLITE_OK ){
p->pShmNode = pShmNode;
+ p->pNext = pShmNode->pFirst;
+ pShmNode->pFirst = p;
+ pShmNode->nRef++;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
+ p->id = pShmNode->nextShmId++;
+#endif
pDbFd->pShm = p;
- rc = winLockSharedMemory(pDbFd);
- pDbFd->pShm = 0;
- if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
+ }else{
+ sqlite3_free(p);
}
- /* Make the new connection a child of the winShmNode */
- p->pShmNode = pShmNode;
-#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
- p->id = pShmNode->nextShmId++;
-#endif
- pShmNode->nRef++;
- pDbFd->pShm = p;
+ assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
winShmLeaveMutex();
-
- /* The reference count on pShmNode has already been incremented under
- ** the cover of the winShmEnterMutex() mutex and the pointer from the
- ** new (struct winShm) object to the pShmNode has been set. All that is
- ** left to do is to link the new object into the linked list starting
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
- ** mutex.
- */
- winShmListMutexEnter(pShmNode);
- p->pNext = pShmNode->pFirst;
- pShmNode->pFirst = p;
- winShmListMutexLeave(pShmNode);
- return rc;
-
- /* Jump here on any error */
-shm_open_err:
-
- winUnlockFile(&pShmNode->hFile.h, WIN_SHM_DMS, 0, 1, 0);
- winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */
- sqlite3_free(p);
sqlite3_free(pNew);
- winShmLeaveMutex();
return rc;
}
@@ -4228,29 +4228,31 @@ static int winShmUnmap(
pDbFd = (winFile*)fd;
p = pDbFd->pShm;
if( p==0 ) return SQLITE_OK;
+ if( p->hShm!=INVALID_HANDLE_VALUE ){
+ osCloseHandle(p->hShm);
+ }
+
pShmNode = p->pShmNode;
+ winShmEnterMutex();
/* Remove connection p from the set of connections associated
** with pShmNode */
- winShmListMutexEnter(pShmNode);
for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
*pp = p->pNext;
- winShmListMutexLeave(pShmNode);
-
- /* Free the connection p */
- sqlite3_free(p);
- pDbFd->pShm = 0;
/* If pShmNode->nRef has reached 0, then close the underlying
- ** shared-memory file, too */
- winShmEnterMutex();
+ ** shared-memory file, too. */
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
+ assert( pShmNode->pFirst==0 );
winShmPurge(pDbFd->pVfs, deleteFlag);
}
winShmLeaveMutex();
+ /* Free the connection p */
+ sqlite3_free(p);
+ pDbFd->pShm = 0;
return SQLITE_OK;
}
@@ -4265,7 +4267,6 @@ static int winShmLock(
){
winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */
winShm *p = pDbFd->pShm; /* The shared memory being locked */
- winShm *pX; /* For looping over all siblings */
winShmNode *pShmNode;
int rc = SQLITE_OK; /* Result code */
u16 mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); /* Mask of locks to take/untake */
@@ -4314,8 +4315,7 @@ static int winShmLock(
** c) An exclusive lock where the requested lock is not already held
**
** The SQLite core never requests an exclusive lock that it already holds.
- ** This is assert()ed below.
- */
+ ** This is assert()ed immediately below. */
assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
|| 0==(p->exclMask & mask)
);
@@ -4324,121 +4324,35 @@ static int winShmLock(
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
- /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
- ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
- ** other thread is holding this mutex, then it is either holding or about
- ** to hold a lock exclusive to the one being requested, and we may
- ** therefore return SQLITE_BUSY to the caller.
- **
- ** Doing this prevents some deadlock scenarios. For example, thread 1 may
- ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
- ** may be a normal SQL client upgrading to a write transaction. In this
- ** case thread 2 does a non-blocking request for the WRITER lock. But -
- ** if it were to use sqlite3_mutex_enter() then it would effectively
- ** become a (doomed) blocking request, as thread 2 would block until thread
- ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
- ** a lock on a read-locking slot at this point, this breaks the
- ** anti-deadlock rules (see above). */
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- int iMutex;
- for(iMutex=ofst; iMutex<ofst+n; iMutex++){
- if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
- rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
- if( rc!=SQLITE_OK ) break;
- }else{
- sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
- }
- }
-#else
- sqlite3_mutex_enter(pShmNode->mutex);
-#endif
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
- if( rc==SQLITE_OK ){
- if( flags & SQLITE_SHM_UNLOCK ){
- /* Case (a) - unlock. */
- u16 allMask = 0; /* Mask of locks held by siblings */
-
- assert( (p->exclMask & p->sharedMask)==0 );
- assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
- assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
-
- /* If this is a shared lock, check if any other connection in this
- ** process is holding the same shared lock. If one or more are, then
- ** do not unlock the system-level lock held on the file-handle. */
- if( flags & SQLITE_SHM_SHARED ){
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- allMask |= AtomicLoad(&pX->sharedMask);
- }
- }
- if( (mask & allMask)==0 ){
- rc = winShmSystemLock(pDbFd, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
- }
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- AtomicStore(&p->exclMask, p->exclMask & ~mask);
- AtomicStore(&p->sharedMask, p->sharedMask & ~mask);
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- /* Case (b) - a shared lock. */
- int bLocked = 0; /* True if process already holds shared lock */
- assert( n==1 );
-
- /* See what locks are held by other connections within this process. If
- ** any are holding an EXCLUSIVE lock, then this call will return
- ** SQLITE_BUSY. Or, if any are holding a SHARED lock, then there is no
- ** need to obtain a new system-level lock. */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- u16 mExcl = AtomicLoad(&pX->exclMask);
- u16 mShared = AtomicLoad(&pX->sharedMask);
- if( mExcl & mask ) rc = SQLITE_BUSY;
- if( mShared & mask ){
- bLocked = 1;
- }
- }
+ rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n);
- if( rc==SQLITE_OK && bLocked==0 ){
- rc = winShmSystemLock(pDbFd, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
- }
-
- /* Get the local shared lock */
- if( rc==SQLITE_OK ){
- AtomicStore(&p->sharedMask, p->sharedMask | mask);
- }
- }else{
- /* Case (c) - an exclusive lock. */
- assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
- assert( (p->sharedMask & mask)==0 );
- assert( (p->exclMask & mask)==0 );
-
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away. */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- u16 mExcl = AtomicLoad(&pX->exclMask);
- u16 mShared = AtomicLoad(&pX->sharedMask);
- if( (mExcl|mShared) & mask ) rc = SQLITE_BUSY;
- }
-
- /* Get the exclusive locks at the system level. If successful,
- ** also update the in-memory values. */
- if( rc==SQLITE_OK ){
- rc = winShmSystemLock(pDbFd, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- AtomicStore(&p->exclMask, p->exclMask | mask);
- }
- }
+ /* If successful, also clear the bits in sharedMask/exclMask */
+ if( rc==SQLITE_OK ){
+ p->exclMask = (p->exclMask & ~mask);
+ p->sharedMask = (p->sharedMask & ~mask);
}
- }
-
- /* Release the mutex(es) taken at the top of this block */
+ }else{
+ int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
+ int nMs = 0;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- for(iMutex--; iMutex>=ofst; iMutex--){
- sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
- }
-#else
- sqlite3_mutex_leave(pShmNode->mutex);
+ nMs = pDbFd->iBusyTimeout
#endif
+ rc = winLockFileTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs);
+ if( rc==SQLITE_OK ){
+ if( bExcl ){
+ p->exclMask = (p->exclMask | mask);
+ }else{
+ p->sharedMask = (p->sharedMask | mask);
+ }
+ }
+ }
}
OSTRACE((
@@ -4506,13 +4420,15 @@ static int winShmMap(
sqlite3_mutex_enter(pShmNode->mutex);
if( pShmNode->isUnlocked ){
- rc = winLockSharedMemory(pDbFd);
+ /* Take the DMS lock. */
+ assert( pShmNode->nRegion==0 );
+ rc = winLockSharedMemory(pShmNode);
if( rc!=SQLITE_OK ) goto shmpage_out;
- pShmNode->isUnlocked = 0;
}
- assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
+ assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
if( pShmNode->nRegion<=iRegion ){
+ HANDLE hShared = pShmNode->hSharedShm;
struct ShmRegion *apNew; /* New aRegion[] array */
int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
sqlite3_int64 sz; /* Current size of wal-index file */
@@ -4523,10 +4439,9 @@ static int winShmMap(
** Check to see if it has been allocated (i.e. if the wal-index file is
** large enough to contain the requested region).
*/
- rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
+ rc = winHandleSize(hShared, &sz);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap1", pDbFd->zPath);
+ rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
@@ -4535,19 +4450,17 @@ static int winShmMap(
** zero, exit early. *pp will be set to NULL and SQLITE_OK returned.
**
** Alternatively, if isWrite is non-zero, use ftruncate() to allocate
- ** the requested memory region.
- */
+ ** the requested memory region. */
if( !isWrite ) goto shmpage_out;
- rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
+ rc = winHandleTruncate(hShared, nByte);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap2", pDbFd->zPath);
+ rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
/* Map the requested memory region into this processes address space. */
- apNew = (struct ShmRegion *)sqlite3_realloc64(
+ apNew = (struct ShmRegion*)sqlite3_realloc64(
pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
);
if( !apNew ){
@@ -4566,18 +4479,13 @@ static int winShmMap(
void *pMap = 0; /* Mapped memory region */
#if SQLITE_OS_WINRT
- hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
- NULL, protect, nByte, NULL
- );
+ hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL);
#elif defined(SQLITE_WIN32_HAS_WIDE)
- hMap = osCreateFileMappingW(pShmNode->hFile.h,
- NULL, protect, 0, nByte, NULL
- );
+ hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL);
#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
- hMap = osCreateFileMappingA(pShmNode->hFile.h,
- NULL, protect, 0, nByte, NULL
- );
+ hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL);
#endif
+
OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
osGetCurrentProcessId(), pShmNode->nRegion, nByte,
hMap ? "ok" : "failed"));
@@ -4620,7 +4528,9 @@ shmpage_out:
}else{
*pp = 0;
}
- if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
+ if( pShmNode->isReadonly && rc==SQLITE_OK ){
+ rc = SQLITE_READONLY;
+ }
sqlite3_mutex_leave(pShmNode->mutex);
return rc;
}
@@ -4962,26 +4872,6 @@ static char *winConvertToUtf8Filename(const void *zFilename){
#endif
/*
-** Convert a UTF-8 filename into whatever form the underlying
-** operating system wants filenames in. Space to hold the result
-** is obtained from malloc and must be freed by the calling
-** function.
-*/
-static void *winConvertFromUtf8Filename(const char *zFilename){
- void *zConverted = 0;
- if( osIsNT() ){
- zConverted = winUtf8ToUnicode(zFilename);
- }
-#ifdef SQLITE_WIN32_HAS_ANSI
- else{
- zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
- }
-#endif
- /* caller will handle out of memory */
- return zConverted;
-}
-
-/*
** This function returns non-zero if the specified UTF-8 string buffer
** ends with a directory separator character or one was successfully
** added to it.
@@ -5459,15 +5349,6 @@ static int winOpen(
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
- /* If we are really opening a *-shm file, and ENABLE_SETLK is defined,
- ** open the file for overlapped-IO. This is to facilitate blocking locks
- ** with timeouts, which use asynchronous IO on windows. */
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- if( flags & WIN32_OPEN_SHM ){
- dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
- }
-#endif
-
if( osIsNT() ){
#if SQLITE_OS_WINRT
CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;