diff --git a/manifest b/manifest index a7fa8de09f..26a47b7b7d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\ssqlite3_set_auxdata()\sand\ssqlite3_get_auxdata()\sAPIs.\s(CVS\s1532) -D 2004-06-05T10:22:17 +C Added\ssqlite3OsLock\sfor\swin32.\s\sAssertion\sfault\sin\sattach.test.\s(CVS\s1533) +D 2004-06-06T00:42:26 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -25,7 +25,7 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2 F src/attach.c c315c58cb16fd6e913b3bfa6412aedecb4567fa5 F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79 -F src/btree.c b8a253fbbcca4da758d1d21232e873d8d5c46eb5 +F src/btree.c 5ee4cad91bdf4e2f0a5dba3a8750283a965d6d27 F src/btree.h 589427ac13bb544d298cd99726e2572a6fe4bdaa F src/build.c e12e602f06e37a0fbcb49af17cba68ad85e101b6 F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2 @@ -45,8 +45,8 @@ F src/os_mac.c b823874690615ace0dd520d3ad1fe8bfd864b7e0 F src/os_mac.h 51d2445f47e182ed32d3bd6937f81070c6fd9bd4 F src/os_unix.c a4feb70b23fa5272f53cd2c74588484b54294800 F src/os_unix.h 7999f2246c6347707e98f7078871ea8ca605df3f -F src/os_win.c 0f895f0491a983740c08af8e5d1986f3bc4629a4 -F src/os_win.h 5d41af24caaef6c13a2d8e2399caa1c57d45c84d +F src/os_win.c a5ab6da08ee90922c3e150afb814d8010383aa96 +F src/os_win.h 004eec47b1780fcaf07420ddc2072294b698d48c F src/pager.c 944f6b071279887574081281f27bb2af88b42905 F src/pager.h 0c7b5ac45c69e690c45d160d03bdc8fbd2d4657b F src/parse.y 27c1ce09f9d309be91f9e537df2fb00892990af4 @@ -215,7 +215,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248 F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P e82eb722b0e0b223ddaf7d85669792c33594f9da -R 89202e63414e4abff52f3cb8f1936bee -U danielk1977 -Z d38834ee375024a85c02b119ebf3aa3d +P c2899b437366d879258ab4f6ae47868441010eca +R 2034d14c2145fcc5ef775d02c0479e42 +U drh +Z 2f61e1823db0bf2026ec8a2b5fdc4a80 diff --git a/manifest.uuid b/manifest.uuid index 1f77420d32..b74ebe0a82 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c2899b437366d879258ab4f6ae47868441010eca \ No newline at end of file +9e6cd9ec75f726ef85e60f593aaa895791315071 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 80167cb675..98beb7b185 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.158 2004/06/05 00:01:45 drh Exp $ +** $Id: btree.c,v 1.159 2004/06/06 00:42:26 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1151,6 +1151,12 @@ page1_init_failed: */ static void unlockBtreeIfUnused(Btree *pBt){ if( pBt->inTrans==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ + if( pBt->pPage1->aData==0 ){ + MemPage *pPage = pBt->pPage1; + pPage->aData = &((char*)pPage)[-pBt->pageSize]; + pPage->pBt = pBt; + pPage->pgno = 1; + } releasePage(pBt->pPage1); pBt->pPage1 = 0; pBt->inStmt = 0; @@ -1585,6 +1591,7 @@ static void getCellInfo(BtCursor *pCur){ }else{ #ifndef NDEBUG CellInfo info; + memset(&info, 0, sizeof(info)); parseCell(pCur->pPage, pCur->idx, &info); assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); #endif diff --git a/src/os_win.c b/src/os_win.c index af45a06b50..4e36f9127c 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -35,6 +35,7 @@ */ int sqlite3OsDelete(const char *zFilename){ DeleteFile(zFilename); + TRACE2("DELETE \"%s\"\n", zFilename); return SQLITE_OK; } @@ -88,8 +89,9 @@ int sqlite3OsOpenReadWrite( *pReadonly = 0; } id->h = h; - id->locked = 0; + id->locktype = NO_LOCK; OpenCounter(+1); + TRACE3("OPEN R/W %d \"%s\"\n", h, zFilename); return SQLITE_OK; } @@ -129,8 +131,9 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ return SQLITE_CANTOPEN; } id->h = h; - id->locked = 0; + id->locktype = NO_LOCK; OpenCounter(+1); + TRACE3("OPEN EX %d \"%s\"\n", h, zFilename); return SQLITE_OK; } @@ -154,8 +157,9 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ return SQLITE_CANTOPEN; } id->h = h; - id->locked = 0; + id->locktype = NO_LOCK; OpenCounter(+1); + TRACE3("OPEN RO %d \"%s\"\n", h, zFilename); return SQLITE_OK; } @@ -206,6 +210,7 @@ int sqlite3OsTempFileName(char *zBuf){ zBuf[j] = 0; if( !sqlite3OsFileExists(zBuf) ) break; } + TRACE2("TEMP FILENAME: %s\n", zBuf); return SQLITE_OK; } @@ -226,7 +231,7 @@ int sqlite3OsClose(OsFile *id){ int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ DWORD got; SimulateIOError(SQLITE_IOERR); - TRACE2("READ %d\n", last_page); + TRACE2("READ %d\n", id->h); if( !ReadFile(id->h, pBuf, amt, &got, 0) ){ got = 0; } @@ -245,7 +250,7 @@ int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ int rc; DWORD wrote; SimulateIOError(SQLITE_IOERR); - TRACE2("WRITE %d\n", last_page); + TRACE2("WRITE %d\n", id->h); while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){ amt -= wrote; pBuf = &((char*)pBuf)[wrote]; @@ -265,7 +270,7 @@ int sqlite3OsSeek(OsFile *id, off_t offset){ DWORD rc; SEEK(offset/1024 + 1); rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN); - /* TRACE3("SEEK rc=0x%x upper=0x%x\n", rc, upperBits); */ + TRACE3("SEEK %d %lld\n", id->h, offset); return SQLITE_OK; } @@ -273,6 +278,7 @@ int sqlite3OsSeek(OsFile *id, off_t offset){ ** Make sure all writes to a particular file are committed to disk. */ int sqlite3OsSync(OsFile *id){ + TRACE2("SYNC %d\n", id->h); if( FlushFileBuffers(id->h) ){ return SQLITE_OK; }else{ @@ -285,6 +291,7 @@ int sqlite3OsSync(OsFile *id){ */ int sqlite3OsTruncate(OsFile *id, off_t nByte){ LONG upperBits = nByte>>32; + TRACE3("TRUNCATE %d %lld\n", id->h, nByte); SimulateIOError(SQLITE_IOERR); SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN); SetEndOfFile(id->h); @@ -302,28 +309,6 @@ int sqlite3OsFileSize(OsFile *id, off_t *pSize){ return SQLITE_OK; } -/* -** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. -** 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 -** API as long as we don't call it win running Win95/98/ME. A call to -** this routine is used to determine if the host is Win95/98/ME or -** WinNT/2K/XP so that we will know whether or not we can safely call -** the LockFileEx() API. -*/ -int isNT(void){ - static int osType = 0; /* 0=unknown 1=win95 2=winNT */ - if( osType==0 ){ - OSVERSIONINFO sInfo; - sInfo.dwOSVersionInfoSize = sizeof(sInfo); - GetVersionEx(&sInfo); - osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; - } - return osType==2; -} - /* ** Windows file locking notes: ** @@ -337,24 +322,18 @@ int isNT(void){ ** end of the file where it is unlikely to ever interfere with an ** actual read attempt. ** -** A database read lock is obtained by locking a single randomly-chosen +** A SHARED_LOCK is obtained by locking a single randomly-chosen ** byte out of a specific range of bytes. The lock byte is obtained at ** random so two separate readers can probably access the file at the ** same time, unless they are unlucky and choose the same lock byte. -** A database write lock is obtained by locking all bytes in the range. -** There can only be one writer. -** -** A lock is obtained on the first byte of the lock range before acquiring -** either a read lock or a write lock. This prevents two processes from -** attempting to get a lock at a same time. The semantics of -** sqlite3OsReadLock() require that if there is already a write lock, that -** lock is converted into a read lock atomically. The lock on the first -** byte allows us to drop the old write lock and get the read lock without -** another process jumping into the middle and messing us up. The same -** argument applies to sqlite3OsWriteLock(). +** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. +** There can only be one writer. A RESERVED_LOCK is obtained by locking +** a single byte of the file that is designated as the reserved lock byte. +** A PENDING_LOCK is obtained by locking a designated byte different from +** the RESERVED_LOCK byte. ** ** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, -** which means we can use reader/writer locks. When reader writer locks +** which means we can use reader/writer locks. When reader/writer locks ** are used, the lock is placed on the same range of bytes that is used ** for probabilistic locking in Win95/98/ME. Hence, the locking scheme ** will support two or more Win95 readers or two or more WinNT readers. @@ -362,107 +341,167 @@ int isNT(void){ ** WinNT reader will lock out all other Win95 readers. ** ** The following #defines specify the range of bytes used for locking. -** N_LOCKBYTE is the number of bytes available for doing the locking. -** The first byte used to hold the lock while the lock is changing does -** not count toward this number. FIRST_LOCKBYTE is the address of -** the first byte in the range of bytes used for locking. +** SHARED_SIZE is the number of bytes available in the pool from which +** a random byte is selected for a shared lock. The pool of bytes for +** shared locks begins at SHARED_FIRST. */ -#define N_LOCKBYTE 10239 -#define FIRST_LOCKBYTE (0xffffffff - N_LOCKBYTE) - -int sqlite3OsLock(OsFile *id, int locktype){ - return SQLITE_OK; -} - -int sqlite3OsCheckWriteLock(OsFile *id){ - return 0; -} +#define SHARED_SIZE 10238 +#define SHARED_FIRST (0xffffffff - SHARED_SIZE + 1) +#define RESERVED_BYTE (SHARED_FIRST - 1) +#define PENDING_BYTE (RESERVED_BYTE - 1) /* -** Change the status of the lock on the file "id" to be a readlock. -** If the file was write locked, then this reduces the lock to a read. -** If the file was read locked, then this acquires a new read lock. +** Return true (non-zero) if we are running under WinNT, Win2K or WinXP. +** Return false (zero) for Win95, Win98, or WinME. ** -** Return SQLITE_OK on success and SQLITE_BUSY on failure. If this -** library was compiled with large file support (LFS) but LFS is not -** available on the host, then an SQLITE_NOLFS is returned. +** Here is an interesting observation: Win95, Win98, and WinME lack +** the LockFileEx() API. But we can still statically link against that +** API as long as we don't call it win running Win95/98/ME. A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. */ -int sqlite3OsReadLock(OsFile *id){ - int rc; - if( id->locked>0 ){ - rc = SQLITE_OK; +static int isNT(void){ + static int osType = 0; /* 0=unknown 1=win95 2=winNT */ + if( osType==0 ){ + OSVERSIONINFO sInfo; + sInfo.dwOSVersionInfoSize = sizeof(sInfo); + GetVersionEx(&sInfo); + osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; + } + return osType==2; +} + +/* +** Acquire a reader lock on the range of bytes from iByte...iByte+nByte-1. +** Different API routines are called depending on whether or not this +** is Win95 or WinNT. +*/ +static int getReadLock(HANDLE h, unsigned int iByte, unsigned int nByte){ + int res; + if( isNT() ){ + OVERLAPPED ovlp; + ovlp.Offset = iByte; + ovlp.OffsetHigh = 0; + ovlp.hEvent = 0; + res = LockFileEx(h, LOCKFILE_FAIL_IMMEDIATELY, 0, nByte, 0, &ovlp); }else{ - int lk; - int res; - int cnt = 100; - sqlite3Randomness(sizeof(lk), &lk); - lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1; - while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){ + res = LockFile(h, iByte, 0, nByte, 0); + } + return res; +} + +/* +** Undo a readlock +*/ +static int unlockReadLock(OsFile *id){ + int res; + if( isNT() ){ + res = UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + }else{ + res = UnlockFile(id->h, SHARED_FIRST + id->sharedLockByte, 0, 1, 0); + } + return res; +} + +/* +** Acquire a lock of the given type on the specified file. If an +** appropriate lock already exists, this routine is a no-op. Return +** SQLITE_OK on success and SQLITE_BUSY if another thread is already +** holding a conflicting lock. +*/ +int sqlite3OsLock(OsFile *id, int locktype){ + int rc = SQLITE_OK; /* Return code from subroutines */ + int res = 1; /* Result of a windows lock call */ + + TRACE4("LOCK %d %d was %d\n", id->h, locktype, id->locktype); + + /* 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 ){ + return SQLITE_OK; + } + + /* 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. + */ + if( id->locktype==NO_LOCK || locktype==PENDING_LOCK ){ + int cnt = 4; + while( cnt-->0 && (res = LockFile(id->h, PENDING_BYTE, 0, 1, 0))==0 ){ + /* Try 4 times to get the pending lock. The pending lock might be + ** held by another reader process who will release it momentarily. + */ Sleep(1); } - if( res ){ - UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); - if( isNT() ){ - OVERLAPPED ovlp; - ovlp.Offset = FIRST_LOCKBYTE+1; - ovlp.OffsetHigh = 0; - ovlp.hEvent = 0; - res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY, - 0, N_LOCKBYTE, 0, &ovlp); - }else{ - res = LockFile(id->h, FIRST_LOCKBYTE+lk, 0, 1, 0); - } - UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0); - } - if( res ){ - id->locked = lk; - rc = SQLITE_OK; + } + + /* Acquire a shared lock + */ + if( locktype>=SHARED_LOCK && id->locktypeh, SHARED_FIRST, SHARED_SIZE); }else{ - rc = SQLITE_BUSY; + int lk; + sqlite3Randomness(sizeof(lk), &lk); + id->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1); + res = LockFile(id->h, SHARED_FIRST+id->sharedLockByte, 0, 1, 0); } + if( locktypeh, PENDING_BYTE, 0, 1, 0); + } + } + + /* Acquire a RESERVED lock + */ + if( locktype>=RESERVED_LOCK && id->locktypeh, RESERVED_BYTE, 1); + } + + /* Acquire an EXCLUSIVE lock + */ + if( locktype==EXCLUSIVE_LOCK ){ + if( id->locktype>=SHARED_LOCK ){ + res = unlockReadLock(id); + } + if( res ){ + res = LockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); + }else{ + res = 0; + } + } + + /* Update the state of the lock has held in the file descriptor then + ** return the appropriate result code. + */ + if( res ){ + id->locktype = locktype; + rc = SQLITE_OK; + }else{ + TRACE2("LOCK FAILED %d\n", id->h); + rc = SQLITE_BUSY; } return rc; } /* -** Change the lock status to be an exclusive or write lock. Return -** SQLITE_OK on success and SQLITE_BUSY on a failure. If this -** library was compiled with large file support (LFS) but LFS is not -** available on the host, then an SQLITE_NOLFS is returned. +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, return +** non-zero, otherwise zero. */ -int sqlite3OsWriteLock(OsFile *id){ +int sqlite3OsCheckWriteLock(OsFile *id){ int rc; - if( id->locked<0 ){ - rc = SQLITE_OK; + if( id->locktype>=RESERVED_LOCK ){ + rc = 1; }else{ - int res; - int cnt = 100; - while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){ - Sleep(1); - } - if( res ){ - if( id->locked>0 ){ - if( isNT() ){ - UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); - }else{ - res = UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, 0, 1, 0); - } - } - if( res ){ - res = LockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); - }else{ - res = 0; - } - UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0); - } - if( res ){ - id->locked = -1; - rc = SQLITE_OK; - }else{ - rc = SQLITE_BUSY; + rc = getReadLock(id->h, RESERVED_BYTE, 1); + if( rc ){ + UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); } } - return rc; + return 0; } /* @@ -473,18 +512,21 @@ int sqlite3OsWriteLock(OsFile *id){ */ int sqlite3OsUnlock(OsFile *id){ int rc; - if( id->locked==0 ){ - rc = SQLITE_OK; - }else if( isNT() || id->locked<0 ){ - UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); - rc = SQLITE_OK; - id->locked = 0; - }else{ - UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0); - rc = SQLITE_OK; - id->locked = 0; + TRACE3("UNLOCK %d was %d\n", id->h, id->locktype); + if( id->locktype>=EXCLUSIVE_LOCK ){ + UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0); } - return rc; + if( id->locktype>=PENDING_LOCK ){ + UnlockFile(id->h, PENDING_BYTE, 0, 1, 0); + } + if( id->locktype>=RESERVED_LOCK ){ + UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0); + } + if( id->locktype==SHARED_LOCK ){ + unlockReadLock(id); + } + id->locktype = NO_LOCK; + return SQLITE_OK; } /* diff --git a/src/os_win.h b/src/os_win.h index 35f77405ea..614a0ea5e7 100644 --- a/src/os_win.h +++ b/src/os_win.h @@ -38,7 +38,8 @@ typedef struct OsFile OsFile; struct OsFile { HANDLE h; /* Handle for accessing the file */ - int locked; /* 0: unlocked, <0: write lock, >0: read lock */ + int locktype; /* Type of lock currently held on this file */ + int sharedLockByte; /* Randomly chosen byte used as a shared lock */ };