aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/btree.c8
-rw-r--r--src/os.h51
-rw-r--r--src/os_common.h13
-rw-r--r--src/os_unix.c92
-rw-r--r--src/os_win.c46
-rw-r--r--src/pager.c22
-rw-r--r--src/printf.c3
-rw-r--r--src/test1.c5
-rw-r--r--src/vdbeaux.c24
9 files changed, 156 insertions, 108 deletions
diff --git a/src/btree.c b/src/btree.c
index fa4fe86a1..ed4c5d157 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.160 2004/06/07 01:52:14 drh Exp $
+** $Id: btree.c,v 1.161 2004/06/07 16:27:46 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -3713,12 +3713,6 @@ int sqlite3BtreeFlags(BtCursor *pCur){
return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}
-/******************************************************************************
-** The complete implementation of the BTree subsystem is above this line.
-** All the code the follows is for testing and troubleshooting the BTree
-** subsystem. None of the code that follows is used during normal operation.
-******************************************************************************/
-
/*
** Print a disassembly of the given page on standard output. This routine
** is used for debugging and testing only.
diff --git a/src/os.h b/src/os.h
index a33662190..c9d7e5838 100644
--- a/src/os.h
+++ b/src/os.h
@@ -90,6 +90,57 @@
#define PENDING_LOCK 3
#define EXCLUSIVE_LOCK 4
+/*
+** Windows file locking notes:
+**
+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
+** those functions are not available. So we use only LockFile() and
+** UnlockFile().
+**
+** LockFile() prevents not just writing but also reading by other processes.
+** (This is a design error on the part of Windows, but there is nothing
+** we can do about that.) So the region used for locking is at the
+** end of the file where it is unlikely to ever interfere with an
+** actual read attempt.
+**
+** 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.
+** 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
+** 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.
+** But a single Win95 reader will lock out all WinNT readers and a single
+** WinNT reader will lock out all other Win95 readers.
+**
+** The following #defines specify 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.
+**
+** These #defines are available in os.h so that Unix can use the same
+** byte ranges for locking. This leaves open the possiblity of having
+** clients on win95, winNT, and unix all talking to the same shared file
+** and all locking correctly.
+**
+** Locking in windows is manditory. For this reason, we cannot store
+** actual data in the bytes used for locking. The pager never allocates
+** the pages involved in locking therefore.
+*/
+#define SHARED_SIZE 10238
+#define SHARED_FIRST (0x3fffffff - (SHARED_SIZE - 1))
+#define RESERVED_BYTE (SHARED_FIRST - 1)
+#define PENDING_BYTE (RESERVED_BYTE - 1)
+
+
int sqlite3OsDelete(const char*);
int sqlite3OsFileExists(const char*);
int sqliteOsFileRename(const char*, const char*);
diff --git a/src/os_common.h b/src/os_common.h
index 8aa4ecc21..8bea7f2df 100644
--- a/src/os_common.h
+++ b/src/os_common.h
@@ -23,7 +23,8 @@
** Macros for performance tracing. Normally turned off. Only works
** on i486 hardware.
*/
-#if 0
+int sqlite3_os_trace = 0;
+#if 1
static int last_page = 0;
__inline__ unsigned long long int hwtime(void){
unsigned long long int x;
@@ -37,11 +38,11 @@ static unsigned int elapse;
#define TIMER_START g_start=hwtime()
#define TIMER_END elapse=hwtime()-g_start
#define SEEK(X) last_page=(X)
-#define TRACE1(X) sqlite3DebugPrintf(X)
-#define TRACE2(X,Y) sqlite3DebugPrintf(X,Y)
-#define TRACE3(X,Y,Z) sqlite3DebugPrintf(X,Y,Z)
-#define TRACE4(X,Y,Z,A) sqlite3DebugPrintf(X,Y,Z,A)
-#define TRACE5(X,Y,Z,A,B) sqlite3DebugPrintf(X,Y,Z,A,B)
+#define TRACE1(X) if( sqlite3_os_trace ) sqlite3DebugPrintf(X)
+#define TRACE2(X,Y) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y)
+#define TRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z)
+#define TRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A)
+#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B)
#else
#define TIMER_START
#define TIMER_END
diff --git a/src/os_unix.c b/src/os_unix.c
index 8ee48de88..110dca38d 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -174,7 +174,7 @@ struct lockKey {
*/
struct lockInfo {
struct lockKey key; /* The lookup key */
- int cnt; /* Number of locks held */
+ int cnt; /* Number of SHARED locks held */
int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
int nRef; /* Number of pointers to this structure */
};
@@ -644,27 +644,27 @@ int sqlite3OsFileSize(OsFile *id, off_t *pSize){
/*
** 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.
+** non-zero. If the file is unlocked or holds only SHARED locks, then
+** return zero.
*/
int sqlite3OsCheckWriteLock(OsFile *id){
int r = 0;
- sqlite3OsEnterMutex();
+ sqlite3OsEnterMutex(); /* Needed because id->pLock is shared across threads */
/* Check if a thread in this process holds such a lock */
if( id->pLock->locktype>SHARED_LOCK ){
r = 1;
}
- /* Otherwise see if some other process holds it. Just check the whole
- ** file for write-locks, rather than any specific bytes.
+ /* Otherwise see if some other process holds it.
*/
if( !r ){
struct flock lock;
lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- lock.l_type = F_RDLCK;
+ lock.l_start = RESERVED_BYTE;
+ lock.l_len = 1;
+ lock.l_type = F_WRLCK;
fcntl(id->fd, F_GETLK, &lock);
if( lock.l_type!=F_UNLCK ){
r = 1;
@@ -672,6 +672,7 @@ int sqlite3OsCheckWriteLock(OsFile *id){
}
sqlite3OsLeaveMutex();
+ TRACE3("TEST WR-LOCK %d %d\n", id->fd, r);
return r;
}
@@ -680,10 +681,25 @@ int sqlite3OsCheckWriteLock(OsFile *id){
** Lock the file with the lock specified by parameter locktype - one
** of the following:
**
-** SHARED_LOCK
-** RESERVED_LOCK
-** PENDING_LOCK
-** EXCLUSIVE_LOCK
+** (1) SHARED_LOCK
+** (2) RESERVED_LOCK
+** (3) PENDING_LOCK
+** (4) EXCLUSIVE_LOCK
+**
+** Locks are are hierarchical. Getting a lock N implies getting all locks
+** N-1, N-2, N-3, .... So, for example, getting a PENDING lock
+** implies a SHARED and a RESERVED lock. This routine adds locks one
+** at a time until the desired lock is acheived. A locking failure might
+** occur at any point. When a failure occurs intermediate locks are
+** retained. For example, if a SHARED lock is held and this routine
+** is called with EXCLUSIVE, it might obtain a RESERVED and PENDING lock
+** but fail to get the EXCLUSIVE lock. In that case, the file would be
+** left in the PENDING lock state - it does not revert to SHARED.
+**
+** This routine will only increase a lock. The sqlite3OsUnlock() 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){
int rc = SQLITE_OK;
@@ -691,12 +707,8 @@ int sqlite3OsLock(OsFile *id, int locktype){
struct flock lock;
int s;
- /* It is an error to request any kind of lock before a shared lock */
- if( locktype>SHARED_LOCK && id->locktype==0 ){
- rc = sqlite3OsLock(id, SHARED_LOCK);
- if( rc!=SQLITE_OK ) return rc;
- }
- assert( locktype==SHARED_LOCK || id->locktype!=0 );
+ TRACE5("LOCK %d %d was %d(%d)\n",
+ id->fd, locktype, id->locktype, pLock->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
@@ -706,14 +718,27 @@ int sqlite3OsLock(OsFile *id, int locktype){
return SQLITE_OK;
}
- sqlite3OsEnterMutex();
+ /* Make sure locking is sequential. In other words, make sure we have
+ ** SHARED before trying for RESERVED, and that we have RESERVED before
+ ** trying for PENDING, and that we have PENDING before trying for
+ ** EXCLUSIVE.
+ */
+ while( locktype>id->locktype+1 ){
+ rc = sqlite3OsLock(id, id->locktype+1);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+ assert( locktype==id->locktype+1 );
+
+ sqlite3OsEnterMutex(); /* Needed because pLock is shared across threads */
/* If some thread using this PID has a lock via a different OsFile*
** handle that precludes the requested lock, return BUSY.
*/
if( (id->locktype!=pLock->locktype &&
- (pLock->locktype>RESERVED_LOCK || locktype!=SHARED_LOCK)) ||
- (locktype>RESERVED_LOCK && pLock->cnt>1)
+ (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK))
+ || (locktype==EXCLUSIVE_LOCK && pLock->cnt>1)
){
rc = SQLITE_BUSY;
goto end_lock;
@@ -744,21 +769,25 @@ int sqlite3OsLock(OsFile *id, int locktype){
assert( pLock->cnt==0 );
assert( pLock->locktype==0 );
- /* Grab a read-lock on byte 2. This ensures that no other process
- ** has a PENDING lock.
+ /* Temporarily grab a PENDING lock. This prevents new SHARED locks from
+ ** being formed if a PENDING lock is already held.
*/
lock.l_type = F_RDLCK;
- lock.l_start = 2;
+ lock.l_start = PENDING_BYTE;
s = fcntl(id->fd, F_SETLK, &lock);
if( s ){
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
goto end_lock;
}
- /* Now get a read-lock on byte 0 and renege on the byte 2 lock. */
- lock.l_start = 0;
+ /* Now get the read-lock */
+ lock.l_start = SHARED_FIRST;
+ lock.l_len = SHARED_SIZE;
s = fcntl(id->fd, F_SETLK, &lock);
- lock.l_start = 2;
+
+ /* Drop the temporary PENDING lock */
+ lock.l_start = PENDING_BYTE;
+ lock.l_len = 1L;
lock.l_type = F_UNLCK;
fcntl(id->fd, F_SETLK, &lock);
if( s ){
@@ -777,13 +806,14 @@ int sqlite3OsLock(OsFile *id, int locktype){
lock.l_type = F_WRLCK;
switch( locktype ){
case RESERVED_LOCK:
- lock.l_start = 1;
+ lock.l_start = RESERVED_BYTE;
break;
case PENDING_LOCK:
- lock.l_start = 2;
+ lock.l_start = PENDING_BYTE;
break;
case EXCLUSIVE_LOCK:
- lock.l_start = 0;
+ lock.l_start = SHARED_FIRST;
+ lock.l_len = SHARED_SIZE;
break;
default:
assert(0);
@@ -797,11 +827,11 @@ int sqlite3OsLock(OsFile *id, int locktype){
if( rc==SQLITE_OK ){
id->locktype = locktype;
pLock->locktype = locktype;
- assert( pLock->locktype==RESERVED_LOCK || pLock->cnt==1 );
}
end_lock:
sqlite3OsLeaveMutex();
+ TRACE4("LOCK %d %d %s\n", id->fd, locktype, rc==SQLITE_OK ? "ok" : "failed");
return rc;
}
diff --git a/src/os_win.c b/src/os_win.c
index 1134eeea2..bc8e02c37 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -314,47 +314,6 @@ int sqlite3OsFileSize(OsFile *id, off_t *pSize){
}
/*
-** Windows file locking notes:
-**
-** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
-** those functions are not available. So we use only LockFile() and
-** UnlockFile().
-**
-** LockFile() prevents not just writing but also reading by other processes.
-** (This is a design error on the part of Windows, but there is nothing
-** we can do about that.) So the region used for locking is at the
-** end of the file where it is unlikely to ever interfere with an
-** actual read attempt.
-**
-** 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.
-** 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
-** 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.
-** But a single Win95 reader will lock out all WinNT readers and a single
-** WinNT reader will lock out all other Win95 readers.
-**
-** The following #defines specify 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 SHARED_SIZE 10238
-#define SHARED_FIRST (0x3fffffff - (SHARED_SIZE - 1))
-#define RESERVED_BYTE (SHARED_FIRST - 1)
-#define PENDING_BYTE (RESERVED_BYTE - 1)
-
-/*
** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
** Return false (zero) for Win95, Win98, or WinME.
**
@@ -531,13 +490,16 @@ int sqlite3OsCheckWriteLock(OsFile *id){
int rc;
if( id->locktype>=RESERVED_LOCK ){
rc = 1;
+ TRACE3("TEST WR-LOCK %d %d (local)\n", id->h, rc);
}else{
rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0);
if( rc ){
UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0);
}
+ rc = !rc;
+ TRACE3("TEST WR-LOCK %d %d (remote)\n", id->h, rc);
}
- return 0;
+ return rc;
}
/*
diff --git a/src/pager.c b/src/pager.c
index 3a655b745..f6e297bd7 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -18,7 +18,7 @@
** file simultaneously, or one process from reading the database while
** another is writing.
**
-** @(#) $Id: pager.c,v 1.112 2004/06/04 10:38:31 danielk1977 Exp $
+** @(#) $Id: pager.c,v 1.113 2004/06/07 16:27:46 drh Exp $
*/
#include "os.h" /* Must be first to enable large file support */
#include "sqliteInt.h"
@@ -1554,20 +1554,16 @@ static int pager_write_pagelist(PgHdr *pList){
** database file. If there is already an EXCLUSIVE lock, the following
** calls to sqlite3OsLock() are no-ops.
**
- ** The upgrade from a RESERVED to PENDING lock cannot return SQLITE_BUSY,
- ** unless someone is not following the locking protocol.
+ ** The upgrade from a RESERVED to PENDING might return SQLITE_BUSY on
+ ** windows because the windows locking mechanism acquires a transient
+ ** PENDING lock during its attempts to get a SHARED lock. So if another
+ ** process were trying to get a SHARED lock at the same time this process
+ ** is upgrading from RESERVED to PENDING, the two could collide.
**
- ** The upgrade from PENDING to EXCLUSIVE can return SQLITE_BUSY. It's
- ** not totally clear that the busy-callback should be invoked here
- ** though. (?)
+ ** The upgrade from PENDING to EXCLUSIVE can return SQLITE_BUSY if there
+ ** are still active readers that were created before the PENDING lock
+ ** was acquired.
*/
- rc = sqlite3OsLock(&pPager->fd, PENDING_LOCK);
- if( rc==SQLITE_BUSY ){
- return SQLITE_PROTOCOL;
- }
- if( rc!=SQLITE_OK ){
- return rc;
- }
do {
rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
}while( rc==SQLITE_BUSY &&
diff --git a/src/printf.c b/src/printf.c
index 9cc625f66..2fdc87dd2 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -810,7 +810,8 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
va_start(ap, zFormat);
base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
va_end(ap);
- fprintf(stderr,"%s", zBuf);
+ fprintf(stdout,"%s", zBuf);
+ fflush(stdout);
}
#endif
diff --git a/src/test1.c b/src/test1.c
index 1d793b262..374ee03a3 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -13,7 +13,7 @@
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
-** $Id: test1.c,v 1.69 2004/06/01 14:09:29 danielk1977 Exp $
+** $Id: test1.c,v 1.70 2004/06/07 16:27:46 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@@ -1913,6 +1913,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
};
int i;
+ extern int sqlite3_os_trace;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
@@ -1929,6 +1930,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
(char*)&sqlite3_open_file_count, TCL_LINK_INT);
Tcl_LinkVar(interp, "sqlite_current_time",
(char*)&sqlite3_current_time, TCL_LINK_INT);
+ Tcl_LinkVar(interp, "sqlite_os_trace",
+ (char*)&sqlite3_os_trace, TCL_LINK_INT);
Tcl_LinkVar(interp, "sqlite_static_bind_value",
(char*)&sqlite_static_bind_value, TCL_LINK_STRING);
return TCL_OK;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index e17a1cbcc..3682320cd 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -922,15 +922,25 @@ static int vdbeCommit(sqlite *db){
}
}
- /* The simple case - if less than two databases have write-transactions
- ** active, there is no need for the master-journal.
+ /* The simple case - no more than one database file (not counting the TEMP
+ ** database) has a transaction active. There is no need for the
+ ** master-journal.
*/
- if( nTrans<2 ){
- for(i=0; i<db->nDb; i++){
+ if( nTrans<=1 ){
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- int rc2 = sqlite3BtreeCommit(db->aDb[i].pBt);
- if( rc==SQLITE_OK ) rc = rc2;
+ rc = sqlite3BtreeSync(pBt, 0);
+ }
+ }
+
+ /* Do the commit only if all databases successfully synced */
+ if( rc==SQLITE_OK ){
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ sqlite3BtreeCommit(pBt);
+ }
}
}
}
@@ -1036,7 +1046,7 @@ static int vdbeCommit(sqlite *db){
}
}
}
- return SQLITE_OK;
+ return rc;
}
/*