# define FormatMessageW(a,b,c,d,e,f,g) 0
#endif
++/* Forward references */
++typedef struct winShm winShm; /* A connection to shared-memory */
++typedef struct winShmNode winShmNode; /* A region of shared-memory */
++
/*
** WinCE lacks native support for file locking so we have to fake it
** with some code of our own.
*/
typedef struct winFile winFile;
struct winFile {
-- const sqlite3_io_methods *pMethod;/* Must be first */
++ const sqlite3_io_methods *pMethod; /*** Must be first ***/
++ sqlite3_vfs *pVfs; /* The VFS used to open this file */
HANDLE h; /* Handle for accessing the file */
unsigned char locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
DWORD lastErrno; /* The Windows errno from the last I/O error */
DWORD sectorSize; /* Sector size of the device file is on */
++ winShm *pShm; /* Instance of shared memory on this file */
++ const char *zPath; /* Full pathname of this file */
#if SQLITE_OS_WINCE
WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
HANDLE hMutex; /* Mutex used to control access to shared lock */
winFile *pFile = (winFile*)id;
assert( id!=0 );
++ assert( pFile->pShm==0 );
+ OSTRACE(("CLOSE %d\n", pFile->h));
do{
rc = CloseHandle(pFile->h);
}while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff);
LONG lowerBits = (LONG)(nByte & 0xffffffff);
- DWORD rc;
+ DWORD dwRet;
winFile *pFile = (winFile*)id;
DWORD error;
+ int rc = SQLITE_OK;
assert( id!=0 );
+ OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
- rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
- if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
+ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+ if( dwRet==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){
pFile->lastErrno = error;
- return SQLITE_IOERR_TRUNCATE;
- }
+ rc = SQLITE_IOERR_TRUNCATE;
/* SetEndOfFile will fail if nByte is negative */
- if( !SetEndOfFile(pFile->h) ){
+ }else if( !SetEndOfFile(pFile->h) ){
pFile->lastErrno = GetLastError();
- return SQLITE_IOERR_TRUNCATE;
+ rc = SQLITE_IOERR_TRUNCATE;
}
- return SQLITE_OK;
+ OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc==SQLITE_OK ? "ok" : "failed"));
+ return rc;
}
#ifdef SQLITE_TEST
return 0;
}
- /*
- ** This vector defines all the methods that can operate on an
- ** sqlite3_file for win32.
++/****************************************************************************
++********************************* Shared Memory *****************************
++**
++** The next subdivision of code manages the shared-memory primitives.
+*/
- static const sqlite3_io_methods winIoMethod = {
- 1, /* iVersion */
- winClose,
- winRead,
- winWrite,
- winTruncate,
- winSync,
- winFileSize,
- winLock,
- winUnlock,
- winCheckReservedLock,
- winFileControl,
- winSectorSize,
- winDeviceCharacteristics
- };
++#ifndef SQLITE_OMIT_WAL
+
- /***************************************************************************
- ** Here ends the I/O methods that form the sqlite3_io_methods object.
++/*
++** Helper functions to obtain and relinquish the global mutex. The
++** global mutex is used to protect the winLockInfo objects used by
++** this file, all of which may be shared by multiple threads.
+**
- ** The next block of code implements the VFS methods.
- ****************************************************************************/
++** Function winShmMutexHeld() is used to assert() that the global mutex
++** is held when required. This function is only used as part of assert()
++** statements. e.g.
++**
++** winShmEnterMutex()
++** assert( winShmMutexHeld() );
++** winEnterLeave()
++*/
++static void winShmEnterMutex(void){
++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++}
++static void winShmLeaveMutex(void){
++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++}
++#ifdef SQLITE_DEBUG
++static int winShmMutexHeld(void) {
++ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++}
++#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 *convertUtf8Filename(const char *zFilename){
- void *zConverted = 0;
- if( isNT() ){
- zConverted = utf8ToUnicode(zFilename);
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
++** 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 winFile object, but they all
++** point to a single instance of this object. In other words, each
++** log-summary is opened only once per process.
++**
++** winShmMutexHeld() 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 winShmNode.mutex must be held or winShmNode.nRef==0 and
++** winShmMutexHeld() is true when reading or writing any other field
++** in this structure.
++**
++** To avoid deadlocks, mutex and mutexBuf are always released in the
++** reverse order that they are acquired. mutexBuf is always acquired
++** first and released last. This invariant is check by asserting
++** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or
++** released.
+*/
- #if SQLITE_OS_WINCE==0
- }else{
- zConverted = utf8ToMbcs(zFilename);
++struct winShmNode {
++ sqlite3_mutex *mutex; /* Mutex to access this object */
++ sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */
++ char *zFilename; /* Name of the file */
++ winFile hFile; /* File handle from winOpen */
++ HANDLE hMap; /* File handle from CreateFileMapping */
++ DWORD lastErrno; /* The Windows errno from the last I/O error */
++ int szMap; /* Size of the mapping of file into memory */
++ char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */
++ 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_DEBUG
++ u8 exclMask; /* Mask of exclusive locks held */
++ u8 sharedMask; /* Mask of shared locks held */
++ u8 nextShmId; /* Next available winShm.id value */
+#endif
- }
- /* caller will handle out of memory */
- return zConverted;
- }
++};
+
+/*
- ** Create a temporary file name in zBuf. zBuf must be big enough to
- ** hold at pVfs->mxPathname characters.
++** A global array of all winShmNode objects.
++**
++** The winShmMutexHeld() must be true while reading or writing this list.
+*/
- static int getTempname(int nBuf, char *zBuf){
- static char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- size_t i, j;
- char zTempPath[MAX_PATH+1];
- if( sqlite3_temp_directory ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
- }else if( isNT() ){
- char *zMulti;
- WCHAR zWidePath[MAX_PATH];
- GetTempPathW(MAX_PATH-30, zWidePath);
- zMulti = unicodeToUtf8(zWidePath);
- if( zMulti ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
- free(zMulti);
- }else{
- return SQLITE_NOMEM;
- }
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
++static winShmNode *winShmNodeList = 0;
++
++/*
++** Structure used internally by this VFS to record the state of an
++** open shared memory connection.
++**
++** winShm.pFile->mutex must be held while reading or writing the
++** winShm.pNext and winShm.locks[] elements.
++**
++** The winShm.pFile element is initialized when the object is created
++** and is read-only thereafter.
+*/
- #if SQLITE_OS_WINCE==0
- }else{
- char *zUtf8;
- char zMbcsPath[MAX_PATH];
- GetTempPathA(MAX_PATH-30, zMbcsPath);
- zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
- if( zUtf8 ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
- free(zUtf8);
- }else{
- return SQLITE_NOMEM;
- }
++struct winShm {
++ winShmNode *pShmNode; /* The underlying winShmNode object */
++ winShm *pNext; /* Next winShm with the same winShmNode */
++ u8 lockState; /* Current lock state */
++ u8 hasMutex; /* True if holding the winShmNode mutex */
++ u8 hasMutexBuf; /* True if holding pFile->mutexBuf */
++ 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 winShmNode */
+#endif
- }
- for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
- zTempPath[i] = 0;
- sqlite3_snprintf(nBuf-30, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
- j = sqlite3Strlen30(zBuf);
- sqlite3_randomness(20, &zBuf[j]);
- for(i=0; i<20; i++, j++){
- zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
- OSTRACE2("TEMP FILENAME: %s\n", zBuf);
- return SQLITE_OK;
- }
++};
+
+/*
- ** The return value of getLastErrorMsg
- ** is zero if the error message fits in the buffer, or non-zero
- ** otherwise (if the message was truncated).
++** Size increment by which shared memory grows
+*/
- static int getLastErrorMsg(int nBuf, char *zBuf){
- /* FormatMessage returns 0 on failure. Otherwise it
- ** returns the number of TCHARs written to the output
- ** buffer, excluding the terminating null char.
- */
- DWORD error = GetLastError();
- DWORD dwLen = 0;
- char *zOut = 0;
++#define SQLITE_WIN_SHM_INCR 4096
+
- if( isNT() ){
- WCHAR *zTempWide = NULL;
- dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPWSTR) &zTempWide,
- 0,
- 0);
- if( dwLen > 0 ){
- /* allocate a buffer and convert to UTF8 */
- zOut = unicodeToUtf8(zTempWide);
- /* free the system buffer allocated by FormatMessage */
- LocalFree(zTempWide);
- }
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
++/*
++** Constants used for locking
+*/
- #if SQLITE_OS_WINCE==0
- }else{
- char *zTemp = NULL;
- dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPSTR) &zTemp,
- 0,
- 0);
- if( dwLen > 0 ){
- /* allocate a buffer and convert to UTF8 */
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
- /* free the system buffer allocated by FormatMessage */
- LocalFree(zTemp);
- }
- #endif
- }
- if( 0 == dwLen ){
- sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
- }else{
- /* copy a maximum of nBuf chars to output buffer */
- sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
- /* free the UTF8 buffer */
- free(zOut);
- }
- return 0;
- }
++#define WIN_SHM_BASE 32 /* Byte offset of the first lock byte */
++#define WIN_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */
++#define WIN_SHM_A 0x10 /* Mask for region locks... */
++#define WIN_SHM_B 0x20
++#define WIN_SHM_C 0x40
++#define WIN_SHM_D 0x80
+
++#ifdef SQLITE_DEBUG
+/*
- ** Open a file.
++** 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 int winOpen(
- sqlite3_vfs *pVfs, /* Not used */
- const char *zName, /* Name of the file (UTF-8) */
- sqlite3_file *id, /* Write the SQLite file handle here */
- int flags, /* Open mode flags */
- int *pOutFlags /* Status return flags */
- ){
- HANDLE h;
- DWORD dwDesiredAccess;
- DWORD dwShareMode;
- DWORD dwCreationDisposition;
- DWORD dwFlagsAndAttributes = 0;
- #if SQLITE_OS_WINCE
- int isTemp = 0;
- #endif
- winFile *pFile = (winFile*)id;
- void *zConverted; /* Filename in OS encoding */
- const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
- char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
-
- assert( id!=0 );
- UNUSED_PARAMETER(pVfs);
-
- pFile->h = INVALID_HANDLE_VALUE;
-
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
- */
- if( !zUtf8Name ){
- int rc = getTempname(MAX_PATH+1, zTmpname);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- zUtf8Name = zTmpname;
- }
++static const char *winShmLockString(u8 mask){
++ static char zBuf[48];
++ static int iBuf = 0;
++ char *z;
+
- /* Convert the filename to the system encoding. */
- zConverted = convertUtf8Filename(zUtf8Name);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
++ z = &zBuf[iBuf];
++ iBuf += 8;
++ if( iBuf>=sizeof(zBuf) ) iBuf = 0;
+
- if( flags & SQLITE_OPEN_READWRITE ){
- dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
- }else{
- dwDesiredAccess = GENERIC_READ;
- }
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
- ** as it is usually understood.
- */
- assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE));
- if( flags & SQLITE_OPEN_EXCLUSIVE ){
- /* Creates a new file, only if it does not already exist. */
- /* If the file exists, it fails. */
- dwCreationDisposition = CREATE_NEW;
- }else if( flags & SQLITE_OPEN_CREATE ){
- /* Open existing file, or create if it doesn't exist */
- dwCreationDisposition = OPEN_ALWAYS;
- }else{
- /* Opens a file, only if it exists. */
- dwCreationDisposition = OPEN_EXISTING;
- }
- dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
- if( flags & SQLITE_OPEN_DELETEONCLOSE ){
- #if SQLITE_OS_WINCE
- dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
- isTemp = 1;
- #else
- dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
- | FILE_ATTRIBUTE_HIDDEN
- | FILE_FLAG_DELETE_ON_CLOSE;
- #endif
- }else{
- dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
- }
- /* Reports from the internet are that performance is always
- ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */
- #if SQLITE_OS_WINCE
- dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
- #endif
- if( isNT() ){
- h = CreateFileW((WCHAR*)zConverted,
- dwDesiredAccess,
- dwShareMode,
- NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL
- );
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
- */
- #if SQLITE_OS_WINCE==0
- }else{
- h = CreateFileA((char*)zConverted,
- dwDesiredAccess,
- dwShareMode,
- NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL
- );
- #endif
- }
- OSTRACE(("OPEN %d %s 0x%lx %s\n",
- h, zName, dwDesiredAccess,
- h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
- if( h==INVALID_HANDLE_VALUE ){
- free(zConverted);
- if( flags & SQLITE_OPEN_READWRITE ){
- return winOpen(pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
- }else{
- return SQLITE_CANTOPEN_BKPT;
- }
- }
- if( pOutFlags ){
- if( flags & SQLITE_OPEN_READWRITE ){
- *pOutFlags = SQLITE_OPEN_READWRITE;
- }else{
- *pOutFlags = SQLITE_OPEN_READONLY;
- }
- }
- memset(pFile, 0, sizeof(*pFile));
- pFile->pMethod = &winIoMethod;
- pFile->h = h;
- pFile->lastErrno = NO_ERROR;
- pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
- #if SQLITE_OS_WINCE
- if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
- (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
- && !winceCreateLock(zName, pFile)
- ){
- CloseHandle(h);
- free(zConverted);
- return SQLITE_CANTOPEN_BKPT;
- }
- if( isTemp ){
- pFile->zDeleteOnClose = zConverted;
- }else
- #endif
- {
- free(zConverted);
- }
- OpenCounter(+1);
- return SQLITE_OK;
++ z[0] = (mask & WIN_SHM_DMS) ? 'S' : '.';
++ z[1] = (mask & WIN_SHM_A) ? 'A' : '.';
++ z[2] = (mask & WIN_SHM_B) ? 'B' : '.';
++ z[3] = (mask & WIN_SHM_C) ? 'C' : '.';
++ z[4] = (mask & WIN_SHM_D) ? 'D' : '.';
++ z[5] = 0;
++ return z;
+}
++#endif /* SQLITE_DEBUG */
+
+/*
- ** Delete the named file.
++** Apply posix advisory locks for all bytes identified in lockMask.
+**
- ** Note that windows does not allow a file to be deleted if some other
- ** process has it open. Sometimes a virus scanner or indexing program
- ** will open a journal file shortly after it is created in order to do
- ** whatever it does. While this other process is holding the
- ** file open, we will be unable to delete it. To work around this
- ** problem, we delay 100 milliseconds and try to delete again. Up
- ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
- ** up and returning an error.
++** lockMask might contain multiple bits but all bits are guaranteed
++** to be contiguous.
++**
++** Locks block if the mask is exactly WIN_SHM_C and are non-blocking
++** otherwise.
+*/
- #define MX_DELETION_ATTEMPTS 5
- static int winDelete(
- sqlite3_vfs *pVfs, /* Not used on win32 */
- const char *zFilename, /* Name of file to delete */
- int syncDir /* Not used on win32 */
++#define _SHM_UNLCK 1
++#define _SHM_RDLCK 2
++#define _SHM_WRLCK 3
++static int winShmSystemLock(
++ winShmNode *pFile, /* Apply locks to this open shared-memory segment */
++ int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */
++ u8 lockMask /* Which bytes to lock or unlock */
+){
- int cnt = 0;
- DWORD rc;
- DWORD error = 0;
- void *zConverted = convertUtf8Filename(zFilename);
- UNUSED_PARAMETER(pVfs);
- UNUSED_PARAMETER(syncDir);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- SimulateIOError(return SQLITE_IOERR_DELETE);
- if( isNT() ){
- do{
- DeleteFileW(zConverted);
- }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
- || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
- && (++cnt < MX_DELETION_ATTEMPTS)
- && (Sleep(100), 1) );
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
- */
- #if SQLITE_OS_WINCE==0
- }else{
- do{
- DeleteFileA(zConverted);
- }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
- || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
- && (++cnt < MX_DELETION_ATTEMPTS)
- && (Sleep(100), 1) );
- #endif
- }
- free(zConverted);
- OSTRACE(("DELETE \"%s\" %s\n", zFilename, ( (rc == INVALID_FILE_ATTRIBUTES)
- && (error == ERROR_FILE_NOT_FOUND)) ? "ok" : "failed" ));
- return ( (rc == INVALID_FILE_ATTRIBUTES)
- && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE;
- }
++ OVERLAPPED ovlp;
++ DWORD dwFlags;
++ int nBytes; /* Number of bytes to lock */
++ int i; /* Offset into the locking byte range */
++ int rc = 0; /* Result code form Lock/UnlockFileEx() */
++ u8 mask; /* Mask of bits in lockMask */
+
- /*
- ** Check the existance and status of a file.
- */
- static int winAccess(
- sqlite3_vfs *pVfs, /* Not used on win32 */
- const char *zFilename, /* Name of file to check */
- int flags, /* Type of test to make on this file */
- int *pResOut /* OUT: Result */
- ){
- DWORD attr;
- int rc = 0;
- void *zConverted = convertUtf8Filename(zFilename);
- UNUSED_PARAMETER(pVfs);
- if( zConverted==0 ){
- return SQLITE_NOMEM;
- }
- if( isNT() ){
- attr = GetFileAttributesW((WCHAR*)zConverted);
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
- */
- #if SQLITE_OS_WINCE==0
++ /* Access to the winShmNode object is serialized by the caller */
++ assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
++
++ /* Initialize the locking parameters */
++ if( lockMask==WIN_SHM_C && lockType!=_SHM_UNLCK ){
++ dwFlags = 0;
++ OSTRACE(("SHM-LOCK %d requesting blocking lock %s\n",
++ pFile->hFile.h,
++ winShmLockString(lockMask)));
+ }else{
- attr = GetFileAttributesA((char*)zConverted);
- #endif
- }
- free(zConverted);
- switch( flags ){
- case SQLITE_ACCESS_READ:
- case SQLITE_ACCESS_EXISTS:
- rc = attr!=INVALID_FILE_ATTRIBUTES;
- break;
- case SQLITE_ACCESS_READWRITE:
- rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
- break;
- default:
- assert(!"Invalid flags argument");
++ dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
++ OSTRACE(("SHM-LOCK %d requesting %s %s\n",
++ pFile->hFile.h,
++ lockType!=_SHM_UNLCK ? "lock" : "unlock",
++ winShmLockString(lockMask)));
+ }
- *pResOut = rc;
- return SQLITE_OK;
- }
++ if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
+
++ /* 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 );
++ memset(&ovlp, 0, sizeof(OVERLAPPED));
++ ovlp.Offset = i+WIN_SHM_BASE;
++ nBytes = 1;
+
- /*
- ** Turn a relative pathname into a full pathname. Write the full
- ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
- ** bytes in size.
- */
- static int winFullPathname(
- sqlite3_vfs *pVfs, /* Pointer to vfs object */
- const char *zRelative, /* Possibly relative input path */
- int nFull, /* Size of output buffer in bytes */
- char *zFull /* Output buffer */
- ){
-
- #if defined(__CYGWIN__)
- UNUSED_PARAMETER(nFull);
- cygwin_conv_to_full_win32_path(zRelative, zFull);
- return SQLITE_OK;
- #endif
++ /* Extend the locking range for each additional bit that is set */
++ mask <<= 1;
++ while( mask!=0 && (lockMask & mask)!=0 ){
++ nBytes++;
++ mask <<= 1;
++ }
+
- #if SQLITE_OS_WINCE
- UNUSED_PARAMETER(nFull);
- /* WinCE has no concept of a relative pathname, or so I am told. */
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
- return SQLITE_OK;
- #endif
++ /* Verify that all bits set in lockMask are contiguous */
++ assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 );
+
- #if !SQLITE_OS_WINCE && !defined(__CYGWIN__)
- int nByte;
- void *zConverted;
- char *zOut;
- UNUSED_PARAMETER(nFull);
- zConverted = convertUtf8Filename(zRelative);
- if( isNT() ){
- WCHAR *zTemp;
- nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
- zTemp = malloc( nByte*sizeof(zTemp[0]) );
- if( zTemp==0 ){
- free(zConverted);
- return SQLITE_NOMEM;
- }
- GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
- free(zConverted);
- zOut = unicodeToUtf8(zTemp);
- free(zTemp);
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
- */
- #if SQLITE_OS_WINCE==0
++ /* Release/Acquire the system-level lock */
++ if( lockType==_SHM_UNLCK ){
++ for(i=0; i<nBytes; i++, ovlp.Offset++){
++ rc = UnlockFileEx(pFile->hFile.h, 0, 1, 0, &ovlp);
++ if( !rc ) break;
++ }
+ }else{
- char *zTemp;
- nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
- zTemp = malloc( nByte*sizeof(zTemp[0]) );
- if( zTemp==0 ){
- free(zConverted);
- return SQLITE_NOMEM;
++ /* release old individual byte locks (if any)
++ ** and set new individual byte locks */
++ for(i=0; i<nBytes; i++, ovlp.Offset++){
++ UnlockFileEx(pFile->hFile.h, 0, 1, 0, &ovlp);
++ rc = LockFileEx(pFile->hFile.h, dwFlags, 0, 1, 0, &ovlp);
++ if( !rc ) break;
+ }
- GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
- free(zConverted);
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
- free(zTemp);
- #endif
+ }
- if( zOut ){
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
- free(zOut);
- return SQLITE_OK;
++ if( !rc ){
++ OSTRACE(("SHM-LOCK %d %s ERROR 0x%08lx\n",
++ pFile->hFile.h,
++ lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx",
++ GetLastError()));
++ /* release individual byte locks (if any) */
++ ovlp.Offset-=i;
++ for(i=0; i<nBytes; i++, ovlp.Offset++){
++ UnlockFileEx(pFile->hFile.h, 0, 1, 0, &ovlp);
++ }
++ }
++ rc = (rc!=0) ? SQLITE_OK : SQLITE_BUSY;
++
++ /* Update the global lock state and do debug tracing */
++#ifdef SQLITE_DEBUG
++ OSTRACE(("SHM-LOCK %d ", pFile->hFile.h));
++ if( rc==SQLITE_OK ){
++ if( lockType==_SHM_UNLCK ){
++ OSTRACE(("unlock ok"));
++ pFile->exclMask &= ~lockMask;
++ pFile->sharedMask &= ~lockMask;
++ }else if( lockType==_SHM_RDLCK ){
++ OSTRACE(("read-lock ok"));
++ pFile->exclMask &= ~lockMask;
++ pFile->sharedMask |= lockMask;
++ }else{
++ assert( lockType==_SHM_WRLCK );
++ OSTRACE(("write-lock ok"));
++ pFile->exclMask |= lockMask;
++ pFile->sharedMask &= ~lockMask;
++ }
+ }else{
- return SQLITE_NOMEM;
++ if( lockType==_SHM_UNLCK ){
++ OSTRACE(("unlock failed"));
++ }else if( lockType==_SHM_RDLCK ){
++ OSTRACE(("read-lock failed"));
++ }else{
++ assert( lockType==_SHM_WRLCK );
++ OSTRACE(("write-lock failed"));
++ }
+ }
++ OSTRACE((" - change requested %s - afterwards %s:%s\n",
++ winShmLockString(lockMask),
++ winShmLockString(pFile->sharedMask),
++ winShmLockString(pFile->exclMask)));
+#endif
++
++ return rc;
+}
+
+/*
- ** Get the sector size of the device used to store
- ** file.
++** For connection p, unlock all of the locks identified by the unlockMask
++** parameter.
+*/
- static int getSectorSize(
- sqlite3_vfs *pVfs,
- const char *zRelative /* UTF-8 file name */
++static int winShmUnlock(
++ winShmNode *pFile, /* The underlying shared-memory file */
++ winShm *p, /* The connection to be unlocked */
++ u8 unlockMask /* Mask of locks to be unlocked */
+){
- DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
- /* GetDiskFreeSpace is not supported under WINCE */
- #if SQLITE_OS_WINCE
- UNUSED_PARAMETER(pVfs);
- UNUSED_PARAMETER(zRelative);
- #else
- char zFullpath[MAX_PATH+1];
- int rc;
- DWORD dwRet = 0;
- DWORD dwDummy;
++ int rc; /* Result code */
++ winShm *pX; /* For looping over all sibling connections */
++ u8 allMask; /* Union of locks held by connections other than "p" */
+
- /*
- ** We need to get the full path name of the file
- ** to get the drive letter to look up the sector
- ** size.
- */
- rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
- if( rc == SQLITE_OK )
- {
- void *zConverted = convertUtf8Filename(zFullpath);
- if( zConverted ){
- if( isNT() ){
- /* trim path to just drive reference */
- WCHAR *p = zConverted;
- for(;*p;p++){
- if( *p == '\\' ){
- *p = '\0';
- break;
- }
- }
- dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
- &dwDummy,
- &bytesPerSector,
- &dwDummy,
- &dwDummy);
- }else{
- /* trim path to just drive reference */
- char *p = (char *)zConverted;
- for(;*p;p++){
- if( *p == '\\' ){
- *p = '\0';
- break;
- }
- }
- dwRet = GetDiskFreeSpaceA((char*)zConverted,
- &dwDummy,
- &bytesPerSector,
- &dwDummy,
- &dwDummy);
- }
- free(zConverted);
- }
- if( !dwRet ){
- bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
- }
++ /* Access to the winShmNode object is serialized by the caller */
++ assert( sqlite3_mutex_held(pFile->mutex) );
++
++ /* don't attempt to unlock anything we don't have locks for */
++ if( (unlockMask & (p->exclMask|p->sharedMask)) != unlockMask ){
++ OSTRACE(("SHM-LOCK %d unlocking more than we have locked - requested %s - have %s\n",
++ pFile->hFile.h,
++ winShmLockString(unlockMask),
++ winShmLockString(p->exclMask|p->sharedMask)));
++ unlockMask &= (p->exclMask|p->sharedMask);
+ }
- #endif
- return (int) bytesPerSector;
- }
+
- #ifndef SQLITE_OMIT_LOAD_EXTENSION
- /*
- ** Interfaces for opening a shared library, finding entry points
- ** within the shared library, and closing the shared library.
- */
- /*
- ** Interfaces for opening a shared library, finding entry points
- ** within the shared library, and closing the shared library.
- */
- static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
- HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
- UNUSED_PARAMETER(pVfs);
- if( zConverted==0 ){
- return 0;
++ /* Compute locks held by sibling connections */
++ allMask = 0;
++ for(pX=pFile->pFirst; pX; pX=pX->pNext){
++ if( pX==p ) continue;
++ assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
++ allMask |= pX->sharedMask;
+ }
- if( isNT() ){
- h = LoadLibraryW((WCHAR*)zConverted);
- /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
- ** Since the ASCII version of these Windows API do not exist for WINCE,
- ** it's important to not reference them for WINCE builds.
- */
- #if SQLITE_OS_WINCE==0
++
++ /* Unlock the system-level locks */
++ if( (unlockMask & allMask)!=unlockMask ){
++ rc = winShmSystemLock(pFile, _SHM_UNLCK, unlockMask & ~allMask);
+ }else{
- h = LoadLibraryA((char*)zConverted);
- #endif
++ rc = SQLITE_OK;
+ }
- free(zConverted);
- return (void*)h;
- }
- static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
- UNUSED_PARAMETER(pVfs);
- getLastErrorMsg(nBuf, zBufOut);
- }
- void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
- UNUSED_PARAMETER(pVfs);
- #if SQLITE_OS_WINCE
- /* The GetProcAddressA() routine is only available on wince. */
- return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol);
- #else
- /* All other windows platforms expect GetProcAddress() to take
- ** an Ansi string regardless of the _UNICODE setting */
- return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol);
- #endif
- }
- void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
- UNUSED_PARAMETER(pVfs);
- FreeLibrary((HANDLE)pHandle);
- }
- #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
- #define winDlOpen 0
- #define winDlError 0
- #define winDlSym 0
- #define winDlClose 0
- #endif
+
++ /* Undo the local locks */
++ if( rc==SQLITE_OK ){
++ p->exclMask &= ~unlockMask;
++ p->sharedMask &= ~unlockMask;
++ }
++ return rc;
++}
+
+/*
- ** Write up to nBuf bytes of randomness into zBuf.
++** Get reader locks for connection p on all locks in the readMask parameter.
+*/
- static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- int n = 0;
- UNUSED_PARAMETER(pVfs);
- #if defined(SQLITE_TEST)
- n = nBuf;
- memset(zBuf, 0, nBuf);
- #else
- if( sizeof(SYSTEMTIME)<=nBuf-n ){
- SYSTEMTIME x;
- GetSystemTime(&x);
- memcpy(&zBuf[n], &x, sizeof(x));
- n += sizeof(x);
- }
- if( sizeof(DWORD)<=nBuf-n ){
- DWORD pid = GetCurrentProcessId();
- memcpy(&zBuf[n], &pid, sizeof(pid));
- n += sizeof(pid);
++static int winShmSharedLock(
++ winShmNode *pFile, /* The underlying shared-memory file */
++ winShm *p, /* The connection to get the shared locks */
++ u8 readMask /* Mask of shared locks to be acquired */
++){
++ int rc; /* Result code */
++ winShm *pX; /* For looping over all sibling connections */
++ u8 allShared; /* Union of locks held by connections other than "p" */
++
++ /* Access to the winShmNode object is serialized by the caller */
++ assert( sqlite3_mutex_held(pFile->mutex) );
++
++ /* 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.
++ */
++ allShared = 0;
++ for(pX=pFile->pFirst; pX; pX=pX->pNext){
++ if( pX==p ) continue;
++ if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY;
++ allShared |= pX->sharedMask;
+ }
- if( sizeof(DWORD)<=nBuf-n ){
- DWORD cnt = GetTickCount();
- memcpy(&zBuf[n], &cnt, sizeof(cnt));
- n += sizeof(cnt);
++
++ /* Get shared locks at the system level, if necessary */
++ if( (~allShared) & readMask ){
++ rc = winShmSystemLock(pFile, _SHM_RDLCK, readMask);
++ }else{
++ rc = SQLITE_OK;
+ }
- if( sizeof(LARGE_INTEGER)<=nBuf-n ){
- LARGE_INTEGER i;
- QueryPerformanceCounter(&i);
- memcpy(&zBuf[n], &i, sizeof(i));
- n += sizeof(i);
++
++ /* Get the local shared locks */
++ if( rc==SQLITE_OK ){
++ p->sharedMask |= readMask;
+ }
- #endif
- return n;
++ return rc;
+}
+
-
+/*
- ** Sleep for a little while. Return the amount of time slept.
++** For connection p, get an exclusive lock on all locks identified in
++** the writeMask parameter.
+*/
- static int winSleep(sqlite3_vfs *pVfs, int microsec){
- Sleep((microsec+999)/1000);
- UNUSED_PARAMETER(pVfs);
- return ((microsec+999)/1000)*1000;
++static int winShmExclusiveLock(
++ winShmNode *pFile, /* The underlying shared-memory file */
++ winShm *p, /* The connection to get the exclusive locks */
++ u8 writeMask /* Mask of exclusive locks to be acquired */
++){
++ int rc; /* Result code */
++ winShm *pX; /* For looping over all sibling connections */
++
++ /* Access to the winShmNode object is serialized by the caller */
++ assert( sqlite3_mutex_held(pFile->mutex) );
++
++ /* 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 = winShmSystemLock(pFile, _SHM_WRLCK, writeMask);
++ if( rc==SQLITE_OK ){
++ p->sharedMask &= ~writeMask;
++ p->exclMask |= writeMask;
++ }
++ return rc;
+}
+
+/*
- ** The following variable, if set to a non-zero value, is interpreted as
- ** the number of seconds since 1970 and is used to set the result of
- ** sqlite3OsCurrentTime() during testing.
++** Purge the winShmNodeList list of all entries with winShmNode.nRef==0.
++**
++** This is not a VFS shared-memory method; it is a utility function called
++** by VFS shared-memory methods.
+*/
- #ifdef SQLITE_TEST
- int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
- #endif
++static void winShmPurge(void){
++ winShmNode **pp;
++ winShmNode *p;
++ assert( winShmMutexHeld() );
++ pp = &winShmNodeList;
++ 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->pMMapBuf ){
++ UnmapViewOfFile(p->pMMapBuf);
++ }
++ if( INVALID_HANDLE_VALUE != p->hMap ){
++ CloseHandle(p->hMap);
++ }
++ if( p->hFile.h != INVALID_HANDLE_VALUE ) {
++ winClose((sqlite3_file *)&p->hFile);
++ }
++ *pp = p->pNext;
++ sqlite3_free(p);
++ }else{
++ pp = &p->pNext;
++ }
++ }
++}
++
++/* Forward references to VFS methods */
++static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
++static int winDelete(sqlite3_vfs *,const char*,int);
+
+/*
- ** Find the current time (in Universal Coordinated Time). Write into *piNow
- ** the current time and date as a Julian Day number times 86_400_000. In
- ** other words, write into *piNow the number of milliseconds since the Julian
- ** epoch of noon in Greenwich on November 24, 4714 B.C according to the
- ** proleptic Gregorian calendar.
++** Open a shared-memory area. This particular implementation uses
++** mmapped files.
+**
- ** On success, return 0. Return 1 if the time and date cannot be found.
++** zName is a filename used to identify the shared-memory area. The
++** implementation does not (and perhaps should not) use this name
++** directly, but rather use it as a template for finding an appropriate
++** name for the shared-memory storage. In this implementation, the
++** string "-index" is appended to zName and used as the name of the
++** mmapped file.
++**
++** 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 winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
- /* FILETIME structure is a 64-bit value representing the number of
- 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
++static int winShmOpen(
++ sqlite3_file *fd /* The file to which to attach shared memory */
++){
++ struct winFile *pDbFd; /* Database to which to attach SHM */
++ 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 */
++
++ pDbFd = (winFile*)fd;
++ 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.
+ */
- FILETIME ft;
- static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000;
- #ifdef SQLITE_TEST
- static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
- #endif
- /* 2^32 - to avoid use of LL and warnings in gcc */
- static const sqlite3_int64 max32BitValue =
- (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296;
++ p = sqlite3_malloc( sizeof(*p) );
++ if( p==0 ) return SQLITE_NOMEM;
++ memset(p, 0, sizeof(*p));
++ nName = sqlite3Strlen30(pDbFd->zPath);
++ pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 15 );
++ if( pNew==0 ){
++ sqlite3_free(p);
++ return SQLITE_NOMEM;
++ }
++ memset(pNew, 0, sizeof(*pNew));
++ pNew->zFilename = (char*)&pNew[1];
++ sqlite3_snprintf(nName+15, pNew->zFilename, "%s-wal-index", pDbFd->zPath);
+
- #if SQLITE_OS_WINCE
- SYSTEMTIME time;
- GetSystemTime(&time);
- /* if SystemTimeToFileTime() fails, it returns zero. */
- if (!SystemTimeToFileTime(&time,&ft)){
- return 1;
++ /* Look to see if there is an existing winShmNode that can be used.
++ ** If no matching winShmNode currently exists, 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.
++ */
++ if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
+ }
- #else
- GetSystemTimeAsFileTime( &ft );
++ if( pShmNode ){
++ sqlite3_free(pNew);
++ }else{
++ pShmNode = pNew;
++ pNew = 0;
++ pShmNode->pMMapBuf = NULL;
++ pShmNode->hMap = INVALID_HANDLE_VALUE;
++ ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
++ pShmNode->pNext = winShmNodeList;
++ winShmNodeList = pShmNode;
++
++ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
++ if( pShmNode->mutex==0 ){
++ rc = SQLITE_NOMEM;
++ goto shm_open_err;
++ }
++ pShmNode->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
++ if( pShmNode->mutexBuf==0 ){
++ rc = SQLITE_NOMEM;
++ goto shm_open_err;
++ }
++ rc = winOpen(pDbFd->pVfs,
++ pShmNode->zFilename, /* Name of the file (UTF-8) */
++ (sqlite3_file*)&pShmNode->hFile, /* File handle here */
++ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
++ 0);
++ if( SQLITE_OK!=rc ){
++ rc = SQLITE_CANTOPEN_BKPT;
++ goto shm_open_err;
++ }
++
++ /* Check to see if another process is holding the dead-man switch.
++ ** If not, truncate the file to zero length.
++ */
++ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS)==SQLITE_OK ){
++ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
++ }
++ if( rc==SQLITE_OK ){
++ rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS);
++ }
++ if( rc ) goto shm_open_err;
++ }
++
++ /* Make the new connection a child of the winShmNode */
++ p->pShmNode = pShmNode;
++ p->pNext = pShmNode->pFirst;
++#ifdef SQLITE_DEBUG
++ p->id = pShmNode->nextShmId++;
+#endif
++ pShmNode->pFirst = p;
++ pShmNode->nRef++;
++ pDbFd->pShm = p;
++ winShmLeaveMutex();
++ return SQLITE_OK;
+
- *piNow = winFiletimeEpoch +
- ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
- (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)1000;
++ /* Jump here on any error */
++shm_open_err:
++ winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS);
++ winShmPurge(); /* This call frees pShmNode if required */
++ sqlite3_free(p);
++ sqlite3_free(pNew);
++ winShmLeaveMutex();
++ return rc;
++}
+
- #ifdef SQLITE_TEST
- if( sqlite3_current_time ){
- *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
++/*
++** Close a connection to shared-memory. Delete the underlying
++** storage if deleteFlag is true.
++*/
++static int winShmClose(
++ sqlite3_file *fd, /* Database holding shared memory */
++ int deleteFlag /* Delete after closing if true */
++){
++ winFile *pDbFd; /* Database holding shared-memory */
++ winShm *p; /* The connection to be closed */
++ winShmNode *pShmNode; /* The underlying shared-memory file */
++ winShm **pp; /* For looping over sibling connections */
++
++ pDbFd = (winFile*)fd;
++ p = pDbFd->pShm;
++ pShmNode = p->pShmNode;
++
++ /* 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 pShmNode */
++ sqlite3_mutex_enter(pShmNode->mutex);
++ for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
++ *pp = p->pNext;
++
++ /* Free the connection p */
++ sqlite3_free(p);
++ pDbFd->pShm = 0;
++ sqlite3_mutex_leave(pShmNode->mutex);
++
++ /* If pShmNode->nRef has reached 0, then close the underlying
++ ** shared-memory file, too */
++ winShmEnterMutex();
++ assert( pShmNode->nRef>0 );
++ pShmNode->nRef--;
++ if( pShmNode->nRef==0 ){
++ if( deleteFlag ) winDelete(pDbFd->pVfs, pShmNode->zFilename, 0);
++ winShmPurge();
+ }
- #endif
- UNUSED_PARAMETER(pVfs);
- return 0;
++ winShmLeaveMutex();
++
++ return SQLITE_OK;
+}
+
+/*
- ** Find the current time (in Universal Coordinated Time). Write the
- ** current time and date as a Julian Day number into *prNow and
- ** return 0. Return 1 if the time and date cannot be found.
++** Query and/or changes the size of the underlying storage for
++** a shared-memory segment. The reqSize parameter is the new size
++** of the underlying storage, or -1 to do just a query. The size
++** of the underlying storage (after resizing if resizing occurs) is
++** written into pNewSize.
++**
++** This routine does not (necessarily) change the size of the mapping
++** of the underlying storage into memory. Use xShmGet() to change
++** the mapping size.
++**
++** The reqSize parameter is the minimum size requested. The implementation
++** is free to expand the storage to some larger amount if it chooses.
+*/
- int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
- int rc;
- sqlite3_int64 i;
- rc = winCurrentTimeInt64(pVfs, &i);
- if( !rc ){
- *prNow = i/86400000.0;
++static int winShmSize(
++ sqlite3_file *fd, /* Database holding the shared memory */
++ int reqSize, /* Requested size. -1 for query only */
++ int *pNewSize /* Write new size here */
++){
++ winFile *pDbFd = (winFile*)fd;
++ winShm *p = pDbFd->pShm;
++ winShmNode *pShmNode = p->pShmNode;
++ int rc = SQLITE_OK;
++
++ *pNewSize = 0;
++ if( reqSize>=0 ){
++ sqlite3_int64 sz;
++ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
++ if( SQLITE_OK==rc ){
++ reqSize = (reqSize + SQLITE_WIN_SHM_INCR - 1)/SQLITE_WIN_SHM_INCR;
++ reqSize *= SQLITE_WIN_SHM_INCR;
++ if( reqSize>sz ){
++ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, reqSize);
++ }
++ }
++ }
++ if( SQLITE_OK==rc ){
++ sqlite3_int64 sz;
++ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
++ if( SQLITE_OK==rc ){
++ *pNewSize = (int)sz;
++ }else{
++ rc = SQLITE_IOERR;
++ }
+ }
+ return rc;
+}
+
++
+/*
- ** The idea is that this function works like a combination of
- ** GetLastError() and FormatMessage() on windows (or errno and
- ** strerror_r() on unix). After an error is returned by an OS
- ** function, SQLite calls this function with zBuf pointing to
- ** a buffer of nBuf bytes. The OS layer should populate the
- ** buffer with a nul-terminated UTF-8 encoded error message
- ** describing the last IO error to have occurred within the calling
- ** thread.
++** Map the shared storage into memory. The minimum size of the
++** mapping should be reqMapSize if reqMapSize is positive. If
++** reqMapSize is zero or negative, the implementation can choose
++** whatever mapping size is convenient.
+**
- ** If the error message is too large for the supplied buffer,
- ** it should be truncated. The return value of xGetLastError
- ** is zero if the error message fits in the buffer, or non-zero
- ** otherwise (if the message was truncated). If non-zero is returned,
- ** then it is not necessary to include the nul-terminator character
- ** in the output buffer.
++** *ppBuf is made to point to the memory which is a mapping of the
++** underlying storage. A mutex is acquired to prevent other threads
++** from running while *ppBuf is in use in order to prevent other threads
++** remapping *ppBuf out from under this thread. The winShmRelease()
++** call will release the mutex. However, if the lock state is CHECKPOINT,
++** the mutex is not acquired because CHECKPOINT will never remap the
++** buffer. RECOVER might remap, though, so CHECKPOINT will acquire
++** the mutex if and when it promotes to RECOVER.
+**
- ** Not supplying an error message will have no adverse effect
- ** on SQLite. It is fine to have an implementation that never
- ** returns an error message:
++** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from
++** being remapped also prevents more than one thread from being in
++** RECOVER at a time. But, RECOVER sometimes wants to remap itself.
++** To prevent RECOVER from losing its lock while remapping, the
++** mutex is not released by winShmRelease() when in RECOVER.
+**
- ** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- ** assert(zBuf[0]=='\0');
- ** return 0;
- ** }
++** *pNewMapSize is set to the size of the mapping.
+**
- ** However if an error message is supplied, it will be incorporated
- ** by sqlite into the error message available to the user using
- ** sqlite3_errmsg(), possibly making IO errors easier to debug.
++** *ppBuf and *pNewMapSize might be NULL and zero if no space has
++** yet been allocated to the underlying storage.
+*/
- static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- UNUSED_PARAMETER(pVfs);
- return getLastErrorMsg(nBuf, zBuf);
- }
++static int winShmGet(
++ sqlite3_file *fd, /* The database file holding the shared memory */
++ int reqMapSize, /* Requested size of mapping. -1 means don't care */
++ int *pNewMapSize, /* Write new size of mapping here */
++ void **ppBuf /* Write mapping buffer origin here */
++){
++ winFile *pDbFd = (winFile*)fd;
++ winShm *p = pDbFd->pShm;
++ winShmNode *pShmNode = p->pShmNode;
++ int rc = SQLITE_OK;
+
- #ifndef SQLITE_OMIT_WAL
++ if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){
++ assert( sqlite3_mutex_notheld(pShmNode->mutex) );
++ sqlite3_mutex_enter(pShmNode->mutexBuf);
++ p->hasMutexBuf = 1;
++ }
++ sqlite3_mutex_enter(pShmNode->mutex);
++ if( pShmNode->szMap==0 || reqMapSize>pShmNode->szMap ){
++ int actualSize;
++ if( winShmSize(fd, -1, &actualSize)==SQLITE_OK
++ && reqMapSize<actualSize
++ ){
++ reqMapSize = actualSize;
++ }
++ if( pShmNode->pMMapBuf ){
++ if( !UnmapViewOfFile(pShmNode->pMMapBuf) ){
++ pShmNode->lastErrno = GetLastError();
++ rc = SQLITE_IOERR;
++ }
++ CloseHandle(pShmNode->hMap);
++ pShmNode->hMap = INVALID_HANDLE_VALUE;
++ }
++ if( SQLITE_OK == rc ){
++ pShmNode->pMMapBuf = 0;
++ if( reqMapSize == 0 ){
++ /* can't create 0 byte file mapping in Windows */
++ pShmNode->szMap = 0;
++ }else{
++ /* create the file mapping object */
++ if( INVALID_HANDLE_VALUE == pShmNode->hMap ){
++ /* TBD provide an object name to each file
++ ** mapping so it can be re-used across processes.
++ */
++ pShmNode->hMap = CreateFileMapping(pShmNode->hFile.h,
++ NULL,
++ PAGE_READWRITE,
++ 0,
++ reqMapSize,
++ NULL);
++ }
++ if( NULL==pShmNode->hMap ){
++ pShmNode->lastErrno = GetLastError();
++ rc = SQLITE_IOERR;
++ pShmNode->szMap = 0;
++ pShmNode->hMap = INVALID_HANDLE_VALUE;
++ }else{
++ pShmNode->pMMapBuf = MapViewOfFile(pShmNode->hMap,
++ FILE_MAP_WRITE | FILE_MAP_READ,
++ 0,
++ 0,
++ reqMapSize);
++ if( !pShmNode->pMMapBuf ){
++ pShmNode->lastErrno = GetLastError();
++ rc = SQLITE_IOERR;
++ pShmNode->szMap = 0;
++ }else{
++ pShmNode->szMap = reqMapSize;
++ }
++ }
++ }
++ }
++ }
++ *pNewMapSize = pShmNode->szMap;
++ *ppBuf = pShmNode->pMMapBuf;
++ sqlite3_mutex_leave(pShmNode->mutex);
++ return rc;
++}
+
+/*
- ** Helper functions to obtain and relinquish the global mutex. The
- ** global mutex is used to protect the winLockInfo objects used by
- ** this file, all of which may be shared by multiple threads.
++** Release the lock held on the shared memory segment so that other
++** threads are free to resize it if necessary.
+**
- ** Function winShmMutexHeld() is used to assert() that the global mutex
- ** is held when required. This function is only used as part of assert()
- ** statements. e.g.
++** If the lock is not currently held, this routine is a harmless no-op.
+**
- ** winShmEnterMutex()
- ** assert( winShmMutexHeld() );
- ** winEnterLeave()
++** If the shared-memory object is in lock state RECOVER, then we do not
++** really want to release the lock, so in that case too, this routine
++** is a no-op.
+*/
- static void winShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- }
- static void winShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
++static int winShmRelease(sqlite3_file *fd){
++ winFile *pDbFd = (winFile*)fd;
++ winShm *p = pDbFd->pShm;
++ if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){
++ winShmNode *pShmNode = p->pShmNode;
++ assert( sqlite3_mutex_notheld(pShmNode->mutex) );
++ sqlite3_mutex_leave(pShmNode->mutexBuf);
++ p->hasMutexBuf = 0;
++ }
++ return SQLITE_OK;
+}
++
++/*
++** Symbolic names for LOCK states used for debugging.
++*/
+#ifdef SQLITE_DEBUG
- static int winShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- }
++static const char *azLkName[] = {
++ "UNLOCK",
++ "READ",
++ "READ_FULL",
++ "WRITE",
++ "PENDING",
++ "CHECKPOINT",
++ "RECOVER"
++};
+#endif
+
- /* Forward reference */
- typedef struct winShm winShm;
- typedef struct winShmFile winShmFile;
+
+/*
- ** 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 winFile object, but they all
- ** point to a single instance of this object. In other words, each
- ** log-summary is opened only once per process.
- **
- ** winShmMutexHeld() 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 winShmFile.mutex must be held or winShmFile.nRef==0 and
- ** winShmMutexHeld() is true when reading or writing any other field
- ** in this structure.
- **
- ** To avoid deadlocks, mutex and mutexBuf are always released in the
- ** reverse order that they are acquired. mutexBuf is always acquired
- ** first and released last. This invariant is check by asserting
- ** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or
- ** released.
++** Change the lock state for a shared-memory segment.
+*/
- struct winShmFile {
- sqlite3_mutex *mutex; /* Mutex to access this object */
- sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */
- char *zFilename; /* Name of the file */
- winFile hFile; /* File handle from winOpen */
- HANDLE hMap; /* File handle from CreateFileMapping */
- DWORD lastErrno; /* The Windows errno from the last I/O error */
- int szMap; /* Size of the mapping of file into memory */
- char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */
- int nRef; /* Number of winShm objects pointing to this */
- winShm *pFirst; /* All winShm objects pointing to this */
- winShmFile *pNext; /* Next in list of all winShmFile objects */
- #ifdef SQLITE_DEBUG
- u8 exclMask; /* Mask of exclusive locks held */
- u8 sharedMask; /* Mask of shared locks held */
- u8 nextShmId; /* Next available winShm.id value */
- #endif
- };
++static int winShmLock(
++ sqlite3_file *fd, /* Database holding the shared memory */
++ int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */
++ int *pGotLock /* The lock you actually got */
++){
++ winFile *pDbFd = (winFile*)fd;
++ winShm *p = pDbFd->pShm;
++ winShmNode *pShmNode = p->pShmNode;
++ 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_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==p->lockState
++ || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL)
++ ){
++ OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s and got %s\n",
++ pShmNode->hFile.h,
++ p->id, (int)GetCurrentProcessId(), azLkName[desiredLock],
++ azLkName[p->lockState]));
++ if( pGotLock ) *pGotLock = p->lockState;
++ return SQLITE_OK;
++ }
++
++ OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s->%s\n",
++ pShmNode->hFile.h,
++ p->id, (int)GetCurrentProcessId(), azLkName[p->lockState],
++ azLkName[desiredLock]));
++
++ if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){
++ assert( sqlite3_mutex_notheld(pShmNode->mutex) );
++ sqlite3_mutex_enter(pShmNode->mutexBuf);
++ p->hasMutexBuf = 1;
++ }
++ sqlite3_mutex_enter(pShmNode->mutex);
++ switch( desiredLock ){
++ case SQLITE_SHM_UNLOCK: {
++ assert( p->lockState!=SQLITE_SHM_RECOVER );
++ winShmUnlock(pShmNode, p, WIN_SHM_A|WIN_SHM_B|WIN_SHM_C|WIN_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 = winShmSharedLock(pShmNode, p, WIN_SHM_A|WIN_SHM_B);
++ if( rc==SQLITE_BUSY ){
++ rc = winShmSharedLock(pShmNode, p, WIN_SHM_D);
++ if( rc==SQLITE_OK ){
++ p->lockState = SQLITE_SHM_READ_FULL;
++ }
++ }else{
++ winShmUnlock(pShmNode, p, WIN_SHM_B);
++ p->lockState = SQLITE_SHM_READ;
++ }
++ }
++ }else{
++ assert( p->lockState==SQLITE_SHM_WRITE
++ || p->lockState==SQLITE_SHM_RECOVER );
++ rc = winShmSharedLock(pShmNode, p, WIN_SHM_A);
++ winShmUnlock(pShmNode, p, WIN_SHM_C|WIN_SHM_D);
++ p->lockState = SQLITE_SHM_READ;
++ }
++ break;
++ }
++ case SQLITE_SHM_WRITE: {
++ assert( p->lockState==SQLITE_SHM_READ
++ || p->lockState==SQLITE_SHM_READ_FULL );
++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_C|WIN_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
++ );
++ if( p->lockState==SQLITE_SHM_UNLOCK ){
++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_B|WIN_SHM_C);
++ if( rc==SQLITE_OK ){
++ p->lockState = SQLITE_SHM_PENDING;
++ }
++ }
++ if( p->lockState==SQLITE_SHM_PENDING ){
++ rc = winShmExclusiveLock(pShmNode, p, WIN_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
++ );
++ assert( sqlite3_mutex_held(pShmNode->mutexBuf) );
++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_C);
++ if( rc==SQLITE_OK ){
++ p->lockState = SQLITE_SHM_RECOVER;
++ }
++ break;
++ }
++ }
++ sqlite3_mutex_leave(pShmNode->mutex);
++ OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d got %s\n",
++ pShmNode->hFile.h,
++ p->id, (int)GetCurrentProcessId(), azLkName[p->lockState]));
++ if( pGotLock ) *pGotLock = p->lockState;
++ return rc;
++}
+
++#else
++# define winShmOpen 0
++# define winShmSize 0
++# define winShmGet 0
++# define winShmRelease 0
++# define winShmLock 0
++# define winShmClose 0
++#endif /* #ifndef SQLITE_OMIT_WAL */
+/*
- ** A global array of all winShmFile objects.
- **
- ** The winShmMutexHeld() must be true while reading or writing this list.
- */
- static winShmFile *winShmFileList = 0;
++***************************** End Shared Memory *****************************
++****************************************************************************/
+
/*
- ** Structure used internally by this VFS to record the state of an
- ** open shared memory connection.
- **
- ** winShm.pFile->mutex must be held while reading or writing the
- ** winShm.pNext and winShm.locks[] elements.
- **
- ** The winShm.pFile element is initialized when the object is created
- ** and is read-only thereafter.
+ ** This vector defines all the methods that can operate on an
+ ** sqlite3_file for win32.
*/
- struct winShm {
- winShmFile *pFile; /* The underlying winShmFile object */
- winShm *pNext; /* Next winShm with the same winShmFile */
- u8 lockState; /* Current lock state */
- u8 hasMutex; /* True if holding the winShmFile mutex */
- u8 hasMutexBuf; /* True if holding pFile->mutexBuf */
- 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 winShmFile */
- #endif
+ static const sqlite3_io_methods winIoMethod = {
- 1, /* iVersion */
++ 2, /* iVersion */
+ winClose,
+ winRead,
+ winWrite,
+ winTruncate,
+ winSync,
+ winFileSize,
+ winLock,
+ winUnlock,
+ winCheckReservedLock,
+ winFileControl,
+ winSectorSize,
- winDeviceCharacteristics
++ winDeviceCharacteristics,
++ winShmOpen, /* xShmOpen */
++ winShmSize, /* xShmSize */
++ winShmGet, /* xShmGet */
++ winShmRelease, /* xShmRelease */
++ winShmLock, /* xShmLock */
++ winShmClose /* xShmClose */
};
- /*
- ** Size increment by which shared memory grows
- */
- #define SQLITE_WIN_SHM_INCR 4096
+ /***************************************************************************
+ ** Here ends the I/O methods that form the sqlite3_io_methods object.
+ **
+ ** The next block of code implements the VFS methods.
+ ****************************************************************************/
/*
- ** Constants used for locking
+ ** 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.
*/
- #define WIN_SHM_BASE 32 /* Byte offset of the first lock byte */
- #define WIN_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */
- #define WIN_SHM_A 0x10 /* Mask for region locks... */
- #define WIN_SHM_B 0x20
- #define WIN_SHM_C 0x40
- #define WIN_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 void *convertUtf8Filename(const char *zFilename){
+ void *zConverted = 0;
+ if( isNT() ){
+ zConverted = utf8ToUnicode(zFilename);
+ /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
*/
- static const char *winShmLockString(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 & WIN_SHM_DMS) ? 'S' : '.';
- z[1] = (mask & WIN_SHM_A) ? 'A' : '.';
- z[2] = (mask & WIN_SHM_B) ? 'B' : '.';
- z[3] = (mask & WIN_SHM_C) ? 'C' : '.';
- z[4] = (mask & WIN_SHM_D) ? 'D' : '.';
- z[5] = 0;
- return z;
+ #if SQLITE_OS_WINCE==0
+ }else{
+ zConverted = utf8ToMbcs(zFilename);
+ #endif
+ }
+ /* caller will handle out of memory */
+ return zConverted;
}
- #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 mask is exactly WIN_SHM_C and are non-blocking
- ** otherwise.
+ ** Create a temporary file name in zBuf. zBuf must be big enough to
+ ** hold at pVfs->mxPathname characters.
*/
- #define _SHM_UNLCK 1
- #define _SHM_RDLCK 2
- #define _SHM_WRLCK 3
- static int winShmSystemLock(
- winShmFile *pFile, /* Apply locks to this open shared-memory segment */
- int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */
- u8 lockMask /* Which bytes to lock or unlock */
- ){
- OVERLAPPED ovlp;
- DWORD dwFlags;
- int nBytes; /* Number of bytes to lock */
- int i; /* Offset into the locking byte range */
- int rc = 0; /* Result code form Lock/UnlockFileEx() */
- u8 mask; /* Mask of bits in lockMask */
-
- /* Access to the winShmFile object is serialized by the caller */
- assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
-
- /* Initialize the locking parameters */
- if( lockMask==WIN_SHM_C && lockType!=_SHM_UNLCK ){
- dwFlags = 0;
- OSTRACE(("SHM-LOCK %d requesting blocking lock %s\n",
- pFile->hFile.h,
- winShmLockString(lockMask)));
+ static int getTempname(int nBuf, char *zBuf){
+ static char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ size_t i, j;
+ char zTempPath[MAX_PATH+1];
+ if( sqlite3_temp_directory ){
+ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
+ }else if( isNT() ){
+ char *zMulti;
+ WCHAR zWidePath[MAX_PATH];
+ GetTempPathW(MAX_PATH-30, zWidePath);
+ zMulti = unicodeToUtf8(zWidePath);
+ if( zMulti ){
+ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
+ free(zMulti);
+ }else{
+ return SQLITE_NOMEM;
+ }
+ /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
+ ** Since the ASCII version of these Windows API do not exist for WINCE,
+ ** it's important to not reference them for WINCE builds.
+ */
+ #if SQLITE_OS_WINCE==0
}else{
- dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
- OSTRACE(("SHM-LOCK %d requesting %s %s\n",
- pFile->hFile.h,
- lockType!=_SHM_UNLCK ? "lock" : "unlock",
- winShmLockString(lockMask)));
+ char *zUtf8;
+ char zMbcsPath[MAX_PATH];
+ GetTempPathA(MAX_PATH-30, zMbcsPath);
+ zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
+ if( zUtf8 ){
+ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
+ free(zUtf8);
+ }else{
+ return SQLITE_NOMEM;
+ }
+ #endif
}
- if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
-
- /* 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 );
- memset(&ovlp, 0, sizeof(OVERLAPPED));
- ovlp.Offset = i+WIN_SHM_BASE;
- nBytes = 1;
-
- /* Extend the locking range for each additional bit that is set */
- mask <<= 1;
- while( mask!=0 && (lockMask & mask)!=0 ){
- nBytes++;
- mask <<= 1;
+ for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
+ zTempPath[i] = 0;
+ sqlite3_snprintf(nBuf-30, zBuf,
+ "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
+ j = sqlite3Strlen30(zBuf);
+ sqlite3_randomness(20, &zBuf[j]);
+ for(i=0; i<20; i++, j++){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
+ zBuf[j] = 0;
+ OSTRACE(("TEMP FILENAME: %s\n", zBuf));
+ return SQLITE_OK;
+ }
- /* Verify that all bits set in lockMask are contiguous */
- assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 );
+ /*
+ ** The return value of getLastErrorMsg
+ ** is zero if the error message fits in the buffer, or non-zero
+ ** otherwise (if the message was truncated).
+ */
+ static int getLastErrorMsg(int nBuf, char *zBuf){
+ /* FormatMessage returns 0 on failure. Otherwise it
+ ** returns the number of TCHARs written to the output
+ ** buffer, excluding the terminating null char.
+ */
+ DWORD error = GetLastError();
+ DWORD dwLen = 0;
+ char *zOut = 0;
- /* Release/Acquire the system-level lock */
- if( lockType==_SHM_UNLCK ){
- for(i=0; i<nBytes; i++, ovlp.Offset++){
- rc = UnlockFileEx(pFile->hFile.h, 0, 1, 0, &ovlp);
- if( !rc ) break;
+ if( isNT() ){
+ WCHAR *zTempWide = NULL;
+ dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ 0,
+ (LPWSTR) &zTempWide,
+ 0,
+ 0);
+ if( dwLen > 0 ){
+ /* allocate a buffer and convert to UTF8 */
+ zOut = unicodeToUtf8(zTempWide);
+ /* free the system buffer allocated by FormatMessage */
+ LocalFree(zTempWide);
}
+ /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
+ ** Since the ASCII version of these Windows API do not exist for WINCE,
+ ** it's important to not reference them for WINCE builds.
+ */
+ #if SQLITE_OS_WINCE==0
}else{
- /* release old individual byte locks (if any)
- ** and set new individual byte locks */
- for(i=0; i<nBytes; i++, ovlp.Offset++){
- UnlockFileEx(pFile->hFile.h, 0, 1, 0, &ovlp);
- rc = LockFileEx(pFile->hFile.h, dwFlags, 0, 1, 0, &ovlp);
- if( !rc ) break;
- }
- }
- if( !rc ){
- OSTRACE(("SHM-LOCK %d %s ERROR 0x%08lx\n",
- pFile->hFile.h,
- lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", GetLastError()));
- /* release individual byte locks (if any) */
- ovlp.Offset-=i;
- for(i=0; i<nBytes; i++, ovlp.Offset++){
- UnlockFileEx(pFile->hFile.h, 0, 1, 0, &ovlp);
+ char *zTemp = NULL;
+ dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ 0,
+ (LPSTR) &zTemp,
+ 0,
+ 0);
+ if( dwLen > 0 ){
+ /* allocate a buffer and convert to UTF8 */
+ zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
+ /* free the system buffer allocated by FormatMessage */
+ LocalFree(zTemp);
}
+ #endif
}
- rc = (rc!=0) ? SQLITE_OK : SQLITE_BUSY;
-
- /* Update the global lock state and do debug tracing */
- #ifdef SQLITE_DEBUG
- OSTRACE(("SHM-LOCK %d ", pFile->hFile.h));
- if( rc==SQLITE_OK ){
- if( lockType==_SHM_UNLCK ){
- OSTRACE(("unlock ok"));
- pFile->exclMask &= ~lockMask;
- pFile->sharedMask &= ~lockMask;
- }else if( lockType==_SHM_RDLCK ){
- OSTRACE(("read-lock ok"));
- pFile->exclMask &= ~lockMask;
- pFile->sharedMask |= lockMask;
- }else{
- assert( lockType==_SHM_WRLCK );
- OSTRACE(("write-lock ok"));
- pFile->exclMask |= lockMask;
- pFile->sharedMask &= ~lockMask;
- }
+ if( 0 == dwLen ){
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
}else{
- if( lockType==_SHM_UNLCK ){
- OSTRACE(("unlock failed"));
- }else if( lockType==_SHM_RDLCK ){
- OSTRACE(("read-lock failed"));
- }else{
- assert( lockType==_SHM_WRLCK );
- OSTRACE(("write-lock failed"));
- }
+ /* copy a maximum of nBuf chars to output buffer */
+ sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
+ /* free the UTF8 buffer */
+ free(zOut);
}
- OSTRACE((" - change requested %s - afterwards %s:%s\n",
- winShmLockString(lockMask),
- winShmLockString(pFile->sharedMask),
- winShmLockString(pFile->exclMask)));
- #endif
-
- return rc;
+ return 0;
}
/*
- ** For connection p, unlock all of the locks identified by the unlockMask
- ** parameter.
+ ** Open a file.
*/
- static int winShmUnlock(
- winShmFile *pFile, /* The underlying shared-memory file */
- winShm *p, /* The connection to be unlocked */
- u8 unlockMask /* Mask of locks to be unlocked */
+ static int winOpen(
+ sqlite3_vfs *pVfs, /* Not used */
+ const char *zName, /* Name of the file (UTF-8) */
+ sqlite3_file *id, /* Write the SQLite file handle here */
+ int flags, /* Open mode flags */
+ int *pOutFlags /* Status return flags */
){
- int rc; /* Result code */
- winShm *pX; /* For looping over all sibling connections */
- u8 allMask; /* Union of locks held by connections other than "p" */
+ HANDLE h;
+ DWORD dwDesiredAccess;
+ DWORD dwShareMode;
+ DWORD dwCreationDisposition;
+ DWORD dwFlagsAndAttributes = 0;
+ #if SQLITE_OS_WINCE
+ int isTemp = 0;
+ #endif
+ winFile *pFile = (winFile*)id;
+ void *zConverted; /* Filename in OS encoding */
+ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
+ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
- /* Access to the winShmFile object is serialized by the caller */
- assert( sqlite3_mutex_held(pFile->mutex) );
+ assert( id!=0 );
+ UNUSED_PARAMETER(pVfs);
- /* don't attempt to unlock anything we don't have locks for */
- if( (unlockMask & (p->exclMask|p->sharedMask)) != unlockMask ){
- OSTRACE(("SHM-LOCK %d unlocking more than we have locked - requested %s - have %s\n",
- pFile->hFile.h,
- winShmLockString(unlockMask),
- winShmLockString(p->exclMask|p->sharedMask)));
- unlockMask &= (p->exclMask|p->sharedMask);
++ pFile->h = INVALID_HANDLE_VALUE;
++
+ /* If the second argument to this function is NULL, generate a
+ ** temporary file name to use
+ */
+ if( !zUtf8Name ){
+ int rc = getTempname(MAX_PATH+1, zTmpname);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ zUtf8Name = zTmpname;
}
- /* Compute locks held by sibling connections */
- allMask = 0;
- for(pX=pFile->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
- allMask |= pX->sharedMask;
+ /* Convert the filename to the system encoding. */
+ zConverted = convertUtf8Filename(zUtf8Name);
+ if( zConverted==0 ){
+ return SQLITE_NOMEM;
}
- /* Unlock the system-level locks */
- if( (unlockMask & allMask)!=unlockMask ){
- rc = winShmSystemLock(pFile, _SHM_UNLCK, unlockMask & ~allMask);
+ if( flags & SQLITE_OPEN_READWRITE ){
+ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
}else{
- rc = SQLITE_OK;
+ dwDesiredAccess = GENERIC_READ;
}
-
- /* 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 winShmSharedLock(
- winShmFile *pFile, /* The underlying shared-memory file */
- winShm *p, /* The connection to get the shared locks */
- u8 readMask /* Mask of shared locks to be acquired */
- ){
- int rc; /* Result code */
- winShm *pX; /* For looping over all sibling connections */
- u8 allShared; /* Union of locks held by connections other than "p" */
-
- /* Access to the winShmFile object is serialized by the caller */
- assert( sqlite3_mutex_held(pFile->mutex) );
-
- /* 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.
+ /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
+ ** created. SQLite doesn't use it to indicate "exclusive access"
+ ** as it is usually understood.
*/
- allShared = 0;
- for(pX=pFile->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY;
- allShared |= pX->sharedMask;
+ assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE));
+ if( flags & SQLITE_OPEN_EXCLUSIVE ){
+ /* Creates a new file, only if it does not already exist. */
+ /* If the file exists, it fails. */
+ dwCreationDisposition = CREATE_NEW;
+ }else if( flags & SQLITE_OPEN_CREATE ){
+ /* Open existing file, or create if it doesn't exist */
+ dwCreationDisposition = OPEN_ALWAYS;
+ }else{
+ /* Opens a file, only if it exists. */
+ dwCreationDisposition = OPEN_EXISTING;
}
-
- /* Get shared locks at the system level, if necessary */
- if( (~allShared) & readMask ){
- rc = winShmSystemLock(pFile, _SHM_RDLCK, readMask);
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ if( flags & SQLITE_OPEN_DELETEONCLOSE ){
+ #if SQLITE_OS_WINCE
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN;
+ isTemp = 1;
+ #else
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY
+ | FILE_ATTRIBUTE_HIDDEN
+ | FILE_FLAG_DELETE_ON_CLOSE;
+ #endif
}else{
- rc = SQLITE_OK;
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+ /* Reports from the internet are that performance is always
+ ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */
+ #if SQLITE_OS_WINCE
+ dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
+ #endif
+ if( isNT() ){
+ h = CreateFileW((WCHAR*)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL
+ );
+ /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
+ ** Since the ASCII version of these Windows API do not exist for WINCE,
+ ** it's important to not reference them for WINCE builds.
+ */
+ #if SQLITE_OS_WINCE==0
+ }else{
+ h = CreateFileA((char*)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL
+ );
+ #endif
+ }
++ OSTRACE(("OPEN %d %s 0x%lx %s\n",
++ h, zName, dwDesiredAccess,
++ h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
+ if( h==INVALID_HANDLE_VALUE ){
+ free(zConverted);
+ if( flags & SQLITE_OPEN_READWRITE ){
+ return winOpen(pVfs, zName, id,
+ ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags);
+ }else{
+ return SQLITE_CANTOPEN_BKPT;
+ }
+ }
+ if( pOutFlags ){
+ if( flags & SQLITE_OPEN_READWRITE ){
+ *pOutFlags = SQLITE_OPEN_READWRITE;
+ }else{
+ *pOutFlags = SQLITE_OPEN_READONLY;
+ }
+ }
+ memset(pFile, 0, sizeof(*pFile));
+ pFile->pMethod = &winIoMethod;
+ pFile->h = h;
+ pFile->lastErrno = NO_ERROR;
++ pFile->pVfs = pVfs;
++ pFile->pShm = 0;
++ pFile->zPath = zName;
+ pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
+ #if SQLITE_OS_WINCE
+ if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) ==
+ (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)
+ && !winceCreateLock(zName, pFile)
+ ){
+ CloseHandle(h);
+ free(zConverted);
+ return SQLITE_CANTOPEN_BKPT;
}
-
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= readMask;
+ if( isTemp ){
+ pFile->zDeleteOnClose = zConverted;
+ }else
+ #endif
+ {
+ free(zConverted);
}
- return rc;
+ OpenCounter(+1);
+ return SQLITE_OK;
}
/*
- ** For connection p, get an exclusive lock on all locks identified in
- ** the writeMask parameter.
+ ** Delete the named file.
+ **
+ ** Note that windows does not allow a file to be deleted if some other
+ ** process has it open. Sometimes a virus scanner or indexing program
+ ** will open a journal file shortly after it is created in order to do
+ ** whatever it does. While this other process is holding the
+ ** file open, we will be unable to delete it. To work around this
+ ** problem, we delay 100 milliseconds and try to delete again. Up
+ ** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
+ ** up and returning an error.
*/
- static int winShmExclusiveLock(
- winShmFile *pFile, /* The underlying shared-memory file */
- winShm *p, /* The connection to get the exclusive locks */
- u8 writeMask /* Mask of exclusive locks to be acquired */
+ #define MX_DELETION_ATTEMPTS 5
+ static int winDelete(
+ sqlite3_vfs *pVfs, /* Not used on win32 */
+ const char *zFilename, /* Name of file to delete */
+ int syncDir /* Not used on win32 */
){
- int rc; /* Result code */
- winShm *pX; /* For looping over all sibling connections */
-
- /* Access to the winShmFile object is serialized by the caller */
- assert( sqlite3_mutex_held(pFile->mutex) );
-
- /* 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;
+ int cnt = 0;
+ DWORD rc;
+ DWORD error = 0;
+ void *zConverted = convertUtf8Filename(zFilename);
+ UNUSED_PARAMETER(pVfs);
+ UNUSED_PARAMETER(syncDir);
+ if( zConverted==0 ){
+ return SQLITE_NOMEM;
}
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also mark the local connection as being locked.
- */
- rc = winShmSystemLock(pFile, _SHM_WRLCK, writeMask);
- if( rc==SQLITE_OK ){
- p->sharedMask &= ~writeMask;
- p->exclMask |= writeMask;
+ SimulateIOError(return SQLITE_IOERR_DELETE);
+ if( isNT() ){
+ do{
+ DeleteFileW(zConverted);
+ }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
+ || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
+ && (++cnt < MX_DELETION_ATTEMPTS)
+ && (Sleep(100), 1) );
+ /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
+ ** Since the ASCII version of these Windows API do not exist for WINCE,
+ ** it's important to not reference them for WINCE builds.
+ */
+ #if SQLITE_OS_WINCE==0
+ }else{
+ do{
+ DeleteFileA(zConverted);
+ }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
+ || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
+ && (++cnt < MX_DELETION_ATTEMPTS)
+ && (Sleep(100), 1) );
+ #endif
}
- return rc;
+ free(zConverted);
- OSTRACE(("DELETE \"%s\"\n", zFilename));
++ OSTRACE(("DELETE \"%s\" %s\n", zFilename,
++ ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ?
++ "ok" : "failed" ));
++
+ return ( (rc == INVALID_FILE_ATTRIBUTES)
+ && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE;
}
/*
}
/*
- ** Symbolic names for LOCK states used for debugging.
-** The following variable, if set to a non-zero value, becomes the result
-** returned from sqlite3OsCurrentTime(). This is used for testing.
++** The following variable, if set to a non-zero value, is interpreted as
++** the number of seconds since 1970 and is used to set the result of
++** sqlite3OsCurrentTime() during testing.
*/
- #ifdef SQLITE_DEBUG
- static const char *azLkName[] = {
- "UNLOCK",
- "READ",
- "READ_FULL",
- "WRITE",
- "PENDING",
- "CHECKPOINT",
- "RECOVER"
- };
+ #ifdef SQLITE_TEST
-int sqlite3_current_time = 0;
++int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
#endif
-
/*
- ** Change the lock state for a shared-memory segment.
-** Find the current time (in Universal Coordinated Time). Write the
-** current time and date as a Julian Day number into *prNow and
-** return 0. Return 1 if the time and date cannot be found.
++** Find the current time (in Universal Coordinated Time). Write into *piNow
++** the current time and date as a Julian Day number times 86_400_000. In
++** other words, write into *piNow the number of milliseconds since the Julian
++** epoch of noon in Greenwich on November 24, 4714 B.C according to the
++** proleptic Gregorian calendar.
++**
++** On success, return 0. Return 1 if the time and date cannot be found.
*/
- static int winShmLock(
- sqlite3_vfs *pVfs, /* The VFS */
- sqlite3_shm *pSharedMem, /* Pointer from winShmOpen() */
- int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */
- int *pGotLock /* The lock you actually got */
- ){
- winShm *p = (winShm*)pSharedMem;
- winShmFile *pFile = p->pFile;
- int rc = SQLITE_PROTOCOL;
-
- UNUSED_PARAMETER(pVfs);
-
- /* 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.
-int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
- FILETIME ft;
++static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
+ /* FILETIME structure is a 64-bit value representing the number of
+ 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/
- assert( desiredLock==SQLITE_SHM_UNLOCK
- || desiredLock==SQLITE_SHM_READ
- || desiredLock==SQLITE_SHM_WRITE
- || desiredLock==SQLITE_SHM_CHECKPOINT
- || desiredLock==SQLITE_SHM_RECOVER );
- sqlite3_int64 timeW; /* Whole days */
- sqlite3_int64 timeF; /* Fractional Days */
-
- /* Number of 100-nanosecond intervals in a single day */
- static const sqlite3_int64 ntuPerDay =
- 10000000*(sqlite3_int64)86400;
-
- /* Number of 100-nanosecond intervals in half of a day */
- static const sqlite3_int64 ntuPerHalfDay =
- 10000000*(sqlite3_int64)43200;
-
++ FILETIME ft;
++ static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000;
++#ifdef SQLITE_TEST
++ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
++#endif
+ /* 2^32 - to avoid use of LL and warnings in gcc */
+ static const sqlite3_int64 max32BitValue =
+ (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296;
- /* Return directly if this is just a lock state query, or if
- ** the connection is already in the desired locking state.
- */
- if( desiredLock==p->lockState
- || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL)
- ){
- OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s and got %s\n",
- pFile->hFile.h,
- p->id, (int)GetCurrentProcessId(), azLkName[desiredLock], azLkName[p->lockState]));
- if( pGotLock ) *pGotLock = p->lockState;
- return SQLITE_OK;
+ #if SQLITE_OS_WINCE
+ SYSTEMTIME time;
+ GetSystemTime(&time);
+ /* if SystemTimeToFileTime() fails, it returns zero. */
+ if (!SystemTimeToFileTime(&time,&ft)){
+ return 1;
}
- UNUSED_PARAMETER(pVfs);
- timeW = (((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime;
- timeF = timeW % ntuPerDay; /* fractional days (100-nanoseconds) */
- timeW = timeW / ntuPerDay; /* whole days */
- timeW = timeW + 2305813; /* add whole days (from 2305813.5) */
- timeF = timeF + ntuPerHalfDay; /* add half a day (from 2305813.5) */
- timeW = timeW + (timeF/ntuPerDay); /* add whole day if half day made one */
- timeF = timeF % ntuPerDay; /* compute new fractional days */
- *prNow = (double)timeW + ((double)timeF / (double)ntuPerDay);
+ #else
+ GetSystemTimeAsFileTime( &ft );
+ #endif
- OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s->%s\n",
- pFile->hFile.h,
- p->id, (int)GetCurrentProcessId(), azLkName[p->lockState], azLkName[desiredLock]));
-
- if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){
- assert( sqlite3_mutex_notheld(pFile->mutex) );
- sqlite3_mutex_enter(pFile->mutexBuf);
- p->hasMutexBuf = 1;
+
- *prNow = ((double)sqlite3_current_time + (double)43200) / (double)86400 + (double)2440587;
++ *piNow = winFiletimeEpoch +
++ ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
++ (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)1000;
++
+ #ifdef SQLITE_TEST
+ if( sqlite3_current_time ){
++ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
}
- sqlite3_mutex_enter(pFile->mutex);
- switch( desiredLock ){
- case SQLITE_SHM_UNLOCK: {
- assert( p->lockState!=SQLITE_SHM_RECOVER );
- winShmUnlock(pFile, p, WIN_SHM_A|WIN_SHM_B|WIN_SHM_C|WIN_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 = winShmSharedLock(pFile, p, WIN_SHM_A|WIN_SHM_B);
- if( rc==SQLITE_BUSY ){
- rc = winShmSharedLock(pFile, p, WIN_SHM_D);
- if( rc==SQLITE_OK ){
- p->lockState = SQLITE_SHM_READ_FULL;
- }
- }else{
- winShmUnlock(pFile, p, WIN_SHM_B);
- p->lockState = SQLITE_SHM_READ;
- }
- }
- }else{
- assert( p->lockState==SQLITE_SHM_WRITE
- || p->lockState==SQLITE_SHM_RECOVER );
- rc = winShmSharedLock(pFile, p, WIN_SHM_A);
- winShmUnlock(pFile, p, WIN_SHM_C|WIN_SHM_D);
- p->lockState = SQLITE_SHM_READ;
- }
- break;
- }
- case SQLITE_SHM_WRITE: {
- assert( p->lockState==SQLITE_SHM_READ
- || p->lockState==SQLITE_SHM_READ_FULL );
- rc = winShmExclusiveLock(pFile, p, WIN_SHM_C|WIN_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
- );
- if( p->lockState==SQLITE_SHM_UNLOCK ){
- rc = winShmExclusiveLock(pFile, p, WIN_SHM_B|WIN_SHM_C);
- if( rc==SQLITE_OK ){
- p->lockState = SQLITE_SHM_PENDING;
- }
- }
- if( p->lockState==SQLITE_SHM_PENDING ){
- rc = winShmExclusiveLock(pFile, p, WIN_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
- );
- assert( sqlite3_mutex_held(pFile->mutexBuf) );
- rc = winShmExclusiveLock(pFile, p, WIN_SHM_C);
- if( rc==SQLITE_OK ){
- p->lockState = SQLITE_SHM_RECOVER;
- }
- break;
- }
+ #endif
++ UNUSED_PARAMETER(pVfs);
+ return 0;
+ }
+
++/*
++** Find the current time (in Universal Coordinated Time). Write the
++** current time and date as a Julian Day number into *prNow and
++** return 0. Return 1 if the time and date cannot be found.
++*/
++int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
++ int rc;
++ sqlite3_int64 i;
++ rc = winCurrentTimeInt64(pVfs, &i);
++ if( !rc ){
++ *prNow = i/86400000.0;
+ }
- sqlite3_mutex_leave(pFile->mutex);
- OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d got %s\n",
- pFile->hFile.h,
- p->id, (int)GetCurrentProcessId(), azLkName[p->lockState]));
- if( pGotLock ) *pGotLock = p->lockState;
+ return rc;
+}
+
- #else
- # define winShmOpen 0
- # define winShmSize 0
- # define winShmGet 0
- # define winShmRelease 0
- # define winShmLock 0
- # define winShmClose 0
- #endif /* #ifndef SQLITE_OMIT_WAL */
+ /*
+ ** The idea is that this function works like a combination of
+ ** GetLastError() and FormatMessage() on windows (or errno and
+ ** strerror_r() on unix). After an error is returned by an OS
+ ** function, SQLite calls this function with zBuf pointing to
+ ** a buffer of nBuf bytes. The OS layer should populate the
+ ** buffer with a nul-terminated UTF-8 encoded error message
+ ** describing the last IO error to have occurred within the calling
+ ** thread.
+ **
+ ** If the error message is too large for the supplied buffer,
+ ** it should be truncated. The return value of xGetLastError
+ ** is zero if the error message fits in the buffer, or non-zero
+ ** otherwise (if the message was truncated). If non-zero is returned,
+ ** then it is not necessary to include the nul-terminator character
+ ** in the output buffer.
+ **
+ ** Not supplying an error message will have no adverse effect
+ ** on SQLite. It is fine to have an implementation that never
+ ** returns an error message:
+ **
+ ** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ ** assert(zBuf[0]=='\0');
+ ** return 0;
+ ** }
+ **
+ ** However if an error message is supplied, it will be incorporated
+ ** by sqlite into the error message available to the user using
+ ** sqlite3_errmsg(), possibly making IO errors easier to debug.
+ */
+ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ UNUSED_PARAMETER(pVfs);
+ return getLastErrorMsg(nBuf, zBuf);
+ }
+
+
+
/*
** Initialize and deinitialize the operating system interface.
*/
int sqlite3_os_init(void){
static sqlite3_vfs winVfs = {
- 1, /* iVersion */
- sizeof(winFile), /* szOsFile */
- MAX_PATH, /* mxPathname */
- 0, /* pNext */
- "win32", /* zName */
- 0, /* pAppData */
-
- winOpen, /* xOpen */
- winDelete, /* xDelete */
- winAccess, /* xAccess */
- winFullPathname, /* xFullPathname */
- winDlOpen, /* xDlOpen */
- winDlError, /* xDlError */
- winDlSym, /* xDlSym */
- winDlClose, /* xDlClose */
- winRandomness, /* xRandomness */
- winSleep, /* xSleep */
- winCurrentTime, /* xCurrentTime */
- winGetLastError, /* xGetLastError */
+ 2, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ MAX_PATH, /* mxPathname */
+ 0, /* pNext */
+ "win32", /* zName */
+ 0, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
- winShmOpen, /* xShmOpen */
- winShmSize, /* xShmSize */
- winShmGet, /* xShmGet */
- winShmRelease, /* xShmRelease */
- winShmLock, /* xShmLock */
- winShmClose, /* xShmClose */
+ 0, /* xRename */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
};
sqlite3_vfs_register(&winVfs, 1);