mirror of
https://github.com/tursodatabase/libsql.git
synced 2024-12-16 03:19:00 +00:00
1064 lines
29 KiB
C
1064 lines
29 KiB
C
/*
|
|
** 2011-12-03
|
|
**
|
|
** The author disclaims copyright to this source code. In place of
|
|
** a legal notice, here is a blessing:
|
|
**
|
|
** May you do good and not evil.
|
|
** May you find forgiveness for yourself and forgive others.
|
|
** May you share freely, never taking more than you give.
|
|
**
|
|
*************************************************************************
|
|
**
|
|
** Win32-specific run-time environment implementation for LSM.
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include "windows.h"
|
|
|
|
#include "lsmInt.h"
|
|
|
|
/*
|
|
** An open file is an instance of the following object
|
|
*/
|
|
typedef struct Win32File Win32File;
|
|
struct Win32File {
|
|
lsm_env *pEnv; /* The run-time environment */
|
|
const char *zName; /* Full path to file */
|
|
|
|
HANDLE hFile; /* Open file handle */
|
|
HANDLE hShmFile; /* File handle for *-shm file */
|
|
|
|
SYSTEM_INFO sysInfo; /* Operating system information */
|
|
HANDLE hMap; /* File handle for mapping */
|
|
LPVOID pMap; /* Pointer to mapping of file fd */
|
|
size_t nMap; /* Size of mapping at pMap in bytes */
|
|
int nShm; /* Number of entries in ahShm[]/apShm[] */
|
|
LPHANDLE ahShm; /* Array of handles for shared mappings */
|
|
LPVOID *apShm; /* Array of 32K shared memory segments */
|
|
};
|
|
|
|
static char *win32ShmFile(Win32File *pWin32File){
|
|
char *zShm;
|
|
int nName = strlen(pWin32File->zName);
|
|
zShm = (char *)lsmMallocZero(pWin32File->pEnv, nName+4+1);
|
|
if( zShm ){
|
|
memcpy(zShm, pWin32File->zName, nName);
|
|
memcpy(&zShm[nName], "-shm", 5);
|
|
}
|
|
return zShm;
|
|
}
|
|
|
|
static int win32Sleep(int us){
|
|
Sleep((us + 999) / 1000);
|
|
return LSM_OK;
|
|
}
|
|
|
|
/*
|
|
** The number of times that an I/O operation will be retried following a
|
|
** locking error - probably caused by antivirus software. Also the initial
|
|
** delay before the first retry. The delay increases linearly with each
|
|
** retry.
|
|
*/
|
|
#ifndef LSM_WIN32_IOERR_RETRY
|
|
# define LSM_WIN32_IOERR_RETRY 10
|
|
#endif
|
|
#ifndef LSM_WIN32_IOERR_RETRY_DELAY
|
|
# define LSM_WIN32_IOERR_RETRY_DELAY 25000
|
|
#endif
|
|
static int win32IoerrRetry = LSM_WIN32_IOERR_RETRY;
|
|
static int win32IoerrRetryDelay = LSM_WIN32_IOERR_RETRY_DELAY;
|
|
|
|
/*
|
|
** The "win32IoerrCanRetry1" macro is used to determine if a particular
|
|
** I/O error code obtained via GetLastError() is eligible to be retried.
|
|
** It must accept the error code DWORD as its only argument and should
|
|
** return non-zero if the error code is transient in nature and the
|
|
** operation responsible for generating the original error might succeed
|
|
** upon being retried. The argument to this macro should be a variable.
|
|
**
|
|
** Additionally, a macro named "win32IoerrCanRetry2" may be defined. If
|
|
** it is defined, it will be consulted only when the macro
|
|
** "win32IoerrCanRetry1" returns zero. The "win32IoerrCanRetry2" macro
|
|
** is completely optional and may be used to include additional error
|
|
** codes in the set that should result in the failing I/O operation being
|
|
** retried by the caller. If defined, the "win32IoerrCanRetry2" macro
|
|
** must exhibit external semantics identical to those of the
|
|
** "win32IoerrCanRetry1" macro.
|
|
*/
|
|
#if !defined(win32IoerrCanRetry1)
|
|
#define win32IoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \
|
|
((a)==ERROR_SHARING_VIOLATION) || \
|
|
((a)==ERROR_LOCK_VIOLATION) || \
|
|
((a)==ERROR_DEV_NOT_EXIST) || \
|
|
((a)==ERROR_NETNAME_DELETED) || \
|
|
((a)==ERROR_SEM_TIMEOUT) || \
|
|
((a)==ERROR_NETWORK_UNREACHABLE))
|
|
#endif
|
|
|
|
/*
|
|
** If an I/O error occurs, invoke this routine to see if it should be
|
|
** retried. Return TRUE to retry. Return FALSE to give up with an
|
|
** error.
|
|
*/
|
|
static int win32RetryIoerr(
|
|
lsm_env *pEnv,
|
|
int *pnRetry
|
|
){
|
|
DWORD lastErrno;
|
|
if( *pnRetry>=win32IoerrRetry ){
|
|
return 0;
|
|
}
|
|
lastErrno = GetLastError();
|
|
if( win32IoerrCanRetry1(lastErrno) ){
|
|
win32Sleep(win32IoerrRetryDelay*(1+*pnRetry));
|
|
++*pnRetry;
|
|
return 1;
|
|
}
|
|
#if defined(win32IoerrCanRetry2)
|
|
else if( win32IoerrCanRetry2(lastErrno) ){
|
|
win32Sleep(win32IoerrRetryDelay*(1+*pnRetry));
|
|
++*pnRetry;
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Convert a UTF-8 string to Microsoft Unicode.
|
|
**
|
|
** Space to hold the returned string is obtained from lsmMalloc().
|
|
*/
|
|
static LPWSTR win32Utf8ToUnicode(lsm_env *pEnv, const char *zText){
|
|
int nChar;
|
|
LPWSTR zWideText;
|
|
|
|
nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
|
|
if( nChar==0 ){
|
|
return 0;
|
|
}
|
|
zWideText = lsmMallocZero(pEnv, nChar * sizeof(WCHAR));
|
|
if( zWideText==0 ){
|
|
return 0;
|
|
}
|
|
nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText, nChar);
|
|
if( nChar==0 ){
|
|
lsmFree(pEnv, zWideText);
|
|
zWideText = 0;
|
|
}
|
|
return zWideText;
|
|
}
|
|
|
|
/*
|
|
** Convert a Microsoft Unicode string to UTF-8.
|
|
**
|
|
** Space to hold the returned string is obtained from lsmMalloc().
|
|
*/
|
|
static char *win32UnicodeToUtf8(lsm_env *pEnv, LPCWSTR zWideText){
|
|
int nByte;
|
|
char *zText;
|
|
|
|
nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
|
|
if( nByte == 0 ){
|
|
return 0;
|
|
}
|
|
zText = lsmMallocZero(pEnv, nByte);
|
|
if( zText==0 ){
|
|
return 0;
|
|
}
|
|
nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte, 0, 0);
|
|
if( nByte == 0 ){
|
|
lsmFree(pEnv, zText);
|
|
zText = 0;
|
|
}
|
|
return zText;
|
|
}
|
|
|
|
#if !defined(win32IsNotFound)
|
|
#define win32IsNotFound(a) (((a)==ERROR_FILE_NOT_FOUND) || \
|
|
((a)==ERROR_PATH_NOT_FOUND))
|
|
#endif
|
|
|
|
static int win32Open(
|
|
lsm_env *pEnv,
|
|
const char *zFile,
|
|
int flags,
|
|
LPHANDLE phFile
|
|
){
|
|
int rc;
|
|
LPWSTR zConverted;
|
|
|
|
zConverted = win32Utf8ToUnicode(pEnv, zFile);
|
|
if( zConverted==0 ){
|
|
rc = LSM_NOMEM_BKPT;
|
|
}else{
|
|
int bReadonly = (flags & LSM_OPEN_READONLY);
|
|
DWORD dwDesiredAccess;
|
|
DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
DWORD dwCreationDisposition;
|
|
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
HANDLE hFile;
|
|
int nRetry = 0;
|
|
if( bReadonly ){
|
|
dwDesiredAccess = GENERIC_READ;
|
|
dwCreationDisposition = OPEN_EXISTING;
|
|
}else{
|
|
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
|
dwCreationDisposition = OPEN_ALWAYS;
|
|
}
|
|
while( (hFile = CreateFileW((LPCWSTR)zConverted,
|
|
dwDesiredAccess,
|
|
dwShareMode, NULL,
|
|
dwCreationDisposition,
|
|
dwFlagsAndAttributes,
|
|
NULL))==INVALID_HANDLE_VALUE &&
|
|
win32RetryIoerr(pEnv, &nRetry) ){
|
|
/* Noop */
|
|
}
|
|
lsmFree(pEnv, zConverted);
|
|
if( hFile!=INVALID_HANDLE_VALUE ){
|
|
*phFile = hFile;
|
|
rc = LSM_OK;
|
|
}else{
|
|
if( win32IsNotFound(GetLastError()) ){
|
|
rc = lsmErrorBkpt(LSM_IOERR_NOENT);
|
|
}else{
|
|
rc = LSM_IOERR_BKPT;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int lsmWin32OsOpen(
|
|
lsm_env *pEnv,
|
|
const char *zFile,
|
|
int flags,
|
|
lsm_file **ppFile
|
|
){
|
|
int rc = LSM_OK;
|
|
Win32File *pWin32File;
|
|
|
|
pWin32File = lsmMallocZero(pEnv, sizeof(Win32File));
|
|
if( pWin32File==0 ){
|
|
rc = LSM_NOMEM_BKPT;
|
|
}else{
|
|
HANDLE hFile = NULL;
|
|
|
|
rc = win32Open(pEnv, zFile, flags, &hFile);
|
|
if( rc==LSM_OK ){
|
|
memset(&pWin32File->sysInfo, 0, sizeof(SYSTEM_INFO));
|
|
GetSystemInfo(&pWin32File->sysInfo);
|
|
pWin32File->pEnv = pEnv;
|
|
pWin32File->zName = zFile;
|
|
pWin32File->hFile = hFile;
|
|
}else{
|
|
lsmFree(pEnv, pWin32File);
|
|
pWin32File = 0;
|
|
}
|
|
}
|
|
*ppFile = (lsm_file *)pWin32File;
|
|
return rc;
|
|
}
|
|
|
|
static int lsmWin32OsWrite(
|
|
lsm_file *pFile, /* File to write to */
|
|
lsm_i64 iOff, /* Offset to write to */
|
|
void *pData, /* Write data from this buffer */
|
|
int nData /* Bytes of data to write */
|
|
){
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
OVERLAPPED overlapped; /* The offset for WriteFile. */
|
|
u8 *aRem = (u8 *)pData; /* Data yet to be written */
|
|
int nRem = nData; /* Number of bytes yet to be written */
|
|
int nRetry = 0; /* Number of retrys */
|
|
|
|
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
|
overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF);
|
|
overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF);
|
|
while( nRem>0 ){
|
|
DWORD nWrite = 0; /* Bytes written using WriteFile */
|
|
if( !WriteFile(pWin32File->hFile, aRem, nRem, &nWrite, &overlapped) ){
|
|
if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue;
|
|
break;
|
|
}
|
|
assert( nWrite==0 || nWrite<=(DWORD)nRem );
|
|
if( nWrite==0 || nWrite>(DWORD)nRem ){
|
|
break;
|
|
}
|
|
iOff += nWrite;
|
|
overlapped.Offset = (LONG)(iOff & 0xFFFFFFFF);
|
|
overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF);
|
|
aRem += nWrite;
|
|
nRem -= nWrite;
|
|
}
|
|
if( nRem!=0 ) return LSM_IOERR_BKPT;
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int win32Truncate(
|
|
HANDLE hFile,
|
|
lsm_i64 nSize
|
|
){
|
|
LARGE_INTEGER offset;
|
|
offset.QuadPart = nSize;
|
|
if( !SetFilePointerEx(hFile, offset, 0, FILE_BEGIN) ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
if (!SetEndOfFile(hFile) ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int lsmWin32OsTruncate(
|
|
lsm_file *pFile, /* File to write to */
|
|
lsm_i64 nSize /* Size to truncate file to */
|
|
){
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
return win32Truncate(pWin32File->hFile, nSize);
|
|
}
|
|
|
|
static int lsmWin32OsRead(
|
|
lsm_file *pFile, /* File to read from */
|
|
lsm_i64 iOff, /* Offset to read from */
|
|
void *pData, /* Read data into this buffer */
|
|
int nData /* Bytes of data to read */
|
|
){
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
OVERLAPPED overlapped; /* The offset for ReadFile */
|
|
DWORD nRead = 0; /* Bytes read using ReadFile */
|
|
int nRetry = 0; /* Number of retrys */
|
|
|
|
memset(&overlapped, 0, sizeof(OVERLAPPED));
|
|
overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF);
|
|
overlapped.OffsetHigh = (LONG)((iOff>>32) & 0X7FFFFFFF);
|
|
while( !ReadFile(pWin32File->hFile, pData, nData, &nRead, &overlapped) &&
|
|
GetLastError()!=ERROR_HANDLE_EOF ){
|
|
if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue;
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
if( nRead<(DWORD)nData ){
|
|
/* Unread parts of the buffer must be zero-filled */
|
|
memset(&((char*)pData)[nRead], 0, nData - nRead);
|
|
}
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int lsmWin32OsSync(lsm_file *pFile){
|
|
int rc = LSM_OK;
|
|
|
|
#ifndef LSM_NO_SYNC
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
|
|
if( pWin32File->pMap!=NULL ){
|
|
if( !FlushViewOfFile(pWin32File->pMap, 0) ){
|
|
rc = LSM_IOERR_BKPT;
|
|
}
|
|
}
|
|
if( rc==LSM_OK && !FlushFileBuffers(pWin32File->hFile) ){
|
|
rc = LSM_IOERR_BKPT;
|
|
}
|
|
#else
|
|
unused_parameter(pFile);
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int lsmWin32OsSectorSize(lsm_file *pFile){
|
|
return 512;
|
|
}
|
|
|
|
static void win32Unmap(Win32File *pWin32File){
|
|
if( pWin32File->pMap!=NULL ){
|
|
UnmapViewOfFile(pWin32File->pMap);
|
|
pWin32File->pMap = NULL;
|
|
pWin32File->nMap = 0;
|
|
}
|
|
if( pWin32File->hMap!=NULL ){
|
|
CloseHandle(pWin32File->hMap);
|
|
pWin32File->hMap = NULL;
|
|
}
|
|
}
|
|
|
|
static int lsmWin32OsRemap(
|
|
lsm_file *pFile,
|
|
lsm_i64 iMin,
|
|
void **ppOut,
|
|
lsm_i64 *pnOut
|
|
){
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
|
|
/* If the file is between 0 and 2MB in size, extend it in chunks of 256K.
|
|
** Thereafter, in chunks of 1MB at a time. */
|
|
const int aIncrSz[] = {256*1024, 1024*1024};
|
|
int nIncrSz = aIncrSz[iMin>(2*1024*1024)];
|
|
|
|
*ppOut = NULL;
|
|
*pnOut = 0;
|
|
|
|
win32Unmap(pWin32File);
|
|
if( iMin>=0 ){
|
|
LARGE_INTEGER fileSize;
|
|
DWORD dwSizeHigh;
|
|
DWORD dwSizeLow;
|
|
HANDLE hMap;
|
|
LPVOID pMap;
|
|
memset(&fileSize, 0, sizeof(LARGE_INTEGER));
|
|
if( !GetFileSizeEx(pWin32File->hFile, &fileSize) ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
assert( fileSize.QuadPart>=0 );
|
|
if( fileSize.QuadPart<iMin ){
|
|
int rc;
|
|
fileSize.QuadPart = ((iMin + nIncrSz-1) / nIncrSz) * nIncrSz;
|
|
rc = lsmWin32OsTruncate(pFile, fileSize.QuadPart);
|
|
if( rc!=LSM_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
dwSizeLow = (DWORD)(fileSize.QuadPart & 0xFFFFFFFF);
|
|
dwSizeHigh = (DWORD)((fileSize.QuadPart & 0x7FFFFFFFFFFFFFFF) >> 32);
|
|
hMap = CreateFileMappingW(pWin32File->hFile, NULL, PAGE_READWRITE,
|
|
dwSizeHigh, dwSizeLow, NULL);
|
|
if( hMap==NULL ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
pWin32File->hMap = hMap;
|
|
assert( fileSize.QuadPart<=0xFFFFFFFF );
|
|
pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0,
|
|
(SIZE_T)fileSize.QuadPart);
|
|
if( pMap==NULL ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
pWin32File->pMap = pMap;
|
|
pWin32File->nMap = (SIZE_T)fileSize.QuadPart;
|
|
}
|
|
*ppOut = pWin32File->pMap;
|
|
*pnOut = pWin32File->nMap;
|
|
return LSM_OK;
|
|
}
|
|
|
|
static BOOL win32IsDriveLetterAndColon(
|
|
const char *zPathname
|
|
){
|
|
return ( isalpha(zPathname[0]) && zPathname[1]==':' );
|
|
}
|
|
|
|
static int lsmWin32OsFullpath(
|
|
lsm_env *pEnv,
|
|
const char *zName,
|
|
char *zOut,
|
|
int *pnOut
|
|
){
|
|
DWORD nByte;
|
|
void *zConverted;
|
|
LPWSTR zTempWide;
|
|
char *zTempUtf8;
|
|
|
|
if( zName[0]=='/' && win32IsDriveLetterAndColon(zName+1) ){
|
|
zName++;
|
|
}
|
|
zConverted = win32Utf8ToUnicode(pEnv, zName);
|
|
if( zConverted==0 ){
|
|
return LSM_NOMEM_BKPT;
|
|
}
|
|
nByte = GetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
|
|
if( nByte==0 ){
|
|
lsmFree(pEnv, zConverted);
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
nByte += 3;
|
|
zTempWide = lsmMallocZero(pEnv, nByte * sizeof(zTempWide[0]));
|
|
if( zTempWide==0 ){
|
|
lsmFree(pEnv, zConverted);
|
|
return LSM_NOMEM_BKPT;
|
|
}
|
|
nByte = GetFullPathNameW((LPCWSTR)zConverted, nByte, zTempWide, 0);
|
|
if( nByte==0 ){
|
|
lsmFree(pEnv, zConverted);
|
|
lsmFree(pEnv, zTempWide);
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
lsmFree(pEnv, zConverted);
|
|
zTempUtf8 = win32UnicodeToUtf8(pEnv, zTempWide);
|
|
lsmFree(pEnv, zTempWide);
|
|
if( zTempUtf8 ){
|
|
int nOut = *pnOut;
|
|
int nLen = strlen(zTempUtf8) + 1;
|
|
if( nLen<=nOut ){
|
|
snprintf(zOut, nOut, "%s", zTempUtf8);
|
|
}
|
|
lsmFree(pEnv, zTempUtf8);
|
|
*pnOut = nLen;
|
|
return LSM_OK;
|
|
}else{
|
|
return LSM_NOMEM_BKPT;
|
|
}
|
|
}
|
|
|
|
static int lsmWin32OsFileid(
|
|
lsm_file *pFile,
|
|
void *pBuf,
|
|
int *pnBuf
|
|
){
|
|
int nBuf;
|
|
int nReq;
|
|
u8 *pBuf2 = (u8 *)pBuf;
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
BY_HANDLE_FILE_INFORMATION fileInfo;
|
|
|
|
nBuf = *pnBuf;
|
|
nReq = (sizeof(fileInfo.dwVolumeSerialNumber) +
|
|
sizeof(fileInfo.nFileIndexHigh) +
|
|
sizeof(fileInfo.nFileIndexLow));
|
|
*pnBuf = nReq;
|
|
if( nReq>nBuf ) return LSM_OK;
|
|
memset(&fileInfo, 0, sizeof(BY_HANDLE_FILE_INFORMATION));
|
|
if( !GetFileInformationByHandle(pWin32File->hFile, &fileInfo) ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
nReq = sizeof(fileInfo.dwVolumeSerialNumber);
|
|
memcpy(pBuf2, &fileInfo.dwVolumeSerialNumber, nReq);
|
|
pBuf2 += nReq;
|
|
nReq = sizeof(fileInfo.nFileIndexHigh);
|
|
memcpy(pBuf, &fileInfo.nFileIndexHigh, nReq);
|
|
pBuf2 += nReq;
|
|
nReq = sizeof(fileInfo.nFileIndexLow);
|
|
memcpy(pBuf2, &fileInfo.nFileIndexLow, nReq);
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int win32Delete(
|
|
lsm_env *pEnv,
|
|
const char *zFile
|
|
){
|
|
int rc;
|
|
LPWSTR zConverted;
|
|
|
|
zConverted = win32Utf8ToUnicode(pEnv, zFile);
|
|
if( zConverted==0 ){
|
|
rc = LSM_NOMEM_BKPT;
|
|
}else{
|
|
int nRetry = 0;
|
|
DWORD attr;
|
|
|
|
do {
|
|
attr = GetFileAttributesW(zConverted);
|
|
if ( attr==INVALID_FILE_ATTRIBUTES ){
|
|
rc = LSM_IOERR_BKPT;
|
|
break;
|
|
}
|
|
if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
|
|
rc = LSM_IOERR_BKPT; /* Files only. */
|
|
break;
|
|
}
|
|
if ( DeleteFileW(zConverted) ){
|
|
rc = LSM_OK; /* Deleted OK. */
|
|
break;
|
|
}
|
|
if ( !win32RetryIoerr(pEnv, &nRetry) ){
|
|
rc = LSM_IOERR_BKPT; /* No more retries. */
|
|
break;
|
|
}
|
|
}while( 1 );
|
|
}
|
|
lsmFree(pEnv, zConverted);
|
|
return rc;
|
|
}
|
|
|
|
static int lsmWin32OsUnlink(lsm_env *pEnv, const char *zFile){
|
|
return win32Delete(pEnv, zFile);
|
|
}
|
|
|
|
#if !defined(win32IsLockBusy)
|
|
#define win32IsLockBusy(a) (((a)==ERROR_LOCK_VIOLATION) || \
|
|
((a)==ERROR_IO_PENDING))
|
|
#endif
|
|
|
|
static int win32LockFile(
|
|
Win32File *pWin32File,
|
|
int iLock,
|
|
int nLock,
|
|
int eType
|
|
){
|
|
OVERLAPPED ovlp;
|
|
|
|
assert( LSM_LOCK_UNLOCK==0 );
|
|
assert( LSM_LOCK_SHARED==1 );
|
|
assert( LSM_LOCK_EXCL==2 );
|
|
assert( eType>=LSM_LOCK_UNLOCK && eType<=LSM_LOCK_EXCL );
|
|
assert( nLock>=0 );
|
|
assert( iLock>0 && iLock<=32 );
|
|
|
|
memset(&ovlp, 0, sizeof(OVERLAPPED));
|
|
ovlp.Offset = (4096-iLock-nLock+1);
|
|
if( eType>LSM_LOCK_UNLOCK ){
|
|
DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
|
|
if( eType>=LSM_LOCK_EXCL ) flags |= LOCKFILE_EXCLUSIVE_LOCK;
|
|
if( !LockFileEx(pWin32File->hFile, flags, 0, (DWORD)nLock, 0, &ovlp) ){
|
|
if( win32IsLockBusy(GetLastError()) ){
|
|
return LSM_BUSY;
|
|
}else{
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
}
|
|
}else{
|
|
if( !UnlockFileEx(pWin32File->hFile, 0, (DWORD)nLock, 0, &ovlp) ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
}
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
return win32LockFile(pWin32File, iLock, 1, eType);
|
|
}
|
|
|
|
static int lsmWin32OsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
|
|
int rc;
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
rc = win32LockFile(pWin32File, iLock, nLock, eType);
|
|
if( rc!=LSM_OK ) return rc;
|
|
win32LockFile(pWin32File, iLock, nLock, LSM_LOCK_UNLOCK);
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int lsmWin32OsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
|
|
int rc;
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
int iOffset = iChunk * sz;
|
|
int iOffsetShift = iOffset % pWin32File->sysInfo.dwAllocationGranularity;
|
|
int nNew = iChunk + 1;
|
|
lsm_i64 nReq = nNew * sz;
|
|
|
|
*ppShm = NULL;
|
|
assert( sz>=0 );
|
|
assert( sz==LSM_SHM_CHUNK_SIZE );
|
|
if( iChunk>=pWin32File->nShm ){
|
|
LPHANDLE ahNew;
|
|
LPVOID *apNew;
|
|
LARGE_INTEGER fileSize;
|
|
|
|
/* If the shared-memory file has not been opened, open it now. */
|
|
if( pWin32File->hShmFile==NULL ){
|
|
char *zShm = win32ShmFile(pWin32File);
|
|
if( !zShm ) return LSM_NOMEM_BKPT;
|
|
rc = win32Open(pWin32File->pEnv, zShm, 0, &pWin32File->hShmFile);
|
|
lsmFree(pWin32File->pEnv, zShm);
|
|
if( rc!=LSM_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* If the shared-memory file is not large enough to contain the
|
|
** requested chunk, cause it to grow. */
|
|
memset(&fileSize, 0, sizeof(LARGE_INTEGER));
|
|
if( !GetFileSizeEx(pWin32File->hShmFile, &fileSize) ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
assert( fileSize.QuadPart>=0 );
|
|
if( fileSize.QuadPart<nReq ){
|
|
rc = win32Truncate(pWin32File->hShmFile, nReq);
|
|
if( rc!=LSM_OK ){
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
ahNew = (LPHANDLE)lsmMallocZero(pWin32File->pEnv, sizeof(HANDLE) * nNew);
|
|
if( !ahNew ) return LSM_NOMEM_BKPT;
|
|
apNew = (LPVOID *)lsmMallocZero(pWin32File->pEnv, sizeof(LPVOID) * nNew);
|
|
if( !apNew ){
|
|
lsmFree(pWin32File->pEnv, ahNew);
|
|
return LSM_NOMEM_BKPT;
|
|
}
|
|
memcpy(ahNew, pWin32File->ahShm, sizeof(HANDLE) * pWin32File->nShm);
|
|
memcpy(apNew, pWin32File->apShm, sizeof(LPVOID) * pWin32File->nShm);
|
|
lsmFree(pWin32File->pEnv, pWin32File->ahShm);
|
|
pWin32File->ahShm = ahNew;
|
|
lsmFree(pWin32File->pEnv, pWin32File->apShm);
|
|
pWin32File->apShm = apNew;
|
|
pWin32File->nShm = nNew;
|
|
}
|
|
|
|
if( pWin32File->ahShm[iChunk]==NULL ){
|
|
HANDLE hMap;
|
|
assert( nReq<=0xFFFFFFFF );
|
|
hMap = CreateFileMappingW(pWin32File->hShmFile, NULL, PAGE_READWRITE, 0,
|
|
(DWORD)nReq, NULL);
|
|
if( hMap==NULL ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
pWin32File->ahShm[iChunk] = hMap;
|
|
}
|
|
if( pWin32File->apShm[iChunk]==NULL ){
|
|
LPVOID pMap;
|
|
pMap = MapViewOfFile(pWin32File->ahShm[iChunk],
|
|
FILE_MAP_WRITE | FILE_MAP_READ, 0,
|
|
iOffset - iOffsetShift, sz + iOffsetShift);
|
|
if( pMap==NULL ){
|
|
return LSM_IOERR_BKPT;
|
|
}
|
|
pWin32File->apShm[iChunk] = pMap;
|
|
}
|
|
if( iOffsetShift!=0 ){
|
|
char *p = (char *)pWin32File->apShm[iChunk];
|
|
*ppShm = (void *)&p[iOffsetShift];
|
|
}else{
|
|
*ppShm = pWin32File->apShm[iChunk];
|
|
}
|
|
return LSM_OK;
|
|
}
|
|
|
|
static void lsmWin32OsShmBarrier(void){
|
|
MemoryBarrier();
|
|
}
|
|
|
|
static int lsmWin32OsShmUnmap(lsm_file *pFile, int bDelete){
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
|
|
if( pWin32File->hShmFile!=NULL ){
|
|
int i;
|
|
for(i=0; i<pWin32File->nShm; i++){
|
|
if( pWin32File->apShm[i]!=NULL ){
|
|
UnmapViewOfFile(pWin32File->apShm[i]);
|
|
pWin32File->apShm[i] = NULL;
|
|
}
|
|
if( pWin32File->ahShm[i]!=NULL ){
|
|
CloseHandle(pWin32File->ahShm[i]);
|
|
pWin32File->ahShm[i] = NULL;
|
|
}
|
|
}
|
|
CloseHandle(pWin32File->hShmFile);
|
|
pWin32File->hShmFile = NULL;
|
|
if( bDelete ){
|
|
char *zShm = win32ShmFile(pWin32File);
|
|
if( zShm ){ win32Delete(pWin32File->pEnv, zShm); }
|
|
lsmFree(pWin32File->pEnv, zShm);
|
|
}
|
|
}
|
|
return LSM_OK;
|
|
}
|
|
|
|
#define MX_CLOSE_ATTEMPT 3
|
|
static int lsmWin32OsClose(lsm_file *pFile){
|
|
int rc;
|
|
int nRetry = 0;
|
|
Win32File *pWin32File = (Win32File *)pFile;
|
|
lsmWin32OsShmUnmap(pFile, 0);
|
|
win32Unmap(pWin32File);
|
|
do{
|
|
if( pWin32File->hFile==NULL ){
|
|
rc = LSM_IOERR_BKPT;
|
|
break;
|
|
}
|
|
rc = CloseHandle(pWin32File->hFile);
|
|
if( rc ){
|
|
pWin32File->hFile = NULL;
|
|
rc = LSM_OK;
|
|
break;
|
|
}
|
|
if( ++nRetry>=MX_CLOSE_ATTEMPT ){
|
|
rc = LSM_IOERR_BKPT;
|
|
break;
|
|
}
|
|
}while( 1 );
|
|
lsmFree(pWin32File->pEnv, pWin32File->ahShm);
|
|
lsmFree(pWin32File->pEnv, pWin32File->apShm);
|
|
lsmFree(pWin32File->pEnv, pWin32File);
|
|
return rc;
|
|
}
|
|
|
|
static int lsmWin32OsSleep(lsm_env *pEnv, int us){
|
|
unused_parameter(pEnv);
|
|
return win32Sleep(us);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Memory allocation routines.
|
|
*/
|
|
|
|
static void *lsmWin32OsMalloc(lsm_env *pEnv, size_t N){
|
|
assert( HeapValidate(GetProcessHeap(), 0, NULL) );
|
|
return HeapAlloc(GetProcessHeap(), 0, (SIZE_T)N);
|
|
}
|
|
|
|
static void lsmWin32OsFree(lsm_env *pEnv, void *p){
|
|
assert( HeapValidate(GetProcessHeap(), 0, NULL) );
|
|
if( p ){
|
|
HeapFree(GetProcessHeap(), 0, p);
|
|
}
|
|
}
|
|
|
|
static void *lsmWin32OsRealloc(lsm_env *pEnv, void *p, size_t N){
|
|
unsigned char *m = (unsigned char *)p;
|
|
assert( HeapValidate(GetProcessHeap(), 0, NULL) );
|
|
if( 1>N ){
|
|
lsmWin32OsFree(pEnv, p);
|
|
return NULL;
|
|
}else if( NULL==p ){
|
|
return lsmWin32OsMalloc(pEnv, N);
|
|
}else{
|
|
#if 0 /* arguable: don't shrink */
|
|
SIZE_T sz = HeapSize(GetProcessHeap(), 0, m);
|
|
if( sz>=(SIZE_T)N ){
|
|
return p;
|
|
}
|
|
#endif
|
|
return HeapReAlloc(GetProcessHeap(), 0, m, N);
|
|
}
|
|
}
|
|
|
|
static size_t lsmWin32OsMSize(lsm_env *pEnv, void *p){
|
|
assert( HeapValidate(GetProcessHeap(), 0, NULL) );
|
|
return (size_t)HeapSize(GetProcessHeap(), 0, p);
|
|
}
|
|
|
|
|
|
#ifdef LSM_MUTEX_WIN32
|
|
/*************************************************************************
|
|
** Mutex methods for Win32 based systems. If LSM_MUTEX_WIN32 is
|
|
** missing then a no-op implementation of mutexes found below will be
|
|
** used instead.
|
|
*/
|
|
#include "windows.h"
|
|
|
|
typedef struct Win32Mutex Win32Mutex;
|
|
struct Win32Mutex {
|
|
lsm_env *pEnv;
|
|
CRITICAL_SECTION mutex;
|
|
#ifdef LSM_DEBUG
|
|
DWORD owner;
|
|
#endif
|
|
};
|
|
|
|
#ifndef WIN32_MUTEX_INITIALIZER
|
|
# define WIN32_MUTEX_INITIALIZER { 0 }
|
|
#endif
|
|
|
|
#ifdef LSM_DEBUG
|
|
# define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER, 0 }
|
|
#else
|
|
# define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER }
|
|
#endif
|
|
|
|
static int lsmWin32OsMutexStatic(
|
|
lsm_env *pEnv,
|
|
int iMutex,
|
|
lsm_mutex **ppStatic
|
|
){
|
|
static volatile LONG initialized = 0;
|
|
static Win32Mutex sMutex[2] = {
|
|
LSM_WIN32_STATIC_MUTEX,
|
|
LSM_WIN32_STATIC_MUTEX
|
|
};
|
|
|
|
assert( iMutex==LSM_MUTEX_GLOBAL || iMutex==LSM_MUTEX_HEAP );
|
|
assert( LSM_MUTEX_GLOBAL==1 && LSM_MUTEX_HEAP==2 );
|
|
|
|
if( InterlockedCompareExchange(&initialized, 1, 0)==0 ){
|
|
int i;
|
|
for(i=0; i<array_size(sMutex); i++){
|
|
InitializeCriticalSection(&sMutex[i].mutex);
|
|
}
|
|
}
|
|
*ppStatic = (lsm_mutex *)&sMutex[iMutex-1];
|
|
return LSM_OK;
|
|
}
|
|
|
|
static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
|
|
Win32Mutex *pMutex; /* Pointer to new mutex */
|
|
|
|
pMutex = (Win32Mutex *)lsmMallocZero(pEnv, sizeof(Win32Mutex));
|
|
if( !pMutex ) return LSM_NOMEM_BKPT;
|
|
|
|
pMutex->pEnv = pEnv;
|
|
InitializeCriticalSection(&pMutex->mutex);
|
|
|
|
*ppNew = (lsm_mutex *)pMutex;
|
|
return LSM_OK;
|
|
}
|
|
|
|
static void lsmWin32OsMutexDel(lsm_mutex *p){
|
|
Win32Mutex *pMutex = (Win32Mutex *)p;
|
|
DeleteCriticalSection(&pMutex->mutex);
|
|
lsmFree(pMutex->pEnv, pMutex);
|
|
}
|
|
|
|
static void lsmWin32OsMutexEnter(lsm_mutex *p){
|
|
Win32Mutex *pMutex = (Win32Mutex *)p;
|
|
EnterCriticalSection(&pMutex->mutex);
|
|
|
|
#ifdef LSM_DEBUG
|
|
assert( pMutex->owner!=GetCurrentThreadId() );
|
|
pMutex->owner = GetCurrentThreadId();
|
|
assert( pMutex->owner==GetCurrentThreadId() );
|
|
#endif
|
|
}
|
|
|
|
static int lsmWin32OsMutexTry(lsm_mutex *p){
|
|
BOOL bRet;
|
|
Win32Mutex *pMutex = (Win32Mutex *)p;
|
|
bRet = TryEnterCriticalSection(&pMutex->mutex);
|
|
#ifdef LSM_DEBUG
|
|
if( bRet ){
|
|
assert( pMutex->owner!=GetCurrentThreadId() );
|
|
pMutex->owner = GetCurrentThreadId();
|
|
assert( pMutex->owner==GetCurrentThreadId() );
|
|
}
|
|
#endif
|
|
return !bRet;
|
|
}
|
|
|
|
static void lsmWin32OsMutexLeave(lsm_mutex *p){
|
|
Win32Mutex *pMutex = (Win32Mutex *)p;
|
|
#ifdef LSM_DEBUG
|
|
assert( pMutex->owner==GetCurrentThreadId() );
|
|
pMutex->owner = 0;
|
|
assert( pMutex->owner!=GetCurrentThreadId() );
|
|
#endif
|
|
LeaveCriticalSection(&pMutex->mutex);
|
|
}
|
|
|
|
#ifdef LSM_DEBUG
|
|
static int lsmWin32OsMutexHeld(lsm_mutex *p){
|
|
Win32Mutex *pMutex = (Win32Mutex *)p;
|
|
return pMutex ? pMutex->owner==GetCurrentThreadId() : 1;
|
|
}
|
|
static int lsmWin32OsMutexNotHeld(lsm_mutex *p){
|
|
Win32Mutex *pMutex = (Win32Mutex *)p;
|
|
return pMutex ? pMutex->owner!=GetCurrentThreadId() : 1;
|
|
}
|
|
#endif
|
|
/*
|
|
** End of Win32 mutex implementation.
|
|
*************************************************************************/
|
|
#else
|
|
/*************************************************************************
|
|
** Noop mutex implementation
|
|
*/
|
|
typedef struct NoopMutex NoopMutex;
|
|
struct NoopMutex {
|
|
lsm_env *pEnv; /* Environment handle (for xFree()) */
|
|
int bHeld; /* True if mutex is held */
|
|
int bStatic; /* True for a static mutex */
|
|
};
|
|
static NoopMutex aStaticNoopMutex[2] = {
|
|
{0, 0, 1},
|
|
{0, 0, 1},
|
|
};
|
|
|
|
static int lsmWin32OsMutexStatic(
|
|
lsm_env *pEnv,
|
|
int iMutex,
|
|
lsm_mutex **ppStatic
|
|
){
|
|
assert( iMutex>=1 && iMutex<=(int)array_size(aStaticNoopMutex) );
|
|
*ppStatic = (lsm_mutex *)&aStaticNoopMutex[iMutex-1];
|
|
return LSM_OK;
|
|
}
|
|
static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
|
|
NoopMutex *p;
|
|
p = (NoopMutex *)lsmMallocZero(pEnv, sizeof(NoopMutex));
|
|
if( p ) p->pEnv = pEnv;
|
|
*ppNew = (lsm_mutex *)p;
|
|
return (p ? LSM_OK : LSM_NOMEM_BKPT);
|
|
}
|
|
static void lsmWin32OsMutexDel(lsm_mutex *pMutex) {
|
|
NoopMutex *p = (NoopMutex *)pMutex;
|
|
assert( p->bStatic==0 && p->pEnv );
|
|
lsmFree(p->pEnv, p);
|
|
}
|
|
static void lsmWin32OsMutexEnter(lsm_mutex *pMutex){
|
|
NoopMutex *p = (NoopMutex *)pMutex;
|
|
assert( p->bHeld==0 );
|
|
p->bHeld = 1;
|
|
}
|
|
static int lsmWin32OsMutexTry(lsm_mutex *pMutex){
|
|
NoopMutex *p = (NoopMutex *)pMutex;
|
|
assert( p->bHeld==0 );
|
|
p->bHeld = 1;
|
|
return 0;
|
|
}
|
|
static void lsmWin32OsMutexLeave(lsm_mutex *pMutex){
|
|
NoopMutex *p = (NoopMutex *)pMutex;
|
|
assert( p->bHeld==1 );
|
|
p->bHeld = 0;
|
|
}
|
|
#ifdef LSM_DEBUG
|
|
static int lsmWin32OsMutexHeld(lsm_mutex *pMutex){
|
|
NoopMutex *p = (NoopMutex *)pMutex;
|
|
return p ? p->bHeld : 1;
|
|
}
|
|
static int lsmWin32OsMutexNotHeld(lsm_mutex *pMutex){
|
|
NoopMutex *p = (NoopMutex *)pMutex;
|
|
return p ? !p->bHeld : 1;
|
|
}
|
|
#endif
|
|
/***************************************************************************/
|
|
#endif /* else LSM_MUTEX_NONE */
|
|
|
|
/* Without LSM_DEBUG, the MutexHeld tests are never called */
|
|
#ifndef LSM_DEBUG
|
|
# define lsmWin32OsMutexHeld 0
|
|
# define lsmWin32OsMutexNotHeld 0
|
|
#endif
|
|
|
|
lsm_env *lsm_default_env(void){
|
|
static lsm_env win32_env = {
|
|
sizeof(lsm_env), /* nByte */
|
|
1, /* iVersion */
|
|
/***** file i/o ******************/
|
|
0, /* pVfsCtx */
|
|
lsmWin32OsFullpath, /* xFullpath */
|
|
lsmWin32OsOpen, /* xOpen */
|
|
lsmWin32OsRead, /* xRead */
|
|
lsmWin32OsWrite, /* xWrite */
|
|
lsmWin32OsTruncate, /* xTruncate */
|
|
lsmWin32OsSync, /* xSync */
|
|
lsmWin32OsSectorSize, /* xSectorSize */
|
|
lsmWin32OsRemap, /* xRemap */
|
|
lsmWin32OsFileid, /* xFileid */
|
|
lsmWin32OsClose, /* xClose */
|
|
lsmWin32OsUnlink, /* xUnlink */
|
|
lsmWin32OsLock, /* xLock */
|
|
lsmWin32OsTestLock, /* xTestLock */
|
|
lsmWin32OsShmMap, /* xShmMap */
|
|
lsmWin32OsShmBarrier, /* xShmBarrier */
|
|
lsmWin32OsShmUnmap, /* xShmUnmap */
|
|
/***** memory allocation *********/
|
|
0, /* pMemCtx */
|
|
lsmWin32OsMalloc, /* xMalloc */
|
|
lsmWin32OsRealloc, /* xRealloc */
|
|
lsmWin32OsFree, /* xFree */
|
|
lsmWin32OsMSize, /* xSize */
|
|
/***** mutexes *********************/
|
|
0, /* pMutexCtx */
|
|
lsmWin32OsMutexStatic, /* xMutexStatic */
|
|
lsmWin32OsMutexNew, /* xMutexNew */
|
|
lsmWin32OsMutexDel, /* xMutexDel */
|
|
lsmWin32OsMutexEnter, /* xMutexEnter */
|
|
lsmWin32OsMutexTry, /* xMutexTry */
|
|
lsmWin32OsMutexLeave, /* xMutexLeave */
|
|
lsmWin32OsMutexHeld, /* xMutexHeld */
|
|
lsmWin32OsMutexNotHeld, /* xMutexNotHeld */
|
|
/***** other *********************/
|
|
lsmWin32OsSleep, /* xSleep */
|
|
};
|
|
return &win32_env;
|
|
}
|
|
|
|
#endif
|