Enhance the command-line completion extension to return the names of
[sqlite.git] / ext / lsm1 / lsm_win32.c
blob6c5d06b4c8e1db924b592fb480cb9da533bc5a6b
1 /*
2 ** 2011-12-03
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
13 ** Win32-specific run-time environment implementation for LSM.
16 #ifdef _WIN32
18 #include <assert.h>
19 #include <string.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <ctype.h>
26 #include "windows.h"
28 #include "lsmInt.h"
31 ** An open file is an instance of the following object
33 typedef struct Win32File Win32File;
34 struct Win32File {
35 lsm_env *pEnv; /* The run-time environment */
36 const char *zName; /* Full path to file */
38 HANDLE hFile; /* Open file handle */
39 HANDLE hShmFile; /* File handle for *-shm file */
41 SYSTEM_INFO sysInfo; /* Operating system information */
42 HANDLE hMap; /* File handle for mapping */
43 LPVOID pMap; /* Pointer to mapping of file fd */
44 size_t nMap; /* Size of mapping at pMap in bytes */
45 int nShm; /* Number of entries in ahShm[]/apShm[] */
46 LPHANDLE ahShm; /* Array of handles for shared mappings */
47 LPVOID *apShm; /* Array of 32K shared memory segments */
50 static char *win32ShmFile(Win32File *pWin32File){
51 char *zShm;
52 int nName = strlen(pWin32File->zName);
53 zShm = (char *)lsmMallocZero(pWin32File->pEnv, nName+4+1);
54 if( zShm ){
55 memcpy(zShm, pWin32File->zName, nName);
56 memcpy(&zShm[nName], "-shm", 5);
58 return zShm;
61 static int win32Sleep(int us){
62 Sleep((us + 999) / 1000);
63 return LSM_OK;
67 ** The number of times that an I/O operation will be retried following a
68 ** locking error - probably caused by antivirus software. Also the initial
69 ** delay before the first retry. The delay increases linearly with each
70 ** retry.
72 #ifndef LSM_WIN32_IOERR_RETRY
73 # define LSM_WIN32_IOERR_RETRY 10
74 #endif
75 #ifndef LSM_WIN32_IOERR_RETRY_DELAY
76 # define LSM_WIN32_IOERR_RETRY_DELAY 25000
77 #endif
78 static int win32IoerrRetry = LSM_WIN32_IOERR_RETRY;
79 static int win32IoerrRetryDelay = LSM_WIN32_IOERR_RETRY_DELAY;
82 ** The "win32IoerrCanRetry1" macro is used to determine if a particular
83 ** I/O error code obtained via GetLastError() is eligible to be retried.
84 ** It must accept the error code DWORD as its only argument and should
85 ** return non-zero if the error code is transient in nature and the
86 ** operation responsible for generating the original error might succeed
87 ** upon being retried. The argument to this macro should be a variable.
89 ** Additionally, a macro named "win32IoerrCanRetry2" may be defined. If
90 ** it is defined, it will be consulted only when the macro
91 ** "win32IoerrCanRetry1" returns zero. The "win32IoerrCanRetry2" macro
92 ** is completely optional and may be used to include additional error
93 ** codes in the set that should result in the failing I/O operation being
94 ** retried by the caller. If defined, the "win32IoerrCanRetry2" macro
95 ** must exhibit external semantics identical to those of the
96 ** "win32IoerrCanRetry1" macro.
98 #if !defined(win32IoerrCanRetry1)
99 #define win32IoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \
100 ((a)==ERROR_SHARING_VIOLATION) || \
101 ((a)==ERROR_LOCK_VIOLATION) || \
102 ((a)==ERROR_DEV_NOT_EXIST) || \
103 ((a)==ERROR_NETNAME_DELETED) || \
104 ((a)==ERROR_SEM_TIMEOUT) || \
105 ((a)==ERROR_NETWORK_UNREACHABLE))
106 #endif
109 ** If an I/O error occurs, invoke this routine to see if it should be
110 ** retried. Return TRUE to retry. Return FALSE to give up with an
111 ** error.
113 static int win32RetryIoerr(
114 lsm_env *pEnv,
115 int *pnRetry
117 DWORD lastErrno;
118 if( *pnRetry>=win32IoerrRetry ){
119 return 0;
121 lastErrno = GetLastError();
122 if( win32IoerrCanRetry1(lastErrno) ){
123 win32Sleep(win32IoerrRetryDelay*(1+*pnRetry));
124 ++*pnRetry;
125 return 1;
127 #if defined(win32IoerrCanRetry2)
128 else if( win32IoerrCanRetry2(lastErrno) ){
129 win32Sleep(win32IoerrRetryDelay*(1+*pnRetry));
130 ++*pnRetry;
131 return 1;
133 #endif
134 return 0;
138 ** Convert a UTF-8 string to Microsoft Unicode.
140 ** Space to hold the returned string is obtained from lsmMalloc().
142 static LPWSTR win32Utf8ToUnicode(lsm_env *pEnv, const char *zText){
143 int nChar;
144 LPWSTR zWideText;
146 nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
147 if( nChar==0 ){
148 return 0;
150 zWideText = lsmMallocZero(pEnv, nChar * sizeof(WCHAR));
151 if( zWideText==0 ){
152 return 0;
154 nChar = MultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText, nChar);
155 if( nChar==0 ){
156 lsmFree(pEnv, zWideText);
157 zWideText = 0;
159 return zWideText;
163 ** Convert a Microsoft Unicode string to UTF-8.
165 ** Space to hold the returned string is obtained from lsmMalloc().
167 static char *win32UnicodeToUtf8(lsm_env *pEnv, LPCWSTR zWideText){
168 int nByte;
169 char *zText;
171 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
172 if( nByte == 0 ){
173 return 0;
175 zText = lsmMallocZero(pEnv, nByte);
176 if( zText==0 ){
177 return 0;
179 nByte = WideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte, 0, 0);
180 if( nByte == 0 ){
181 lsmFree(pEnv, zText);
182 zText = 0;
184 return zText;
187 #if !defined(win32IsNotFound)
188 #define win32IsNotFound(a) (((a)==ERROR_FILE_NOT_FOUND) || \
189 ((a)==ERROR_PATH_NOT_FOUND))
190 #endif
192 static int win32Open(
193 lsm_env *pEnv,
194 const char *zFile,
195 int flags,
196 LPHANDLE phFile
198 int rc;
199 LPWSTR zConverted;
201 zConverted = win32Utf8ToUnicode(pEnv, zFile);
202 if( zConverted==0 ){
203 rc = LSM_NOMEM_BKPT;
204 }else{
205 int bReadonly = (flags & LSM_OPEN_READONLY);
206 DWORD dwDesiredAccess;
207 DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
208 DWORD dwCreationDisposition;
209 DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
210 HANDLE hFile;
211 int nRetry = 0;
212 if( bReadonly ){
213 dwDesiredAccess = GENERIC_READ;
214 dwCreationDisposition = OPEN_EXISTING;
215 }else{
216 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
217 dwCreationDisposition = OPEN_ALWAYS;
219 while( (hFile = CreateFileW((LPCWSTR)zConverted,
220 dwDesiredAccess,
221 dwShareMode, NULL,
222 dwCreationDisposition,
223 dwFlagsAndAttributes,
224 NULL))==INVALID_HANDLE_VALUE &&
225 win32RetryIoerr(pEnv, &nRetry) ){
226 /* Noop */
228 lsmFree(pEnv, zConverted);
229 if( hFile!=INVALID_HANDLE_VALUE ){
230 *phFile = hFile;
231 rc = LSM_OK;
232 }else{
233 if( win32IsNotFound(GetLastError()) ){
234 rc = lsmErrorBkpt(LSM_IOERR_NOENT);
235 }else{
236 rc = LSM_IOERR_BKPT;
240 return rc;
243 static int lsmWin32OsOpen(
244 lsm_env *pEnv,
245 const char *zFile,
246 int flags,
247 lsm_file **ppFile
249 int rc = LSM_OK;
250 Win32File *pWin32File;
252 pWin32File = lsmMallocZero(pEnv, sizeof(Win32File));
253 if( pWin32File==0 ){
254 rc = LSM_NOMEM_BKPT;
255 }else{
256 HANDLE hFile = NULL;
258 rc = win32Open(pEnv, zFile, flags, &hFile);
259 if( rc==LSM_OK ){
260 memset(&pWin32File->sysInfo, 0, sizeof(SYSTEM_INFO));
261 GetSystemInfo(&pWin32File->sysInfo);
262 pWin32File->pEnv = pEnv;
263 pWin32File->zName = zFile;
264 pWin32File->hFile = hFile;
265 }else{
266 lsmFree(pEnv, pWin32File);
267 pWin32File = 0;
270 *ppFile = (lsm_file *)pWin32File;
271 return rc;
274 static int lsmWin32OsWrite(
275 lsm_file *pFile, /* File to write to */
276 lsm_i64 iOff, /* Offset to write to */
277 void *pData, /* Write data from this buffer */
278 int nData /* Bytes of data to write */
280 Win32File *pWin32File = (Win32File *)pFile;
281 OVERLAPPED overlapped; /* The offset for WriteFile. */
282 u8 *aRem = (u8 *)pData; /* Data yet to be written */
283 int nRem = nData; /* Number of bytes yet to be written */
284 int nRetry = 0; /* Number of retrys */
286 memset(&overlapped, 0, sizeof(OVERLAPPED));
287 overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF);
288 overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF);
289 while( nRem>0 ){
290 DWORD nWrite = 0; /* Bytes written using WriteFile */
291 if( !WriteFile(pWin32File->hFile, aRem, nRem, &nWrite, &overlapped) ){
292 if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue;
293 break;
295 assert( nWrite==0 || nWrite<=(DWORD)nRem );
296 if( nWrite==0 || nWrite>(DWORD)nRem ){
297 break;
299 iOff += nWrite;
300 overlapped.Offset = (LONG)(iOff & 0xFFFFFFFF);
301 overlapped.OffsetHigh = (LONG)((iOff>>32) & 0x7FFFFFFF);
302 aRem += nWrite;
303 nRem -= nWrite;
305 if( nRem!=0 ) return LSM_IOERR_BKPT;
306 return LSM_OK;
309 static int win32Truncate(
310 HANDLE hFile,
311 lsm_i64 nSize
313 LARGE_INTEGER offset;
314 offset.QuadPart = nSize;
315 if( !SetFilePointerEx(hFile, offset, 0, FILE_BEGIN) ){
316 return LSM_IOERR_BKPT;
318 if (!SetEndOfFile(hFile) ){
319 return LSM_IOERR_BKPT;
321 return LSM_OK;
324 static int lsmWin32OsTruncate(
325 lsm_file *pFile, /* File to write to */
326 lsm_i64 nSize /* Size to truncate file to */
328 Win32File *pWin32File = (Win32File *)pFile;
329 return win32Truncate(pWin32File->hFile, nSize);
332 static int lsmWin32OsRead(
333 lsm_file *pFile, /* File to read from */
334 lsm_i64 iOff, /* Offset to read from */
335 void *pData, /* Read data into this buffer */
336 int nData /* Bytes of data to read */
338 Win32File *pWin32File = (Win32File *)pFile;
339 OVERLAPPED overlapped; /* The offset for ReadFile */
340 DWORD nRead = 0; /* Bytes read using ReadFile */
341 int nRetry = 0; /* Number of retrys */
343 memset(&overlapped, 0, sizeof(OVERLAPPED));
344 overlapped.Offset = (LONG)(iOff & 0XFFFFFFFF);
345 overlapped.OffsetHigh = (LONG)((iOff>>32) & 0X7FFFFFFF);
346 while( !ReadFile(pWin32File->hFile, pData, nData, &nRead, &overlapped) &&
347 GetLastError()!=ERROR_HANDLE_EOF ){
348 if( win32RetryIoerr(pWin32File->pEnv, &nRetry) ) continue;
349 return LSM_IOERR_BKPT;
351 if( nRead<(DWORD)nData ){
352 /* Unread parts of the buffer must be zero-filled */
353 memset(&((char*)pData)[nRead], 0, nData - nRead);
355 return LSM_OK;
358 static int lsmWin32OsSync(lsm_file *pFile){
359 int rc = LSM_OK;
361 #ifndef LSM_NO_SYNC
362 Win32File *pWin32File = (Win32File *)pFile;
364 if( pWin32File->pMap!=NULL ){
365 if( !FlushViewOfFile(pWin32File->pMap, 0) ){
366 rc = LSM_IOERR_BKPT;
369 if( rc==LSM_OK && !FlushFileBuffers(pWin32File->hFile) ){
370 rc = LSM_IOERR_BKPT;
372 #else
373 unused_parameter(pFile);
374 #endif
376 return rc;
379 static int lsmWin32OsSectorSize(lsm_file *pFile){
380 return 512;
383 static void win32Unmap(Win32File *pWin32File){
384 if( pWin32File->pMap!=NULL ){
385 UnmapViewOfFile(pWin32File->pMap);
386 pWin32File->pMap = NULL;
387 pWin32File->nMap = 0;
389 if( pWin32File->hMap!=NULL ){
390 CloseHandle(pWin32File->hMap);
391 pWin32File->hMap = NULL;
395 static int lsmWin32OsRemap(
396 lsm_file *pFile,
397 lsm_i64 iMin,
398 void **ppOut,
399 lsm_i64 *pnOut
401 Win32File *pWin32File = (Win32File *)pFile;
403 /* If the file is between 0 and 2MB in size, extend it in chunks of 256K.
404 ** Thereafter, in chunks of 1MB at a time. */
405 const int aIncrSz[] = {256*1024, 1024*1024};
406 int nIncrSz = aIncrSz[iMin>(2*1024*1024)];
408 *ppOut = NULL;
409 *pnOut = 0;
411 win32Unmap(pWin32File);
412 if( iMin>=0 ){
413 LARGE_INTEGER fileSize;
414 DWORD dwSizeHigh;
415 DWORD dwSizeLow;
416 HANDLE hMap;
417 LPVOID pMap;
418 memset(&fileSize, 0, sizeof(LARGE_INTEGER));
419 if( !GetFileSizeEx(pWin32File->hFile, &fileSize) ){
420 return LSM_IOERR_BKPT;
422 assert( fileSize.QuadPart>=0 );
423 if( fileSize.QuadPart<iMin ){
424 int rc;
425 fileSize.QuadPart = ((iMin + nIncrSz-1) / nIncrSz) * nIncrSz;
426 rc = lsmWin32OsTruncate(pFile, fileSize.QuadPart);
427 if( rc!=LSM_OK ){
428 return rc;
431 dwSizeLow = (DWORD)(fileSize.QuadPart & 0xFFFFFFFF);
432 dwSizeHigh = (DWORD)((fileSize.QuadPart & 0x7FFFFFFFFFFFFFFF) >> 32);
433 hMap = CreateFileMappingW(pWin32File->hFile, NULL, PAGE_READWRITE,
434 dwSizeHigh, dwSizeLow, NULL);
435 if( hMap==NULL ){
436 return LSM_IOERR_BKPT;
438 pWin32File->hMap = hMap;
439 assert( fileSize.QuadPart<=0xFFFFFFFF );
440 pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0,
441 (SIZE_T)fileSize.QuadPart);
442 if( pMap==NULL ){
443 return LSM_IOERR_BKPT;
445 pWin32File->pMap = pMap;
446 pWin32File->nMap = (SIZE_T)fileSize.QuadPart;
448 *ppOut = pWin32File->pMap;
449 *pnOut = pWin32File->nMap;
450 return LSM_OK;
453 static BOOL win32IsDriveLetterAndColon(
454 const char *zPathname
456 return ( isalpha(zPathname[0]) && zPathname[1]==':' );
459 static int lsmWin32OsFullpath(
460 lsm_env *pEnv,
461 const char *zName,
462 char *zOut,
463 int *pnOut
465 DWORD nByte;
466 void *zConverted;
467 LPWSTR zTempWide;
468 char *zTempUtf8;
470 if( zName[0]=='/' && win32IsDriveLetterAndColon(zName+1) ){
471 zName++;
473 zConverted = win32Utf8ToUnicode(pEnv, zName);
474 if( zConverted==0 ){
475 return LSM_NOMEM_BKPT;
477 nByte = GetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
478 if( nByte==0 ){
479 lsmFree(pEnv, zConverted);
480 return LSM_IOERR_BKPT;
482 nByte += 3;
483 zTempWide = lsmMallocZero(pEnv, nByte * sizeof(zTempWide[0]));
484 if( zTempWide==0 ){
485 lsmFree(pEnv, zConverted);
486 return LSM_NOMEM_BKPT;
488 nByte = GetFullPathNameW((LPCWSTR)zConverted, nByte, zTempWide, 0);
489 if( nByte==0 ){
490 lsmFree(pEnv, zConverted);
491 lsmFree(pEnv, zTempWide);
492 return LSM_IOERR_BKPT;
494 lsmFree(pEnv, zConverted);
495 zTempUtf8 = win32UnicodeToUtf8(pEnv, zTempWide);
496 lsmFree(pEnv, zTempWide);
497 if( zTempUtf8 ){
498 int nOut = *pnOut;
499 int nLen = strlen(zTempUtf8) + 1;
500 if( nLen<=nOut ){
501 snprintf(zOut, nOut, "%s", zTempUtf8);
503 lsmFree(pEnv, zTempUtf8);
504 *pnOut = nLen;
505 return LSM_OK;
506 }else{
507 return LSM_NOMEM_BKPT;
511 static int lsmWin32OsFileid(
512 lsm_file *pFile,
513 void *pBuf,
514 int *pnBuf
516 int nBuf;
517 int nReq;
518 u8 *pBuf2 = (u8 *)pBuf;
519 Win32File *pWin32File = (Win32File *)pFile;
520 BY_HANDLE_FILE_INFORMATION fileInfo;
522 nBuf = *pnBuf;
523 nReq = (sizeof(fileInfo.dwVolumeSerialNumber) +
524 sizeof(fileInfo.nFileIndexHigh) +
525 sizeof(fileInfo.nFileIndexLow));
526 *pnBuf = nReq;
527 if( nReq>nBuf ) return LSM_OK;
528 memset(&fileInfo, 0, sizeof(BY_HANDLE_FILE_INFORMATION));
529 if( !GetFileInformationByHandle(pWin32File->hFile, &fileInfo) ){
530 return LSM_IOERR_BKPT;
532 nReq = sizeof(fileInfo.dwVolumeSerialNumber);
533 memcpy(pBuf2, &fileInfo.dwVolumeSerialNumber, nReq);
534 pBuf2 += nReq;
535 nReq = sizeof(fileInfo.nFileIndexHigh);
536 memcpy(pBuf, &fileInfo.nFileIndexHigh, nReq);
537 pBuf2 += nReq;
538 nReq = sizeof(fileInfo.nFileIndexLow);
539 memcpy(pBuf2, &fileInfo.nFileIndexLow, nReq);
540 return LSM_OK;
543 static int win32Delete(
544 lsm_env *pEnv,
545 const char *zFile
547 int rc;
548 LPWSTR zConverted;
550 zConverted = win32Utf8ToUnicode(pEnv, zFile);
551 if( zConverted==0 ){
552 rc = LSM_NOMEM_BKPT;
553 }else{
554 int nRetry = 0;
555 DWORD attr;
557 do {
558 attr = GetFileAttributesW(zConverted);
559 if ( attr==INVALID_FILE_ATTRIBUTES ){
560 rc = LSM_IOERR_BKPT;
561 break;
563 if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
564 rc = LSM_IOERR_BKPT; /* Files only. */
565 break;
567 if ( DeleteFileW(zConverted) ){
568 rc = LSM_OK; /* Deleted OK. */
569 break;
571 if ( !win32RetryIoerr(pEnv, &nRetry) ){
572 rc = LSM_IOERR_BKPT; /* No more retries. */
573 break;
575 }while( 1 );
577 lsmFree(pEnv, zConverted);
578 return rc;
581 static int lsmWin32OsUnlink(lsm_env *pEnv, const char *zFile){
582 return win32Delete(pEnv, zFile);
585 #if !defined(win32IsLockBusy)
586 #define win32IsLockBusy(a) (((a)==ERROR_LOCK_VIOLATION) || \
587 ((a)==ERROR_IO_PENDING))
588 #endif
590 static int win32LockFile(
591 Win32File *pWin32File,
592 int iLock,
593 int nLock,
594 int eType
596 OVERLAPPED ovlp;
598 assert( LSM_LOCK_UNLOCK==0 );
599 assert( LSM_LOCK_SHARED==1 );
600 assert( LSM_LOCK_EXCL==2 );
601 assert( eType>=LSM_LOCK_UNLOCK && eType<=LSM_LOCK_EXCL );
602 assert( nLock>=0 );
603 assert( iLock>0 && iLock<=32 );
605 memset(&ovlp, 0, sizeof(OVERLAPPED));
606 ovlp.Offset = (4096-iLock-nLock+1);
607 if( eType>LSM_LOCK_UNLOCK ){
608 DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
609 if( eType>=LSM_LOCK_EXCL ) flags |= LOCKFILE_EXCLUSIVE_LOCK;
610 if( !LockFileEx(pWin32File->hFile, flags, 0, (DWORD)nLock, 0, &ovlp) ){
611 if( win32IsLockBusy(GetLastError()) ){
612 return LSM_BUSY;
613 }else{
614 return LSM_IOERR_BKPT;
617 }else{
618 if( !UnlockFileEx(pWin32File->hFile, 0, (DWORD)nLock, 0, &ovlp) ){
619 return LSM_IOERR_BKPT;
622 return LSM_OK;
625 static int lsmWin32OsLock(lsm_file *pFile, int iLock, int eType){
626 Win32File *pWin32File = (Win32File *)pFile;
627 return win32LockFile(pWin32File, iLock, 1, eType);
630 static int lsmWin32OsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
631 int rc;
632 Win32File *pWin32File = (Win32File *)pFile;
633 rc = win32LockFile(pWin32File, iLock, nLock, eType);
634 if( rc!=LSM_OK ) return rc;
635 win32LockFile(pWin32File, iLock, nLock, LSM_LOCK_UNLOCK);
636 return LSM_OK;
639 static int lsmWin32OsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){
640 int rc;
641 Win32File *pWin32File = (Win32File *)pFile;
642 int iOffset = iChunk * sz;
643 int iOffsetShift = iOffset % pWin32File->sysInfo.dwAllocationGranularity;
644 int nNew = iChunk + 1;
645 lsm_i64 nReq = nNew * sz;
647 *ppShm = NULL;
648 assert( sz>=0 );
649 assert( sz==LSM_SHM_CHUNK_SIZE );
650 if( iChunk>=pWin32File->nShm ){
651 LPHANDLE ahNew;
652 LPVOID *apNew;
653 LARGE_INTEGER fileSize;
655 /* If the shared-memory file has not been opened, open it now. */
656 if( pWin32File->hShmFile==NULL ){
657 char *zShm = win32ShmFile(pWin32File);
658 if( !zShm ) return LSM_NOMEM_BKPT;
659 rc = win32Open(pWin32File->pEnv, zShm, 0, &pWin32File->hShmFile);
660 lsmFree(pWin32File->pEnv, zShm);
661 if( rc!=LSM_OK ){
662 return rc;
666 /* If the shared-memory file is not large enough to contain the
667 ** requested chunk, cause it to grow. */
668 memset(&fileSize, 0, sizeof(LARGE_INTEGER));
669 if( !GetFileSizeEx(pWin32File->hShmFile, &fileSize) ){
670 return LSM_IOERR_BKPT;
672 assert( fileSize.QuadPart>=0 );
673 if( fileSize.QuadPart<nReq ){
674 rc = win32Truncate(pWin32File->hShmFile, nReq);
675 if( rc!=LSM_OK ){
676 return rc;
680 ahNew = (LPHANDLE)lsmMallocZero(pWin32File->pEnv, sizeof(HANDLE) * nNew);
681 if( !ahNew ) return LSM_NOMEM_BKPT;
682 apNew = (LPVOID *)lsmMallocZero(pWin32File->pEnv, sizeof(LPVOID) * nNew);
683 if( !apNew ){
684 lsmFree(pWin32File->pEnv, ahNew);
685 return LSM_NOMEM_BKPT;
687 memcpy(ahNew, pWin32File->ahShm, sizeof(HANDLE) * pWin32File->nShm);
688 memcpy(apNew, pWin32File->apShm, sizeof(LPVOID) * pWin32File->nShm);
689 lsmFree(pWin32File->pEnv, pWin32File->ahShm);
690 pWin32File->ahShm = ahNew;
691 lsmFree(pWin32File->pEnv, pWin32File->apShm);
692 pWin32File->apShm = apNew;
693 pWin32File->nShm = nNew;
696 if( pWin32File->ahShm[iChunk]==NULL ){
697 HANDLE hMap;
698 assert( nReq<=0xFFFFFFFF );
699 hMap = CreateFileMappingW(pWin32File->hShmFile, NULL, PAGE_READWRITE, 0,
700 (DWORD)nReq, NULL);
701 if( hMap==NULL ){
702 return LSM_IOERR_BKPT;
704 pWin32File->ahShm[iChunk] = hMap;
706 if( pWin32File->apShm[iChunk]==NULL ){
707 LPVOID pMap;
708 pMap = MapViewOfFile(pWin32File->ahShm[iChunk],
709 FILE_MAP_WRITE | FILE_MAP_READ, 0,
710 iOffset - iOffsetShift, sz + iOffsetShift);
711 if( pMap==NULL ){
712 return LSM_IOERR_BKPT;
714 pWin32File->apShm[iChunk] = pMap;
716 if( iOffsetShift!=0 ){
717 char *p = (char *)pWin32File->apShm[iChunk];
718 *ppShm = (void *)&p[iOffsetShift];
719 }else{
720 *ppShm = pWin32File->apShm[iChunk];
722 return LSM_OK;
725 static void lsmWin32OsShmBarrier(void){
726 MemoryBarrier();
729 static int lsmWin32OsShmUnmap(lsm_file *pFile, int bDelete){
730 Win32File *pWin32File = (Win32File *)pFile;
732 if( pWin32File->hShmFile!=NULL ){
733 int i;
734 for(i=0; i<pWin32File->nShm; i++){
735 if( pWin32File->apShm[i]!=NULL ){
736 UnmapViewOfFile(pWin32File->apShm[i]);
737 pWin32File->apShm[i] = NULL;
739 if( pWin32File->ahShm[i]!=NULL ){
740 CloseHandle(pWin32File->ahShm[i]);
741 pWin32File->ahShm[i] = NULL;
744 CloseHandle(pWin32File->hShmFile);
745 pWin32File->hShmFile = NULL;
746 if( bDelete ){
747 char *zShm = win32ShmFile(pWin32File);
748 if( zShm ){ win32Delete(pWin32File->pEnv, zShm); }
749 lsmFree(pWin32File->pEnv, zShm);
752 return LSM_OK;
755 #define MX_CLOSE_ATTEMPT 3
756 static int lsmWin32OsClose(lsm_file *pFile){
757 int rc;
758 int nRetry = 0;
759 Win32File *pWin32File = (Win32File *)pFile;
760 lsmWin32OsShmUnmap(pFile, 0);
761 win32Unmap(pWin32File);
763 if( pWin32File->hFile==NULL ){
764 rc = LSM_IOERR_BKPT;
765 break;
767 rc = CloseHandle(pWin32File->hFile);
768 if( rc ){
769 pWin32File->hFile = NULL;
770 rc = LSM_OK;
771 break;
773 if( ++nRetry>=MX_CLOSE_ATTEMPT ){
774 rc = LSM_IOERR_BKPT;
775 break;
777 }while( 1 );
778 lsmFree(pWin32File->pEnv, pWin32File->ahShm);
779 lsmFree(pWin32File->pEnv, pWin32File->apShm);
780 lsmFree(pWin32File->pEnv, pWin32File);
781 return rc;
784 static int lsmWin32OsSleep(lsm_env *pEnv, int us){
785 unused_parameter(pEnv);
786 return win32Sleep(us);
789 /****************************************************************************
790 ** Memory allocation routines.
793 static void *lsmWin32OsMalloc(lsm_env *pEnv, size_t N){
794 assert( HeapValidate(GetProcessHeap(), 0, NULL) );
795 return HeapAlloc(GetProcessHeap(), 0, (SIZE_T)N);
798 static void lsmWin32OsFree(lsm_env *pEnv, void *p){
799 assert( HeapValidate(GetProcessHeap(), 0, NULL) );
800 if( p ){
801 HeapFree(GetProcessHeap(), 0, p);
805 static void *lsmWin32OsRealloc(lsm_env *pEnv, void *p, size_t N){
806 unsigned char *m = (unsigned char *)p;
807 assert( HeapValidate(GetProcessHeap(), 0, NULL) );
808 if( 1>N ){
809 lsmWin32OsFree(pEnv, p);
810 return NULL;
811 }else if( NULL==p ){
812 return lsmWin32OsMalloc(pEnv, N);
813 }else{
814 #if 0 /* arguable: don't shrink */
815 SIZE_T sz = HeapSize(GetProcessHeap(), 0, m);
816 if( sz>=(SIZE_T)N ){
817 return p;
819 #endif
820 return HeapReAlloc(GetProcessHeap(), 0, m, N);
824 static size_t lsmWin32OsMSize(lsm_env *pEnv, void *p){
825 assert( HeapValidate(GetProcessHeap(), 0, NULL) );
826 return (size_t)HeapSize(GetProcessHeap(), 0, p);
830 #ifdef LSM_MUTEX_WIN32
831 /*************************************************************************
832 ** Mutex methods for Win32 based systems. If LSM_MUTEX_WIN32 is
833 ** missing then a no-op implementation of mutexes found below will be
834 ** used instead.
836 #include "windows.h"
838 typedef struct Win32Mutex Win32Mutex;
839 struct Win32Mutex {
840 lsm_env *pEnv;
841 CRITICAL_SECTION mutex;
842 #ifdef LSM_DEBUG
843 DWORD owner;
844 #endif
847 #ifndef WIN32_MUTEX_INITIALIZER
848 # define WIN32_MUTEX_INITIALIZER { 0 }
849 #endif
851 #ifdef LSM_DEBUG
852 # define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER, 0 }
853 #else
854 # define LSM_WIN32_STATIC_MUTEX { 0, WIN32_MUTEX_INITIALIZER }
855 #endif
857 static int lsmWin32OsMutexStatic(
858 lsm_env *pEnv,
859 int iMutex,
860 lsm_mutex **ppStatic
862 static volatile LONG initialized = 0;
863 static Win32Mutex sMutex[2] = {
864 LSM_WIN32_STATIC_MUTEX,
865 LSM_WIN32_STATIC_MUTEX
868 assert( iMutex==LSM_MUTEX_GLOBAL || iMutex==LSM_MUTEX_HEAP );
869 assert( LSM_MUTEX_GLOBAL==1 && LSM_MUTEX_HEAP==2 );
871 if( InterlockedCompareExchange(&initialized, 1, 0)==0 ){
872 int i;
873 for(i=0; i<array_size(sMutex); i++){
874 InitializeCriticalSection(&sMutex[i].mutex);
877 *ppStatic = (lsm_mutex *)&sMutex[iMutex-1];
878 return LSM_OK;
881 static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
882 Win32Mutex *pMutex; /* Pointer to new mutex */
884 pMutex = (Win32Mutex *)lsmMallocZero(pEnv, sizeof(Win32Mutex));
885 if( !pMutex ) return LSM_NOMEM_BKPT;
887 pMutex->pEnv = pEnv;
888 InitializeCriticalSection(&pMutex->mutex);
890 *ppNew = (lsm_mutex *)pMutex;
891 return LSM_OK;
894 static void lsmWin32OsMutexDel(lsm_mutex *p){
895 Win32Mutex *pMutex = (Win32Mutex *)p;
896 DeleteCriticalSection(&pMutex->mutex);
897 lsmFree(pMutex->pEnv, pMutex);
900 static void lsmWin32OsMutexEnter(lsm_mutex *p){
901 Win32Mutex *pMutex = (Win32Mutex *)p;
902 EnterCriticalSection(&pMutex->mutex);
904 #ifdef LSM_DEBUG
905 assert( pMutex->owner!=GetCurrentThreadId() );
906 pMutex->owner = GetCurrentThreadId();
907 assert( pMutex->owner==GetCurrentThreadId() );
908 #endif
911 static int lsmWin32OsMutexTry(lsm_mutex *p){
912 BOOL bRet;
913 Win32Mutex *pMutex = (Win32Mutex *)p;
914 bRet = TryEnterCriticalSection(&pMutex->mutex);
915 #ifdef LSM_DEBUG
916 if( bRet ){
917 assert( pMutex->owner!=GetCurrentThreadId() );
918 pMutex->owner = GetCurrentThreadId();
919 assert( pMutex->owner==GetCurrentThreadId() );
921 #endif
922 return !bRet;
925 static void lsmWin32OsMutexLeave(lsm_mutex *p){
926 Win32Mutex *pMutex = (Win32Mutex *)p;
927 #ifdef LSM_DEBUG
928 assert( pMutex->owner==GetCurrentThreadId() );
929 pMutex->owner = 0;
930 assert( pMutex->owner!=GetCurrentThreadId() );
931 #endif
932 LeaveCriticalSection(&pMutex->mutex);
935 #ifdef LSM_DEBUG
936 static int lsmWin32OsMutexHeld(lsm_mutex *p){
937 Win32Mutex *pMutex = (Win32Mutex *)p;
938 return pMutex ? pMutex->owner==GetCurrentThreadId() : 1;
940 static int lsmWin32OsMutexNotHeld(lsm_mutex *p){
941 Win32Mutex *pMutex = (Win32Mutex *)p;
942 return pMutex ? pMutex->owner!=GetCurrentThreadId() : 1;
944 #endif
946 ** End of Win32 mutex implementation.
947 *************************************************************************/
948 #else
949 /*************************************************************************
950 ** Noop mutex implementation
952 typedef struct NoopMutex NoopMutex;
953 struct NoopMutex {
954 lsm_env *pEnv; /* Environment handle (for xFree()) */
955 int bHeld; /* True if mutex is held */
956 int bStatic; /* True for a static mutex */
958 static NoopMutex aStaticNoopMutex[2] = {
959 {0, 0, 1},
960 {0, 0, 1},
963 static int lsmWin32OsMutexStatic(
964 lsm_env *pEnv,
965 int iMutex,
966 lsm_mutex **ppStatic
968 assert( iMutex>=1 && iMutex<=(int)array_size(aStaticNoopMutex) );
969 *ppStatic = (lsm_mutex *)&aStaticNoopMutex[iMutex-1];
970 return LSM_OK;
972 static int lsmWin32OsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
973 NoopMutex *p;
974 p = (NoopMutex *)lsmMallocZero(pEnv, sizeof(NoopMutex));
975 if( p ) p->pEnv = pEnv;
976 *ppNew = (lsm_mutex *)p;
977 return (p ? LSM_OK : LSM_NOMEM_BKPT);
979 static void lsmWin32OsMutexDel(lsm_mutex *pMutex) {
980 NoopMutex *p = (NoopMutex *)pMutex;
981 assert( p->bStatic==0 && p->pEnv );
982 lsmFree(p->pEnv, p);
984 static void lsmWin32OsMutexEnter(lsm_mutex *pMutex){
985 NoopMutex *p = (NoopMutex *)pMutex;
986 assert( p->bHeld==0 );
987 p->bHeld = 1;
989 static int lsmWin32OsMutexTry(lsm_mutex *pMutex){
990 NoopMutex *p = (NoopMutex *)pMutex;
991 assert( p->bHeld==0 );
992 p->bHeld = 1;
993 return 0;
995 static void lsmWin32OsMutexLeave(lsm_mutex *pMutex){
996 NoopMutex *p = (NoopMutex *)pMutex;
997 assert( p->bHeld==1 );
998 p->bHeld = 0;
1000 #ifdef LSM_DEBUG
1001 static int lsmWin32OsMutexHeld(lsm_mutex *pMutex){
1002 NoopMutex *p = (NoopMutex *)pMutex;
1003 return p ? p->bHeld : 1;
1005 static int lsmWin32OsMutexNotHeld(lsm_mutex *pMutex){
1006 NoopMutex *p = (NoopMutex *)pMutex;
1007 return p ? !p->bHeld : 1;
1009 #endif
1010 /***************************************************************************/
1011 #endif /* else LSM_MUTEX_NONE */
1013 /* Without LSM_DEBUG, the MutexHeld tests are never called */
1014 #ifndef LSM_DEBUG
1015 # define lsmWin32OsMutexHeld 0
1016 # define lsmWin32OsMutexNotHeld 0
1017 #endif
1019 lsm_env *lsm_default_env(void){
1020 static lsm_env win32_env = {
1021 sizeof(lsm_env), /* nByte */
1022 1, /* iVersion */
1023 /***** file i/o ******************/
1024 0, /* pVfsCtx */
1025 lsmWin32OsFullpath, /* xFullpath */
1026 lsmWin32OsOpen, /* xOpen */
1027 lsmWin32OsRead, /* xRead */
1028 lsmWin32OsWrite, /* xWrite */
1029 lsmWin32OsTruncate, /* xTruncate */
1030 lsmWin32OsSync, /* xSync */
1031 lsmWin32OsSectorSize, /* xSectorSize */
1032 lsmWin32OsRemap, /* xRemap */
1033 lsmWin32OsFileid, /* xFileid */
1034 lsmWin32OsClose, /* xClose */
1035 lsmWin32OsUnlink, /* xUnlink */
1036 lsmWin32OsLock, /* xLock */
1037 lsmWin32OsTestLock, /* xTestLock */
1038 lsmWin32OsShmMap, /* xShmMap */
1039 lsmWin32OsShmBarrier, /* xShmBarrier */
1040 lsmWin32OsShmUnmap, /* xShmUnmap */
1041 /***** memory allocation *********/
1042 0, /* pMemCtx */
1043 lsmWin32OsMalloc, /* xMalloc */
1044 lsmWin32OsRealloc, /* xRealloc */
1045 lsmWin32OsFree, /* xFree */
1046 lsmWin32OsMSize, /* xSize */
1047 /***** mutexes *********************/
1048 0, /* pMutexCtx */
1049 lsmWin32OsMutexStatic, /* xMutexStatic */
1050 lsmWin32OsMutexNew, /* xMutexNew */
1051 lsmWin32OsMutexDel, /* xMutexDel */
1052 lsmWin32OsMutexEnter, /* xMutexEnter */
1053 lsmWin32OsMutexTry, /* xMutexTry */
1054 lsmWin32OsMutexLeave, /* xMutexLeave */
1055 lsmWin32OsMutexHeld, /* xMutexHeld */
1056 lsmWin32OsMutexNotHeld, /* xMutexNotHeld */
1057 /***** other *********************/
1058 lsmWin32OsSleep, /* xSleep */
1060 return &win32_env;
1063 #endif