diff options
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/os_win.c')
| -rw-r--r-- | ext/pdo_sqlite/sqlite/src/os_win.c | 936 |
1 files changed, 776 insertions, 160 deletions
diff --git a/ext/pdo_sqlite/sqlite/src/os_win.c b/ext/pdo_sqlite/sqlite/src/os_win.c index ad874ae63..6c167ab5b 100644 --- a/ext/pdo_sqlite/sqlite/src/os_win.c +++ b/ext/pdo_sqlite/sqlite/src/os_win.c @@ -35,6 +35,49 @@ #include "os_common.h" /* +** Determine if we are dealing with WindowsCE - which has a much +** reduced API. +*/ +#if defined(_WIN32_WCE) +# define OS_WINCE 1 +#else +# define OS_WINCE 0 +#endif + +/* +** WinCE lacks native support for file locking so we have to fake it +** with some code of our own. +*/ +#if OS_WINCE +typedef struct winceLock { + int nReaders; /* Number of reader locks obtained */ + BOOL bPending; /* Indicates a pending lock has been obtained */ + BOOL bReserved; /* Indicates a reserved lock has been obtained */ + BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ +} winceLock; +#endif + +/* +** The winFile structure is a subclass of OsFile specific to the win32 +** portability layer. +*/ +typedef struct winFile winFile; +struct winFile { + IoMethod const *pMethod;/* Must be first */ + 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 */ +#if OS_WINCE + WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ + HANDLE hMutex; /* Mutex used to control access to shared lock */ + HANDLE hShared; /* Shared memory segment used for locking */ + winceLock local; /* Locks obtained by this instance of winFile */ + winceLock *shared; /* Global shared lock memory for the file */ +#endif +}; + + +/* ** Do not include any of the File I/O interface procedures if the ** SQLITE_OMIT_DISKIO macro is defined (indicating that there database ** will be in-memory only) @@ -56,8 +99,8 @@ int sqlite3_os_type = 0; /* -** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. -** Return false (zero) for Win95, Win98, or WinME. +** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, +** or WinCE. Return false (zero) for Win95, Win98, or WinME. ** ** Here is an interesting observation: Win95, Win98, and WinME lack ** the LockFileEx() API. But we can still statically link against that @@ -66,34 +109,38 @@ int sqlite3_os_type = 0; ** WinNT/2K/XP so that we will know whether or not we can safely call ** the LockFileEx() API. */ -static int isNT(void){ - if( sqlite3_os_type==0 ){ - OSVERSIONINFO sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - GetVersionEx(&sInfo); - sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; +#if OS_WINCE +# define isNT() (1) +#else + static int isNT(void){ + if( sqlite3_os_type==0 ){ + OSVERSIONINFO sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + GetVersionEx(&sInfo); + sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; + } + return sqlite3_os_type==2; } - return sqlite3_os_type==2; -} +#endif /* OS_WINCE */ /* ** Convert a UTF-8 string to UTF-32. Space to hold the returned string ** is obtained from sqliteMalloc. */ static WCHAR *utf8ToUnicode(const char *zFilename){ - int nByte; + int nChar; WCHAR *zWideFilename; if( !isNT() ){ return 0; } - nByte = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0)*sizeof(WCHAR); - zWideFilename = sqliteMalloc( nByte*sizeof(zWideFilename[0]) ); + nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); + zWideFilename = sqliteMalloc( nChar*sizeof(zWideFilename[0]) ); if( zWideFilename==0 ){ return 0; } - nByte = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nByte); - if( nByte==0 ){ + nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); + if( nChar==0 ){ sqliteFree(zWideFilename); zWideFilename = 0; } @@ -122,37 +169,369 @@ static char *unicodeToUtf8(const WCHAR *zWideFilename){ return zFilename; } +#if OS_WINCE +/************************************************************************* +** This section contains code for WinCE only. +*/ +/* +** WindowsCE does not have a localtime() function. So create a +** substitute. +*/ +#include <time.h> +struct tm *__cdecl localtime(const time_t *t) +{ + static struct tm y; + FILETIME uTm, lTm; + SYSTEMTIME pTm; + i64 t64; + t64 = *t; + t64 = (t64 + 11644473600)*10000000; + uTm.dwLowDateTime = t64 & 0xFFFFFFFF; + uTm.dwHighDateTime= t64 >> 32; + FileTimeToLocalFileTime(&uTm,&lTm); + FileTimeToSystemTime(&lTm,&pTm); + y.tm_year = pTm.wYear - 1900; + y.tm_mon = pTm.wMonth - 1; + y.tm_wday = pTm.wDayOfWeek; + y.tm_mday = pTm.wDay; + y.tm_hour = pTm.wHour; + y.tm_min = pTm.wMinute; + y.tm_sec = pTm.wSecond; + return &y; +} + +/* This will never be called, but defined to make the code compile */ +#define GetTempPathA(a,b) + +#define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e) +#define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e) +#define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f) + +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-offsetof(winFile,h)] + +/* +** Acquire a lock on the handle h +*/ +static void winceMutexAcquire(HANDLE h){ + DWORD dwErr; + do { + dwErr = WaitForSingleObject(h, INFINITE); + } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); +} +/* +** Release a lock acquired by winceMutexAcquire() +*/ +#define winceMutexRelease(h) ReleaseMutex(h) + +/* +** Create the mutex and shared memory used for locking in the file +** descriptor pFile +*/ +static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ + WCHAR *zTok; + WCHAR *zName = utf8ToUnicode(zFilename); + BOOL bInit = TRUE; + + /* Initialize the local lockdata */ + ZeroMemory(&pFile->local, sizeof(pFile->local)); + + /* Replace the backslashes from the filename and lowercase it + ** to derive a mutex name. */ + zTok = CharLowerW(zName); + for (;*zTok;zTok++){ + if (*zTok == '\\') *zTok = '_'; + } + + /* Create/open the named mutex */ + pFile->hMutex = CreateMutexW(NULL, FALSE, zName); + if (!pFile->hMutex){ + sqliteFree(zName); + return FALSE; + } + + /* Acquire the mutex before continuing */ + winceMutexAcquire(pFile->hMutex); + + /* Since the names of named mutexes, semaphores, file mappings etc are + ** case-sensitive, take advantage of that by uppercasing the mutex name + ** and using that as the shared filemapping name. + */ + CharUpperW(zName); + pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, + PAGE_READWRITE, 0, sizeof(winceLock), + zName); + + /* Set a flag that indicates we're the first to create the memory so it + ** must be zero-initialized */ + if (GetLastError() == ERROR_ALREADY_EXISTS){ + bInit = FALSE; + } + + sqliteFree(zName); + + /* If we succeeded in making the shared memory handle, map it. */ + if (pFile->hShared){ + pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared, + FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); + /* If mapping failed, close the shared memory handle and erase it */ + if (!pFile->shared){ + CloseHandle(pFile->hShared); + pFile->hShared = NULL; + } + } + + /* If shared memory could not be created, then close the mutex and fail */ + if (pFile->hShared == NULL){ + winceMutexRelease(pFile->hMutex); + CloseHandle(pFile->hMutex); + pFile->hMutex = NULL; + return FALSE; + } + + /* Initialize the shared memory if we're supposed to */ + if (bInit) { + ZeroMemory(pFile->shared, sizeof(winceLock)); + } + + winceMutexRelease(pFile->hMutex); + return TRUE; +} + +/* +** Destroy the part of winFile that deals with wince locks +*/ +static void winceDestroyLock(winFile *pFile){ + if (pFile->hMutex){ + /* Acquire the mutex */ + winceMutexAcquire(pFile->hMutex); + + /* The following blocks should probably assert in debug mode, but they + are to cleanup in case any locks remained open */ + if (pFile->local.nReaders){ + pFile->shared->nReaders --; + } + if (pFile->local.bReserved){ + pFile->shared->bReserved = FALSE; + } + if (pFile->local.bPending){ + pFile->shared->bPending = FALSE; + } + if (pFile->local.bExclusive){ + pFile->shared->bExclusive = FALSE; + } + + /* De-reference and close our copy of the shared memory handle */ + UnmapViewOfFile(pFile->shared); + CloseHandle(pFile->hShared); + + /* Done with the mutex */ + winceMutexRelease(pFile->hMutex); + CloseHandle(pFile->hMutex); + pFile->hMutex = NULL; + } +} + +/* +** An implementation of the LockFile() API of windows for wince +*/ +static BOOL winceLockFile( + HANDLE *phFile, + DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh +){ + winFile *pFile = HANDLE_TO_WINFILE(phFile); + BOOL bReturn = FALSE; + + if (!pFile->hMutex) return TRUE; + winceMutexAcquire(pFile->hMutex); + + /* Wanting an exclusive lock? */ + if (dwFileOffsetLow == SHARED_FIRST + && nNumberOfBytesToLockLow == SHARED_SIZE){ + if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ + pFile->shared->bExclusive = TRUE; + pFile->local.bExclusive = TRUE; + bReturn = TRUE; + } + } + + /* Want a read-only lock? */ + else if ((dwFileOffsetLow >= SHARED_FIRST && + dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE) && + nNumberOfBytesToLockLow == 1){ + if (pFile->shared->bExclusive == 0){ + pFile->local.nReaders ++; + if (pFile->local.nReaders == 1){ + pFile->shared->nReaders ++; + } + bReturn = TRUE; + } + } + + /* Want a pending lock? */ + else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToLockLow == 1){ + /* If no pending lock has been acquired, then acquire it */ + if (pFile->shared->bPending == 0) { + pFile->shared->bPending = TRUE; + pFile->local.bPending = TRUE; + bReturn = TRUE; + } + } + /* Want a reserved lock? */ + else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ + if (pFile->shared->bReserved == 0) { + pFile->shared->bReserved = TRUE; + pFile->local.bReserved = TRUE; + bReturn = TRUE; + } + } + + winceMutexRelease(pFile->hMutex); + return bReturn; +} + +/* +** An implementation of the UnlockFile API of windows for wince +*/ +static BOOL winceUnlockFile( + HANDLE *phFile, + DWORD dwFileOffsetLow, + DWORD dwFileOffsetHigh, + DWORD nNumberOfBytesToUnlockLow, + DWORD nNumberOfBytesToUnlockHigh +){ + winFile *pFile = HANDLE_TO_WINFILE(phFile); + BOOL bReturn = FALSE; + + if (!pFile->hMutex) return TRUE; + winceMutexAcquire(pFile->hMutex); + + /* Releasing a reader lock or an exclusive lock */ + if (dwFileOffsetLow >= SHARED_FIRST && + dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE){ + /* Did we have an exclusive lock? */ + if (pFile->local.bExclusive){ + pFile->local.bExclusive = FALSE; + pFile->shared->bExclusive = FALSE; + bReturn = TRUE; + } + + /* Did we just have a reader lock? */ + else if (pFile->local.nReaders){ + pFile->local.nReaders --; + if (pFile->local.nReaders == 0) + { + pFile->shared->nReaders --; + } + bReturn = TRUE; + } + } + + /* Releasing a pending lock */ + else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ + if (pFile->local.bPending){ + pFile->local.bPending = FALSE; + pFile->shared->bPending = FALSE; + bReturn = TRUE; + } + } + /* Releasing a reserved lock */ + else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ + if (pFile->local.bReserved) { + pFile->local.bReserved = FALSE; + pFile->shared->bReserved = FALSE; + bReturn = TRUE; + } + } + + winceMutexRelease(pFile->hMutex); + return bReturn; +} + +/* +** An implementation of the LockFileEx() API of windows for wince +*/ +static BOOL winceLockFileEx( + HANDLE *phFile, + DWORD dwFlags, + DWORD dwReserved, + DWORD nNumberOfBytesToLockLow, + DWORD nNumberOfBytesToLockHigh, + LPOVERLAPPED lpOverlapped +){ + /* If the caller wants a shared read lock, forward this call + ** to winceLockFile */ + if (lpOverlapped->Offset == SHARED_FIRST && + dwFlags == 1 && + nNumberOfBytesToLockLow == SHARED_SIZE){ + return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0); + } + return FALSE; +} +/* +** End of the special code for wince +*****************************************************************************/ +#endif /* OS_WINCE */ /* -** Delete the named file +** 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 is 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. */ -int sqlite3OsDelete(const char *zFilename){ +#define MX_DELETION_ATTEMPTS 3 +int sqlite3WinDelete(const char *zFilename){ WCHAR *zWide = utf8ToUnicode(zFilename); + int cnt = 0; + int rc; if( zWide ){ - DeleteFileW(zWide); + do{ + rc = DeleteFileW(zWide); + }while( rc==0 && cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) ); sqliteFree(zWide); }else{ - DeleteFileA(zFilename); +#if OS_WINCE + return SQLITE_NOMEM; +#else + do{ + rc = DeleteFileA(zFilename); + }while( rc==0 && cnt++ < MX_DELETION_ATTEMPTS && (Sleep(100), 1) ); +#endif } TRACE2("DELETE \"%s\"\n", zFilename); - return SQLITE_OK; + return rc==0 ? SQLITE_OK : SQLITE_IOERR; } /* ** Return TRUE if the named file exists. */ -int sqlite3OsFileExists(const char *zFilename){ +int sqlite3WinFileExists(const char *zFilename){ int exists = 0; WCHAR *zWide = utf8ToUnicode(zFilename); if( zWide ){ exists = GetFileAttributesW(zWide) != 0xffffffff; sqliteFree(zWide); }else{ +#if OS_WINCE + return SQLITE_NOMEM; +#else exists = GetFileAttributesA(zFilename) != 0xffffffff; +#endif } return exists; } +/* Forward declaration */ +static int allocateWinFile(winFile *pInit, OsFile **pId); + /* ** Attempt to open a file for both reading and writing. If that ** fails, try opening it read-only. If the file does not exist, @@ -166,14 +545,15 @@ int sqlite3OsFileExists(const char *zFilename){ ** On failure, the function returns SQLITE_CANTOPEN and leaves ** *id and *pReadonly unchanged. */ -int sqlite3OsOpenReadWrite( +int sqlite3WinOpenReadWrite( const char *zFilename, - OsFile *id, + OsFile **pId, int *pReadonly ){ + winFile f; HANDLE h; WCHAR *zWide = utf8ToUnicode(zFilename); - assert( !id->isOpen ); + assert( *pId==0 ); if( zWide ){ h = CreateFileW(zWide, GENERIC_READ | GENERIC_WRITE, @@ -186,7 +566,7 @@ int sqlite3OsOpenReadWrite( if( h==INVALID_HANDLE_VALUE ){ h = CreateFileW(zWide, GENERIC_READ, - FILE_SHARE_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, @@ -200,8 +580,18 @@ int sqlite3OsOpenReadWrite( }else{ *pReadonly = 0; } +#if OS_WINCE + if (!winceCreateLock(zFilename, &f)){ + CloseHandle(h); + sqliteFree(zWide); + return SQLITE_CANTOPEN; + } +#endif sqliteFree(zWide); }else{ +#if OS_WINCE + return SQLITE_NOMEM; +#else h = CreateFileA(zFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -213,7 +603,7 @@ int sqlite3OsOpenReadWrite( if( h==INVALID_HANDLE_VALUE ){ h = CreateFileA(zFilename, GENERIC_READ, - FILE_SHARE_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, @@ -226,14 +616,14 @@ int sqlite3OsOpenReadWrite( }else{ *pReadonly = 0; } +#endif /* OS_WINCE */ } - id->h = h; - id->locktype = NO_LOCK; - id->sharedLockByte = 0; - id->isOpen = 1; - OpenCounter(+1); + f.h = h; +#if OS_WINCE + f.zDeleteOnClose = 0; +#endif TRACE3("OPEN R/W %d \"%s\"\n", h, zFilename); - return SQLITE_OK; + return allocateWinFile(&f, pId); } @@ -250,48 +640,65 @@ int sqlite3OsOpenReadWrite( ** On success, write the file handle into *id and return SQLITE_OK. ** ** On failure, return SQLITE_CANTOPEN. +** +** Sometimes if we have just deleted a prior journal file, windows +** will fail to open a new one because there is a "pending delete". +** To work around this bug, we pause for 100 milliseconds and attempt +** a second open after the first one fails. The whole operation only +** fails if both open attempts are unsuccessful. */ -int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ +int sqlite3WinOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){ + winFile f; HANDLE h; int fileflags; WCHAR *zWide = utf8ToUnicode(zFilename); - assert( !id->isOpen ); + assert( *pId == 0 ); + fileflags = FILE_FLAG_RANDOM_ACCESS; +#if !OS_WINCE if( delFlag ){ - fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS - | FILE_FLAG_DELETE_ON_CLOSE; - }else{ - fileflags = FILE_FLAG_RANDOM_ACCESS; + fileflags |= FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE; } +#endif if( zWide ){ - h = CreateFileW(zWide, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - fileflags, - NULL - ); + int cnt = 0; + do{ + h = CreateFileW(zWide, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + fileflags, + NULL + ); + }while( h==INVALID_HANDLE_VALUE && cnt++ < 2 && (Sleep(100), 1) ); sqliteFree(zWide); }else{ - h = CreateFileA(zFilename, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - CREATE_ALWAYS, - fileflags, - NULL - ); +#if OS_WINCE + return SQLITE_NOMEM; +#else + int cnt = 0; + do{ + h = CreateFileA(zFilename, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + fileflags, + NULL + ); + }while( h==INVALID_HANDLE_VALUE && cnt++ < 2 && (Sleep(100), 1) ); +#endif /* OS_WINCE */ } if( h==INVALID_HANDLE_VALUE ){ return SQLITE_CANTOPEN; } - id->h = h; - id->locktype = NO_LOCK; - id->sharedLockByte = 0; - id->isOpen = 1; - OpenCounter(+1); + f.h = h; +#if OS_WINCE + f.zDeleteOnClose = delFlag ? utf8ToUnicode(zFilename) : 0; + f.hMutex = NULL; +#endif TRACE3("OPEN EX %d \"%s\"\n", h, zFilename); - return SQLITE_OK; + return allocateWinFile(&f, pId); } /* @@ -301,10 +708,11 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ +int sqlite3WinOpenReadOnly(const char *zFilename, OsFile **pId){ + winFile f; HANDLE h; WCHAR *zWide = utf8ToUnicode(zFilename); - assert( !id->isOpen ); + assert( *pId==0 ); if( zWide ){ h = CreateFileW(zWide, GENERIC_READ, @@ -316,6 +724,9 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ ); sqliteFree(zWide); }else{ +#if OS_WINCE + return SQLITE_NOMEM; +#else h = CreateFileA(zFilename, GENERIC_READ, 0, @@ -324,17 +735,18 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL ); +#endif } if( h==INVALID_HANDLE_VALUE ){ return SQLITE_CANTOPEN; } - id->h = h; - id->locktype = NO_LOCK; - id->sharedLockByte = 0; - id->isOpen = 1; - OpenCounter(+1); + f.h = h; +#if OS_WINCE + f.zDeleteOnClose = 0; + f.hMutex = NULL; +#endif TRACE3("OPEN RO %d \"%s\"\n", h, zFilename); - return SQLITE_OK; + return allocateWinFile(&f, pId); } /* @@ -353,9 +765,9 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ ** On failure, the function returns SQLITE_CANTOPEN and leaves ** *id unchanged. */ -int sqlite3OsOpenDirectory( - const char *zDirname, - OsFile *id +static int winOpenDirectory( + OsFile *id, + const char *zDirname ){ return SQLITE_OK; } @@ -371,7 +783,7 @@ char *sqlite3_temp_directory = 0; ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ -int sqlite3OsTempFileName(char *zBuf){ +int sqlite3WinTempFileName(char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -412,15 +824,36 @@ int sqlite3OsTempFileName(char *zBuf){ /* ** Close a file. +** +** It is reported that an attempt to close a handle might sometimes +** fail. This is a very unreasonable result, but windows is notorious +** for being unreasonable so I do not doubt that it might happen. If +** the close fails, we pause for 100 milliseconds and try again. As +** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before +** giving up and returning an error. */ -int sqlite3OsClose(OsFile *id){ - if( id->isOpen ){ - TRACE2("CLOSE %d\n", id->h); - CloseHandle(id->h); +#define MX_CLOSE_ATTEMPT 3 +static int winClose(OsFile **pId){ + winFile *pFile; + int rc = 1; + if( pId && (pFile = (winFile*)*pId)!=0 ){ + int rc, cnt = 0; + TRACE2("CLOSE %d\n", pFile->h); + do{ + rc = CloseHandle(pFile->h); + }while( rc==0 && cnt++ < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); +#if OS_WINCE + winceDestroyLock(pFile); + if( pFile->zDeleteOnClose ){ + DeleteFileW(pFile->zDeleteOnClose); + sqliteFree(pFile->zDeleteOnClose); + } +#endif OpenCounter(-1); - id->isOpen = 0; + sqliteFree(pFile); + *pId = 0; } - return SQLITE_OK; + return rc ? SQLITE_OK : SQLITE_IOERR; } /* @@ -428,12 +861,12 @@ int sqlite3OsClose(OsFile *id){ ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. */ -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ +static int winRead(OsFile *id, void *pBuf, int amt){ DWORD got; - assert( id->isOpen ); + assert( id!=0 ); SimulateIOError(SQLITE_IOERR); - TRACE3("READ %d lock=%d\n", id->h, id->locktype); - if( !ReadFile(id->h, pBuf, amt, &got, 0) ){ + TRACE3("READ %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype); + if( !ReadFile(((winFile*)id)->h, pBuf, amt, &got, 0) ){ got = 0; } if( got==(DWORD)amt ){ @@ -447,15 +880,16 @@ int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ ** Write data from a buffer into a file. Return SQLITE_OK on success ** or some other error code on failure. */ -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ +static int winWrite(OsFile *id, const void *pBuf, int amt){ int rc = 0; DWORD wrote; - assert( id->isOpen ); + assert( id!=0 ); SimulateIOError(SQLITE_IOERR); SimulateDiskfullError; - TRACE3("WRITE %d lock=%d\n", id->h, id->locktype); + TRACE3("WRITE %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype); assert( amt>0 ); - while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ + while( amt>0 && (rc = WriteFile(((winFile*)id)->h, pBuf, amt, &wrote, 0))!=0 + && wrote>0 ){ amt -= wrote; pBuf = &((char*)pBuf)[wrote]; } @@ -475,17 +909,17 @@ int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ /* ** Move the read/write pointer in a file. */ -int sqlite3OsSeek(OsFile *id, i64 offset){ +static int winSeek(OsFile *id, i64 offset){ LONG upperBits = offset>>32; LONG lowerBits = offset & 0xffffffff; DWORD rc; - assert( id->isOpen ); + assert( id!=0 ); #ifdef SQLITE_TEST if( offset ) SimulateDiskfullError #endif SEEK(offset/1024 + 1); - rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN); - TRACE3("SEEK %d %lld\n", id->h, offset); + rc = SetFilePointer(((winFile*)id)->h, lowerBits, &upperBits, FILE_BEGIN); + TRACE3("SEEK %d %lld\n", ((winFile*)id)->h, offset); if( rc==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR ){ return SQLITE_FULL; } @@ -495,10 +929,10 @@ int sqlite3OsSeek(OsFile *id, i64 offset){ /* ** Make sure all writes to a particular file are committed to disk. */ -int sqlite3OsSync(OsFile *id, int dataOnly){ - assert( id->isOpen ); - TRACE3("SYNC %d lock=%d\n", id->h, id->locktype); - if( FlushFileBuffers(id->h) ){ +static int winSync(OsFile *id, int dataOnly){ + assert( id!=0 ); + TRACE3("SYNC %d lock=%d\n", ((winFile*)id)->h, ((winFile*)id)->locktype); + if( FlushFileBuffers(((winFile*)id)->h) ){ return SQLITE_OK; }else{ return SQLITE_IOERR; @@ -509,7 +943,7 @@ int sqlite3OsSync(OsFile *id, int dataOnly){ ** Sync the directory zDirname. This is a no-op on operating systems other ** than UNIX. */ -int sqlite3OsSyncDirectory(const char *zDirname){ +int sqlite3WinSyncDirectory(const char *zDirname){ SimulateIOError(SQLITE_IOERR); return SQLITE_OK; } @@ -517,34 +951,41 @@ int sqlite3OsSyncDirectory(const char *zDirname){ /* ** Truncate an open file to a specified size */ -int sqlite3OsTruncate(OsFile *id, i64 nByte){ +static int winTruncate(OsFile *id, i64 nByte){ LONG upperBits = nByte>>32; - assert( id->isOpen ); - TRACE3("TRUNCATE %d %lld\n", id->h, nByte); + assert( id!=0 ); + TRACE3("TRUNCATE %d %lld\n", ((winFile*)id)->h, nByte); SimulateIOError(SQLITE_IOERR); - SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN); - SetEndOfFile(id->h); + SetFilePointer(((winFile*)id)->h, nByte, &upperBits, FILE_BEGIN); + SetEndOfFile(((winFile*)id)->h); return SQLITE_OK; } /* ** Determine the current size of a file in bytes */ -int sqlite3OsFileSize(OsFile *id, i64 *pSize){ +static int winFileSize(OsFile *id, i64 *pSize){ DWORD upperBits, lowerBits; - assert( id->isOpen ); + assert( id!=0 ); SimulateIOError(SQLITE_IOERR); - lowerBits = GetFileSize(id->h, &upperBits); + lowerBits = GetFileSize(((winFile*)id)->h, &upperBits); *pSize = (((i64)upperBits)<<32) + lowerBits; return SQLITE_OK; } /* +** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. +*/ +#ifndef LOCKFILE_FAIL_IMMEDIATELY +# define LOCKFILE_FAIL_IMMEDIATELY 1 +#endif + +/* ** Acquire a reader lock. ** Different API routines are called depending on whether or not this ** is Win95 or WinNT. */ -static int getReadLock(OsFile *id){ +static int getReadLock(winFile *id){ int res; if( isNT() ){ OVERLAPPED ovlp; @@ -564,12 +1005,12 @@ static int getReadLock(OsFile *id){ /* ** Undo a readlock */ -static int unlockReadLock(OsFile *id){ +static int unlockReadLock(winFile *pFile){ int res; if( isNT() ){ - res = UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); }else{ - res = UnlockFile(id->h, SHARED_FIRST + id->sharedLockByte, 0, 1, 0); + res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); } return res; } @@ -579,7 +1020,7 @@ static int unlockReadLock(OsFile *id){ ** Check that a given pathname is a directory and is writable ** */ -int sqlite3OsIsDirWritable(char *zDirname){ +int sqlite3WinIsDirWritable(char *zDirname){ int fileAttr; WCHAR *zWide; if( zDirname==0 ) return 0; @@ -589,7 +1030,11 @@ int sqlite3OsIsDirWritable(char *zDirname){ fileAttr = GetFileAttributesW(zWide); sqliteFree(zWide); }else{ +#if OS_WINCE + return 0; +#else fileAttr = GetFileAttributesA(zDirname); +#endif } if( fileAttr == 0xffffffff ) return 0; if( (fileAttr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ){ @@ -620,45 +1065,46 @@ int sqlite3OsIsDirWritable(char *zDirname){ ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** -** This routine will only increase a lock. The sqlite3OsUnlock() routine +** This routine will only increase a lock. The winUnlock() routine ** erases all locks at once and returns us immediately to locking level 0. ** It is not possible to lower the locking level one step at a time. You ** must go straight to locking level 0. */ -int sqlite3OsLock(OsFile *id, int locktype){ +static int winLock(OsFile *id, int locktype){ int rc = SQLITE_OK; /* Return code from subroutines */ int res = 1; /* Result of a windows lock call */ int newLocktype; /* Set id->locktype to this value before exiting */ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ + winFile *pFile = (winFile*)id; - assert( id->isOpen ); + assert( pFile!=0 ); TRACE5("LOCK %d %d was %d(%d)\n", - id->h, locktype, id->locktype, id->sharedLockByte); + pFile->h, locktype, pFile->locktype, pFile->sharedLockByte); /* If there is already a lock of this type or more restrictive on the ** OsFile, do nothing. Don't use the end_lock: exit path, as ** sqlite3OsEnterMutex() hasn't been called yet. */ - if( id->locktype>=locktype ){ + if( pFile->locktype>=locktype ){ return SQLITE_OK; } /* Make sure the locking sequence is correct */ - assert( id->locktype!=NO_LOCK || locktype==SHARED_LOCK ); + assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); assert( locktype!=PENDING_LOCK ); - assert( locktype!=RESERVED_LOCK || id->locktype==SHARED_LOCK ); + assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of ** the PENDING_LOCK byte is temporary. */ - newLocktype = id->locktype; - if( id->locktype==NO_LOCK - || (locktype==EXCLUSIVE_LOCK && id->locktype==RESERVED_LOCK) + newLocktype = pFile->locktype; + if( pFile->locktype==NO_LOCK + || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) ){ int cnt = 3; - while( cnt-->0 && (res = LockFile(id->h, PENDING_BYTE, 0, 1, 0))==0 ){ + while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ /* Try 3 times to get the pending lock. The pending lock might be ** held by another reader process who will release it momentarily. */ @@ -671,8 +1117,8 @@ int sqlite3OsLock(OsFile *id, int locktype){ /* Acquire a shared lock */ if( locktype==SHARED_LOCK && res ){ - assert( id->locktype==NO_LOCK ); - res = getReadLock(id); + assert( pFile->locktype==NO_LOCK ); + res = getReadLock(pFile); if( res ){ newLocktype = SHARED_LOCK; } @@ -681,8 +1127,8 @@ int sqlite3OsLock(OsFile *id, int locktype){ /* Acquire a RESERVED lock */ if( locktype==RESERVED_LOCK && res ){ - assert( id->locktype==SHARED_LOCK ); - res = LockFile(id->h, RESERVED_BYTE, 0, 1, 0); + assert( pFile->locktype==SHARED_LOCK ); + res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( res ){ newLocktype = RESERVED_LOCK; } @@ -698,10 +1144,10 @@ int sqlite3OsLock(OsFile *id, int locktype){ /* Acquire an EXCLUSIVE lock */ if( locktype==EXCLUSIVE_LOCK && res ){ - assert( id->locktype>=SHARED_LOCK ); - res = unlockReadLock(id); + assert( pFile->locktype>=SHARED_LOCK ); + res = unlockReadLock(pFile); TRACE2("unreadlock = %d\n", res); - res = LockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ newLocktype = EXCLUSIVE_LOCK; }else{ @@ -713,7 +1159,7 @@ int sqlite3OsLock(OsFile *id, int locktype){ ** release it now. */ if( gotPendingLock && locktype==SHARED_LOCK ){ - UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); + UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } /* Update the state of the lock has held in the file descriptor then @@ -722,11 +1168,11 @@ int sqlite3OsLock(OsFile *id, int locktype){ if( res ){ rc = SQLITE_OK; }else{ - TRACE4("LOCK FAILED %d trying for %d but got %d\n", id->h, + TRACE4("LOCK FAILED %d trying for %d but got %d\n", pFile->h, locktype, newLocktype); rc = SQLITE_BUSY; } - id->locktype = newLocktype; + pFile->locktype = newLocktype; return rc; } @@ -735,19 +1181,20 @@ int sqlite3OsLock(OsFile *id, int locktype){ ** file by this or any other process. If such a lock is held, return ** non-zero, otherwise zero. */ -int sqlite3OsCheckReservedLock(OsFile *id){ +static int winCheckReservedLock(OsFile *id){ int rc; - assert( id->isOpen ); - if( id->locktype>=RESERVED_LOCK ){ + winFile *pFile = (winFile*)id; + assert( pFile!=0 ); + if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; - TRACE3("TEST WR-LOCK %d %d (local)\n", id->h, rc); + TRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc); }else{ - rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0); + rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); if( rc ){ - UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); + UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } rc = !rc; - TRACE3("TEST WR-LOCK %d %d (remote)\n", id->h, rc); + TRACE3("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc); } return rc; } @@ -763,32 +1210,33 @@ int sqlite3OsCheckReservedLock(OsFile *id){ ** is NO_LOCK. If the second argument is SHARED_LOCK then this routine ** might return SQLITE_IOERR; */ -int sqlite3OsUnlock(OsFile *id, int locktype){ +static int winUnlock(OsFile *id, int locktype){ int type; int rc = SQLITE_OK; - assert( id->isOpen ); + winFile *pFile = (winFile*)id; + assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); - TRACE5("UNLOCK %d to %d was %d(%d)\n", id->h, locktype, - id->locktype, id->sharedLockByte); - type = id->locktype; + TRACE5("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, + pFile->locktype, pFile->sharedLockByte); + type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ - UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( locktype==SHARED_LOCK && !getReadLock(id) ){ + UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ rc = SQLITE_IOERR; } } if( type>=RESERVED_LOCK ){ - UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); + UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); } if( locktype==NO_LOCK && type>=SHARED_LOCK ){ - unlockReadLock(id); + unlockReadLock(pFile); } if( type>=PENDING_LOCK ){ - UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); + UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); } - id->locktype = locktype; + pFile->locktype = locktype; return rc; } @@ -798,17 +1246,21 @@ int sqlite3OsUnlock(OsFile *id, int locktype){ ** The calling function is responsible for freeing this space once it ** is no longer needed. */ -char *sqlite3OsFullPathname(const char *zRelative){ - char *zNotUsed; +char *sqlite3WinFullPathname(const char *zRelative){ char *zFull; - WCHAR *zWide; +#if defined(__CYGWIN__) int nByte; -#ifdef __CYGWIN__ nByte = strlen(zRelative) + MAX_PATH + 1001; zFull = sqliteMalloc( nByte ); if( zFull==0 ) return 0; if( cygwin_conv_to_full_win32_path(zRelative, zFull) ) return 0; +#elif OS_WINCE + /* WinCE has no concept of a relative pathname, or so I am told. */ + zFull = sqliteStrDup(zRelative); #else + char *zNotUsed; + WCHAR *zWide; + int nByte; zWide = utf8ToUnicode(zRelative); if( zWide ){ WCHAR *zTemp, *zNotUsedW; @@ -829,6 +1281,76 @@ char *sqlite3OsFullPathname(const char *zRelative){ return zFull; } +/* +** The fullSync option is meaningless on windows. This is a no-op. +*/ +static void winSetFullSync(OsFile *id, int v){ + return; +} + +/* +** Return the underlying file handle for an OsFile +*/ +static int winFileHandle(OsFile *id){ + return (int)((winFile*)id)->h; +} + +/* +** Return an integer that indices the type of lock currently held +** by this handle. (Used for testing and analysis only.) +*/ +static int winLockState(OsFile *id){ + return ((winFile*)id)->locktype; +} + +/* +** This vector defines all the methods that can operate on an OsFile +** for win32. +*/ +static const IoMethod sqlite3WinIoMethod = { + winClose, + winOpenDirectory, + winRead, + winWrite, + winSeek, + winTruncate, + winSync, + winSetFullSync, + winFileHandle, + winFileSize, + winLock, + winUnlock, + winLockState, + winCheckReservedLock, +}; + +/* +** Allocate memory for an OsFile. Initialize the new OsFile +** to the value given in pInit and return a pointer to the new +** OsFile. If we run out of memory, close the file and return NULL. +*/ +static int allocateWinFile(winFile *pInit, OsFile **pId){ + winFile *pNew; + pNew = sqliteMalloc( sizeof(*pNew) ); + if( pNew==0 ){ + CloseHandle(pInit->h); +#if OS_WINCE + sqliteFree(pInit->zDeleteOnClose); +#endif + *pId = 0; + return SQLITE_NOMEM; + }else{ + *pNew = *pInit; + pNew->pMethod = &sqlite3WinIoMethod; + pNew->locktype = NO_LOCK; + pNew->sharedLockByte = 0; + *pId = (OsFile*)pNew; + OpenCounter(+1); + return SQLITE_OK; + } +} + + #endif /* SQLITE_OMIT_DISKIO */ /*************************************************************************** ** Everything above deals with file I/O. Everything that follows deals @@ -840,7 +1362,7 @@ char *sqlite3OsFullPathname(const char *zRelative){ ** is written into the buffer zBuf[256]. The calling function must ** supply a sufficiently large buffer. */ -int sqlite3OsRandomSeed(char *zBuf){ +int sqlite3WinRandomSeed(char *zBuf){ /* We have to initialize zBuf to prevent valgrind from reporting ** errors. The reports issued by valgrind are incorrect - we would ** prefer that the randomness be increased by making use of the @@ -861,7 +1383,7 @@ int sqlite3OsRandomSeed(char *zBuf){ /* ** Sleep for a little while. Return the amount of time slept. */ -int sqlite3OsSleep(int ms){ +int sqlite3WinSleep(int ms){ Sleep(ms); return ms; } @@ -871,18 +1393,22 @@ int sqlite3OsSleep(int ms){ */ static int inMutex = 0; #ifdef SQLITE_W32_THREADS + static DWORD mutexOwner; static CRITICAL_SECTION cs; #endif /* -** The following pair of routine implement mutual exclusion for +** The following pair of routines implement mutual exclusion for ** multi-threaded processes. Only a single thread is allowed to ** executed code that is surrounded by EnterMutex() and LeaveMutex(). ** ** SQLite uses only a single Mutex. There is not much critical ** code and what little there is executes quickly and without blocking. +** +** Version 3.3.1 and earlier used a simple mutex. Beginning with +** version 3.3.2, a recursive mutex is required. */ -void sqlite3OsEnterMutex(){ +void sqlite3WinEnterMutex(){ #ifdef SQLITE_W32_THREADS static int isInit = 0; while( !isInit ){ @@ -895,19 +1421,36 @@ void sqlite3OsEnterMutex(){ } } EnterCriticalSection(&cs); + mutexOwner = GetCurrentThreadId(); #endif - assert( !inMutex ); - inMutex = 1; + inMutex++; } -void sqlite3OsLeaveMutex(){ +void sqlite3WinLeaveMutex(){ assert( inMutex ); - inMutex = 0; + inMutex--; #ifdef SQLITE_W32_THREADS + assert( mutexOwner==GetCurrentThreadId() ); LeaveCriticalSection(&cs); #endif } /* +** Return TRUE if the mutex is currently held. +** +** If the thisThreadOnly parameter is true, return true if and only if the +** calling thread holds the mutex. If the parameter is false, return +** true if any thread holds the mutex. +*/ +int sqlite3WinInMutex(int thisThreadOnly){ +#ifdef SQLITE_W32_THREADS + return inMutex>0 && (thisThreadOnly==0 || mutexOwner==GetCurrentThreadId()); +#else + return inMutex>0; +#endif +} + + +/* ** The following variable, if set to a non-zero value, becomes the result ** returned from sqlite3OsCurrentTime(). This is used for testing. */ @@ -920,13 +1463,19 @@ int sqlite3_current_time = 0; ** 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 sqlite3OsCurrentTime(double *prNow){ +int sqlite3WinCurrentTime(double *prNow){ FILETIME ft; /* FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). */ double now; +#if OS_WINCE + SYSTEMTIME time; + GetSystemTime(&time); + SystemTimeToFileTime(&time,&ft); +#else GetSystemTimeAsFileTime( &ft ); +#endif now = ((double)ft.dwHighDateTime) * 4294967296.0; *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5; #ifdef SQLITE_TEST @@ -937,4 +1486,71 @@ int sqlite3OsCurrentTime(double *prNow){ return 0; } +/* +** Remember the number of thread-specific-data blocks allocated. +** Use this to verify that we are not leaking thread-specific-data. +** Ticket #1601 +*/ +#ifdef SQLITE_TEST +int sqlite3_tsd_count = 0; +# define TSD_COUNTER_INCR InterlockedIncrement(&sqlite3_tsd_count) +# define TSD_COUNTER_DECR InterlockedDecrement(&sqlite3_tsd_count) +#else +# define TSD_COUNTER_INCR /* no-op */ +# define TSD_COUNTER_DECR /* no-op */ +#endif + + + +/* +** If called with allocateFlag>1, then return a pointer to thread +** specific data for the current thread. Allocate and zero the +** thread-specific data if it does not already exist necessary. +** +** If called with allocateFlag==0, then check the current thread +** specific data. Return it if it exists. If it does not exist, +** then return NULL. +** +** If called with allocateFlag<0, check to see if the thread specific +** data is allocated and is all zero. If it is then deallocate it. +** Return a pointer to the thread specific data or NULL if it is +** unallocated or gets deallocated. +*/ +ThreadData *sqlite3WinThreadSpecificData(int allocateFlag){ + static int key; + static int keyInit = 0; + static const ThreadData zeroData = {0}; + ThreadData *pTsd; + + if( !keyInit ){ + sqlite3OsEnterMutex(); + if( !keyInit ){ + key = TlsAlloc(); + if( key==0xffffffff ){ + sqlite3OsLeaveMutex(); + return 0; + } + keyInit = 1; + } + sqlite3OsLeaveMutex(); + } + pTsd = TlsGetValue(key); + if( allocateFlag>0 ){ + if( !pTsd ){ + pTsd = sqlite3OsMalloc( sizeof(zeroData) ); + if( pTsd ){ + *pTsd = zeroData; + TlsSetValue(key, pTsd); + TSD_COUNTER_INCR; + } + } + }else if( pTsd!=0 && allocateFlag<0 + && memcmp(pTsd, &zeroData, sizeof(ThreadData))==0 ){ + sqlite3OsFree(pTsd); + TlsSetValue(key, 0); + TSD_COUNTER_DECR; + pTsd = 0; + } + return pTsd; +} #endif /* OS_WIN */ |
