From 7b6944047e7d7e8e1b6c975800d566eaf51a9390 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 29 Apr 2010 15:17:48 +0000 Subject: [PATCH] Progress towards a VFS that will support WAL. Locking code is in place but is untested. Still no support for the DMS. FossilOrigin-Name: 1bde41cf081570ad257f927b641e752dff4ed014 --- manifest | 28 ++- manifest.uuid | 2 +- src/os_common.h | 2 + src/os_unix.c | 647 +++++++++++++++++++++++++++++++++++++++++++----- src/sqlite.h.in | 4 +- 5 files changed, 612 insertions(+), 71 deletions(-) diff --git a/manifest b/manifest index a96719324c..81762c0b4c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,8 @@ -C Close\sall\sopen\sdatabase\sconnections\sat\sthe\send\sof\swal.test. -D 2010-04-29T14:58:53 +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +C Progress\stowards\sa\sVFS\sthat\swill\ssupport\sWAL.\s\sLocking\scode\sis\sin\splace\nbut\sis\suntested.\s\sStill\sno\ssupport\sfor\sthe\sDMS. +D 2010-04-29T15:17:48 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in d83a0ffef3dcbfb08b410a6c6dd6c009ec9167fb F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -148,9 +151,9 @@ F src/mutex_w32.c 4cc201c1bfd11d1562810554ff5500e735559d7e F src/notify.c cbfa66a836da3a51567209636e6a94059c137930 F src/os.c 8bc63cf91e9802e2b807198e54e50227fa889306 F src/os.h 534b082c3cb349ad05fa6fa0b06087e022af282c -F src/os_common.h 240c88b163b02c21a9f21f87d49678a0aa21ff30 +F src/os_common.h 0d6ee583b6ee3185eb9d951f890c6dd03021a08d F src/os_os2.c 8ad77a418630d7dee91d1bb04f79c2096301d3a0 -F src/os_unix.c c8b7d1e0f4315e08cf54324ca706e1a1a80f5fc2 +F src/os_unix.c 340c503705cdb447753a55771911a92aa41d27c8 F src/os_win.c a8fc01d8483be472e495793c01064fd87e56a5c1 F src/pager.c b4a41030860229e80295fa1f37addab24d21799c F src/pager.h cee4487ab4f0911dd9f22a40e3cd55afdb7ef444 @@ -166,7 +169,7 @@ F src/resolve.c ac5f1a713cd1ae77f08b83cc69581e11bf5ae6f9 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b F src/shell.c c40427c7245535a04a9cb4a417b6cc05c022e6a4 -F src/sqlite.h.in caf60df0991a14e22cce8243b9caa1c1dbd09d42 +F src/sqlite.h.in 0cc43a0fa5d26cd26d2609b84ecf094a159e9cb9 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 F src/sqliteInt.h 700a2df7b8dfe57c3b8d83c52ff40928e026220c F src/sqliteLimit.h 3afab2291762b5d09ae20c18feb8e9fa935a60a6 @@ -808,7 +811,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P d1cadeed4eea20d8892726cc8c69f4f3f57d0cd4 -R 606ec50aa0ca99ee2a7fabcf3364b376 -U dan -Z c947c540d09aca713bd3451e1bec9253 +P 3cc55a7568daa3796483f632e33478969e381cf5 +R 1f2ad602233779dda1e35a2877201d1f +U drh +Z 5bb08c68390b552fdeb1ef93301ae98c +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.6 (GNU/Linux) + +iD8DBQFL2aMhoxKgR168RlERAntLAKCNwNXdF9LrZZqkEN8f3gsvrSxoXACaA2Ku +SvIawhrGV2wraxrir5MNan8= +=Vfpz +-----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 32ff2b16a0..000aaadb5f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3cc55a7568daa3796483f632e33478969e381cf5 \ No newline at end of file +1bde41cf081570ad257f927b641e752dff4ed014 \ No newline at end of file diff --git a/src/os_common.h b/src/os_common.h index 6a2e2d93be..e6de2b25f7 100644 --- a/src/os_common.h +++ b/src/os_common.h @@ -40,6 +40,7 @@ int sqlite3OSTrace = 0; if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C) #define OSTRACE7(X,Y,Z,A,B,C,D) \ if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D) +#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X #else #define OSTRACE1(X) #define OSTRACE2(X,Y) @@ -48,6 +49,7 @@ int sqlite3OSTrace = 0; #define OSTRACE5(X,Y,Z,A,B) #define OSTRACE6(X,Y,Z,A,B,C) #define OSTRACE7(X,Y,Z,A,B,C,D) +#define OSTRACE(X) #endif /* diff --git a/src/os_unix.c b/src/os_unix.c index 7e3930a1e6..634f5ee218 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4562,42 +4562,355 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ return 0; } +/* Forward reference */ +typedef struct unixShm unixShm; +typedef struct unixShmFile unixShmFile; + /* -** Structure used internally by this VFS to record the state of an -** open shared memory segment. +** Object used to represent a single file opened and mmapped to provide +** shared memory. When multiple threads all reference the same +** log-summary, each thread has its own unixFile object, but they all +** point to a single instance of this object. In other words, each +** log-summary is opened only once per process. +** +** unixMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: +** +** nRef +** pNext +** +** The following fields are read-only after the object is created: +** +** fid +** zFilename +** +** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and +** unixMutexHeld() is true when reading or writing any other field +** in this structure. */ -struct unixShm { - sqlite3_vfs *pVfs; /* VFS that opened this shared-memory segment */ - int size; /* Size of the shared memory area */ - char *pBuf; /* Pointer to the beginning */ - unixFile fd; /* The open file descriptor */ +struct unixShmFile { + struct unixFileId fid; /* Unique file identifier */ + sqlite3_mutex *mutex; /* Mutex to access this object */ + sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ + sqlite3_mutex *mutexRecov; /* The RECOVER mutex */ + char *zFilename; /* Name of the file */ + int size; /* Size of the file */ + int h; /* Open file descriptor */ + char *pMMapBuf; /* Where currently mmapped() */ + int nReadPrefix; /* Number of SQLITE_SHM_READ_PREFIX locks */ + int nReadFull; /* Number of SQLITE_SHM_READ_FULL locks */ + int nRef; /* Number of unixShm objects pointing to this */ + unixShm *pFirst; /* All unixShm objects pointing to this */ + unixShmFile *pNext; /* Next in list of all unixShmFile objects */ +#ifdef SQLITE_DEBUG + u8 exclMask; /* Mask of exclusive locks held */ + u8 sharedMask; /* Mask of shared locks held */ + u8 nextShmId; /* Next available unixShm.id value */ +#endif }; /* -** Close a shared-memory segment +** A global array of all unixShmFile objects. +** +** The unixMutexHeld() must be true while reading or writing this list. */ -static int unixShmClose(sqlite3_shm *pSharedMem){ - struct unixShm *p = (struct unixShm*)pSharedMem; - if( p && p->pVfs ){ - if( p->pBuf ){ - munmap(p->pBuf, p->size); - } - if( p->fd.pMethod ){ - p->fd.pMethod->xClose((sqlite3_file*)&p->fd); - } - memset(p, 0, sizeof(*p)); - sqlite3_free(p); - } - return SQLITE_OK; -} +static unixShmFile *unixShmFileList = 0; + +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** unixShm.pFile->mutex must be held while reading or writing the +** unixShm.pNext and unixShm.locks[] elements. +** +** The unixShm.pFile element is initialized when the object is created +** and is read-only thereafter. +*/ +struct unixShm { + unixShmFile *pFile; /* The underlying unixShmFile object */ + unixShm *pNext; /* Next unixShm with the same unixShmFile */ + u8 lockState; /* Current lock state */ + u8 readLock; /* Which of the two read-lock states to use */ + u8 hasMutex; /* True if holding the unixShmFile mutex */ + u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ + u8 hasMutexRecov; /* True if holding pFile->mutexRecov */ + u8 sharedMask; /* Mask of shared locks held */ + u8 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG + u8 id; /* Id of this connection with its unixShmFile */ +#endif +}; /* ** Size increment by which shared memory grows */ #define SQLITE_UNIX_SHM_INCR 4096 +/* +** Constants used for locking +*/ +#define UNIX_SHM_BASE 32 /* Byte offset of the first lock byte */ +#define UNIX_SHM_MUTEX 0x01 /* Mask for MUTEX lock */ +#define UNIX_SHM_DMS 0x04 /* Mask for Dead-Man-Switch lock */ +#define UNIX_SHM_A 0x10 /* Mask for region locks... */ +#define UNIX_SHM_B 0x20 +#define UNIX_SHM_C 0x40 +#define UNIX_SHM_D 0x80 + +#ifdef SQLITE_DEBUG +/* +** Return a pointer to a nul-terminated string in static memory that +** describes a locking mask. The string is of the form "MSABCD" with +** each character representing a lock. "M" for MUTEX, "S" for DMS, +** and "A" through "D" for the region locks. If a lock is held, the +** letter is shown. If the lock is not held, the letter is converted +** to ".". +** +** This routine is for debugging purposes only and does not appear +** in a production build. +*/ +static const char *unixShmLockString(u8 mask){ + static char zBuf[48]; + static int iBuf = 0; + char *z; + + z = &zBuf[iBuf]; + iBuf += 8; + if( iBuf>=sizeof(zBuf) ) iBuf = 0; + + z[0] = (mask & UNIX_SHM_MUTEX) ? 'M' : '.'; + z[1] = (mask & UNIX_SHM_DMS) ? 'S' : '.'; + z[2] = (mask & UNIX_SHM_A) ? 'A' : '.'; + z[3] = (mask & UNIX_SHM_B) ? 'B' : '.'; + z[4] = (mask & UNIX_SHM_C) ? 'C' : '.'; + z[5] = (mask & UNIX_SHM_D) ? 'D' : '.'; + z[6] = 0; + return z; +} +#endif /* SQLITE_DEBUG */ + +/* +** Apply posix advisory locks for all bytes identified in lockMask. +** +** lockMask might contain multiple bits but all bits are guaranteed +** to be contiguous. +** +** Locks block if the UNIX_SHM_MUTEX bit is set and are non-blocking +** otherwise. +*/ +static int unixShmSystemLocks( + unixShmFile *pFile, /* Apply locks to this open shared-memory segment */ + int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ + u8 lockMask /* Which bytes to lock or unlock */ +){ + struct flock f; /* The posix advisory locking structure */ + int lockOp; /* The opcode for fcntl() */ + int i; /* Offset into the locking byte range */ + int rc; /* Result code form fcntl() */ + u8 mask; /* Mask of bits in lockMask */ + + /* Initialize the locking parameters */ + memset(&f, 0, sizeof(f)); + f.l_type = lockType; + f.l_whence = SEEK_SET; + if( (lockMask & UNIX_SHM_MUTEX)!=0 && lockType!=F_UNLCK ){ + lockOp = F_SETLKW; + }else{ + lockOp = F_SETLK; + } + + /* Find the first bit in lockMask that is set */ + for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} + assert( mask!=0 ); + f.l_start = i+UNIX_SHM_BASE; + f.l_len = 1; + + /* Extend the locking range for each additional bit that is set */ + mask <<= 1; + while( mask!=0 && (lockMask & mask)!=0 ){ + f.l_len++; + } + + /* Verify that all bits set in lockMask are contiguous */ + assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); + + /* Acquire the system-level lock */ + rc = (fcntl(pFile->h, lockOp, &f)==0) ? SQLITE_OK : SQLITE_BUSY; + + /* Update the global lock state and do debug tracing */ +#ifdef SQLITE_DEBUG + OSTRACE(("SHM-LOCK ")); + if( rc==SQLITE_OK ){ + if( lockType==F_UNLCK ){ + OSTRACE(("unlock ok")); + pFile->exclMask &= ~lockMask; + pFile->sharedMask &= ~lockMask; + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock ok")); + pFile->exclMask &= ~lockMask; + pFile->sharedMask |= lockMask; + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock ok")); + pFile->exclMask |= lockMask; + pFile->sharedMask &= ~lockMask; + } + }else{ + if( lockType==F_UNLCK ){ + OSTRACE(("unlock failed")); + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock failed")); + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock failed")); + } + } + OSTRACE((" - change requested %s - afterwards %s,%s\n", + unixShmLockString(lockMask), + unixShmLockString(pFile->sharedMask), + unixShmLockString(pFile->exclMask))); +#endif + + return rc; +} + +/* +** For connection p, unlock all of the locks identified by the unlockMask +** parameter. +*/ +static int unixShmUnlock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to be unlocked */ + u8 unlockMask /* Mask of locks to be unlocked */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + u8 allMask; /* Union of locks held by connections other than "p" */ + + /* We never try to unlock locks that we do not hold */ + assert( ((p->exclMask|p->sharedMask) & unlockMask)==unlockMask ); + + /* Compute locks held by sibling connections */ + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + assert( (pX->exclMask & unlockMask)==0 ); + allMask |= pX->sharedMask; + } + + /* Unlock the system-level locks */ + if( (unlockMask & allMask)!=unlockMask ){ + rc = unixShmSystemLocks(pFile, F_UNLCK, unlockMask & ~allMask); + }else{ + rc = SQLITE_OK; + } + + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~unlockMask; + p->sharedMask &= ~unlockMask; + } + return rc; +} + +/* +** Get reader locks for connection p on all locks in the readMask parameter. +*/ +static int unixShmSharedLock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to get the shared locks */ + u8 readMask /* Mask of shared locks to be acquired */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + u8 allShared; /* Union of locks held by connections other than "p" */ + + /* Find out which shared locks are already held by sibling connections. + ** If any sibling already holds an exclusive lock, go ahead and return + ** SQLITE_BUSY. + */ + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; + allShared |= pX->sharedMask; + } + + /* Get shared locks at the system level, if necessary */ + if( (~allShared) & readMask ){ + rc = unixShmSystemLocks(pFile, F_RDLCK, readMask); + }else{ + rc = SQLITE_OK; + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= readMask; + } + return rc; +} + +/* +** For connection p, get an exclusive lock on all locks identified in +** the writeMask parameter. +*/ +static int unixShmExclusiveLock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to get the exclusive locks */ + u8 writeMask /* Mask of exclusive locks to be acquired */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. + */ + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; + if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; + } + + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + rc = unixShmSystemLocks(pFile, F_WRLCK, writeMask); + if( rc==SQLITE_OK ){ + p->sharedMask &= ~writeMask; + p->exclMask |= writeMask; + } + return rc; +} + +/* +** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static void unixShmPurge(void){ + unixShmFile **pp; + unixShmFile *p; + assert( unixMutexHeld() ); + pp = &unixShmFileList; + while( (p = *pp)!=0 ){ + if( p->nRef==0 ){ + if( p->mutex ) sqlite3_mutex_free(p->mutex); + if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf); + if( p->mutexRecov ) sqlite3_mutex_free(p->mutexRecov); + if( p->h>=0 ) close(p->h); + *pp = p->pNext; + sqlite3_free(p); + }else{ + pp = &p->pNext; + } + } +} + /* ** Open a shared-memory area. This implementation uses mmapped files. +** +** When opening a new shared-memory file, if no other instances of that +** file are currently open, in this process or in other processes, then +** the file must be truncated to zero length or have its header cleared. */ static int unixShmOpen( sqlite3_vfs *pVfs, /* The VFS */ @@ -4605,44 +4918,136 @@ static int unixShmOpen( sqlite3_shm **pShm /* Write the unixShm object created here */ ){ struct unixShm *p = 0; + struct unixShmFile *pFile = 0; int rc; - int outFlags; + struct unixFileId fid; struct stat sStat; p = sqlite3_malloc( sizeof(*p) ); - *pShm = (sqlite3_shm*)p; if( p==0 ) return SQLITE_NOMEM; memset(p, 0, sizeof(*p)); - p->pVfs = pVfs; - rc = pVfs->xOpen(pVfs, zName, (sqlite3_file*)&p->fd, - SQLITE_OPEN_READWRITE | SQLITE_OPEN_MAIN_JOURNAL, - &outFlags); - if( rc!=SQLITE_OK ) goto shm_open_err; - rc = fstat(p->fd.h, &sStat); - if( rc!=0 ) goto shm_open_err; - if( sStat.st_sizefd.h, SQLITE_UNIX_SHM_INCR); - if( rc!=0 ) goto shm_open_err; - p->size = SQLITE_UNIX_SHM_INCR; - }else{ - p->size = sStat.st_size; + unixEnterMutex(); + rc = stat(zName, &sStat); + if( rc==0 ){ + memset(&fid, 0, sizeof(fid)); + fid.dev = sStat.st_dev; + fid.ino = sStat.st_ino; + for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){ + if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break; + } + } + if( pFile==0 ){ + int nName = strlen(zName); + pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 ); + if( pFile==0 ){ + rc = SQLITE_NOMEM; + goto shm_open_err; + } + memset(pFile, 0, sizeof(pFile)); + pFile->zFilename = (char*)&pFile[1]; + memcpy(pFile->zFilename, zName, nName+1); + pFile->h = -1; + pFile->pNext = unixShmFileList; + unixShmFileList = pFile; + + pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutex==0 ){ + rc = SQLITE_NOMEM; + goto shm_open_err; + } + pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutexBuf==0 ){ + rc = SQLITE_NOMEM; + goto shm_open_err; + } + pFile->mutexRecov = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutexRecov==0 ){ + rc = SQLITE_NOMEM; + goto shm_open_err; + } + + pFile->h = open(zName, O_CREAT, 0664); + if( pFile->h<0 ){ + rc = SQLITE_CANTOPEN; + goto shm_open_err; + } + + rc = fstat(pFile->h, &sStat); + if( rc ){ + rc = SQLITE_CANTOPEN; + goto shm_open_err; + } + pFile->fid.dev = sStat.st_dev; + pFile->fid.ino = sStat.st_ino; + pFile->size = (int)sStat.st_size; + pFile->size = (pFile->size/SQLITE_UNIX_SHM_INCR)*SQLITE_UNIX_SHM_INCR; + if( pFile->size==0 ){ + pFile->size = SQLITE_UNIX_SHM_INCR; + rc = ftruncate(pFile->h, pFile->size); + if( rc ){ + rc = SQLITE_FULL; + goto shm_open_err; + } + } } - /* Map the file. */ - p->pBuf = mmap(0, p->size, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd.h, 0); - if( p->pBuf==MAP_FAILED ){ - rc = SQLITE_IOERR; - goto shm_open_err; - } + p->pFile = pFile; + p->pNext = pFile->pFirst; +#ifdef SQLITE_DEBUG + p->id = pFile->nextShmId++; +#endif + pFile->pFirst = p; + pFile->nRef++; + *pShm = (sqlite3_shm*)p; + unixLeaveMutex(); return SQLITE_OK; shm_open_err: - unixShmClose((sqlite3_shm*)p); + unixShmPurge(); + sqlite3_free(p); + sqlite3_free(pFile); *pShm = 0; + unixLeaveMutex(); return rc; } +/* +** Close a connectioon to shared-memory. +*/ +static int unixShmClose(sqlite3_shm *pSharedMem){ + unixShm *p; /* The connection to be closed */ + unixShmFile *pFile; /* The underlying shared-memory file */ + unixShm **pp; /* For looping over sibling connections */ + int nRef; /* Number of connections to pFile */ + + p = (struct unixShm*)pSharedMem; + pFile = p->pFile; + + /* Verify that the connection being closed holds no locks */ + assert( p->exclMask==0 ); + assert( p->sharedMask==0 ); + + + /* Remove connection p from the set of connections associated with pFile */ + sqlite3_mutex_enter(pFile->mutex); + for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} + *pp = p->pNext; + pFile->nRef--; + nRef = pFile->nRef; + + /* Free the connection p */ + sqlite3_free(p); + sqlite3_mutex_leave(pFile->mutex); + + /* If pFile->nRef has reached 0, then close the underlying + ** shared-memory file, too */ + if( nRef==0 ){ + unixShmPurge(); + } + return SQLITE_OK; +} + /* ** Query and/or changes the size of a shared-memory segment. ** The reqSize parameter is the new size of the segment, or -1 to @@ -4661,26 +5066,31 @@ static int unixShmSize( int *pNewSize, /* Write new size here */ char **ppBuf /* Write new buffer origin here */ ){ - struct unixShm *p = (struct unixShm*)pSharedMem; + unixShm *p = (unixShm*)pSharedMem; + unixShmFile *pFile = p->pFile; int rc = SQLITE_OK; + sqlite3_mutex_enter(pFile->mutexBuf); + sqlite3_mutex_enter(pFile->mutex); if( reqSize>=0 ){ reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR; reqSize *= SQLITE_UNIX_SHM_INCR; - if( reqSize!=p->size ){ - munmap(p->pBuf, p->size); - rc = ftruncate(p->fd.h, reqSize); + if( reqSize!=pFile->size ){ + if( pFile->pMMapBuf ) munmap(pFile->pMMapBuf, pFile->size); + rc = ftruncate(pFile->h, reqSize); if( rc ){ - p->pBuf = 0; - p->size = 0; + pFile->pMMapBuf = 0; + pFile->size = 0; }else{ - p->pBuf = mmap(0, reqSize, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd.h,0); - p->size = p->pBuf ? reqSize : 0; + pFile->pMMapBuf = mmap(0, reqSize, PROT_READ|PROT_WRITE, MAP_SHARED, + pFile->h, 0); + pFile->size = pFile->pMMapBuf ? reqSize : 0; } } } - *pNewSize = p->size; - *ppBuf = p->pBuf; + *pNewSize = pFile->size; + *ppBuf = pFile->pMMapBuf; + sqlite3_mutex_leave(pFile->mutex); return rc; } @@ -4689,19 +5099,138 @@ static int unixShmSize( ** threads are free to resize it if necessary. */ static int unixShmRelease(sqlite3_shm *pSharedMem){ + unixShm *p = (unixShm*)pSharedMem; + unixShmFile *pFile = p->pFile; + sqlite3_mutex_leave(pFile->mutexBuf); return SQLITE_OK; } + + /* -** Create or release a lock on shared memory. +** Change the lock state for a shared-memory segment. */ static int unixShmLock( sqlite3_shm *pSharedMem, /* Pointer from unixShmOpen() */ - int desiredLock, /* The locking state desired */ - int *pGotLock, /* The locking state actually obtained */ - int shouldBlock /* Block for the lock if true and possible */ + int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ + int *pGotLock /* The lock you actually got */ ){ - return SQLITE_OK; + unixShm *p = (unixShm*)pSharedMem; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_PROTOCOL; + + /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never + ** directly requested; they are side effects from requesting + ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. + */ + assert( desiredLock==SQLITE_SHM_QUERY + || desiredLock==SQLITE_SHM_UNLOCK + || desiredLock==SQLITE_SHM_READ + || desiredLock==SQLITE_SHM_WRITE + || desiredLock==SQLITE_SHM_CHECKPOINT + || desiredLock==SQLITE_SHM_RECOVER ); + + /* Return directly if this is just a lock state query, or if + ** the connection is already in the desired locking state. + */ + if( desiredLock==SQLITE_SHM_QUERY + || desiredLock==p->lockState + || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) + ){ + *pGotLock = p->lockState; + return SQLITE_OK; + } + + sqlite3_mutex_enter(pFile->mutex); + switch( desiredLock ){ + case SQLITE_SHM_UNLOCK: { + assert( p->lockState!=SQLITE_SHM_RECOVER ); + unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D); + rc = SQLITE_OK; + p->lockState = SQLITE_SHM_UNLOCK; + break; + } + case SQLITE_SHM_READ: { + if( p->lockState==SQLITE_SHM_UNLOCK ){ + int nAttempt; + rc = SQLITE_BUSY; + assert( p->lockState==SQLITE_SHM_UNLOCK ); + for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ + rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B); + if( rc==SQLITE_BUSY ){ + rc = unixShmSharedLock(pFile, p, UNIX_SHM_D); + if( rc==SQLITE_OK ){ + p->lockState = p->readLock = SQLITE_SHM_READ_FULL; + } + }else{ + unixShmUnlock(pFile, p, UNIX_SHM_B); + p->lockState = p->readLock = SQLITE_SHM_READ; + } + } + }else if( p->lockState==SQLITE_SHM_WRITE ){ + unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); + p->lockState = p->readLock; + rc = SQLITE_OK; + }else{ + assert( p->lockState==SQLITE_SHM_RECOVER ); + unixShmUnlock(pFile, p, UNIX_SHM_MUTEX); + sqlite3_mutex_leave(pFile->mutexRecov); + p->lockState = p->readLock; + rc = SQLITE_OK; + } + break; + } + case SQLITE_SHM_WRITE: { + assert( p->lockState==SQLITE_SHM_READ + || p->lockState==SQLITE_SHM_READ_FULL ); + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_WRITE; + } + break; + } + case SQLITE_SHM_CHECKPOINT: { + assert( p->lockState==SQLITE_SHM_UNLOCK + || p->lockState==SQLITE_SHM_PENDING + || p->lockState==SQLITE_SHM_RECOVER ); + if( p->lockState==SQLITE_SHM_RECOVER ){ + unixShmUnlock(pFile, p, UNIX_SHM_MUTEX); + sqlite3_mutex_leave(pFile->mutexRecov); + p->lockState = SQLITE_SHM_CHECKPOINT; + rc = SQLITE_OK; + } + if( p->lockState==SQLITE_SHM_UNLOCK ){ + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_PENDING; + } + } + if( p->lockState==SQLITE_SHM_PENDING ){ + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_CHECKPOINT; + } + } + break; + } + default: { + assert( desiredLock==SQLITE_SHM_RECOVER ); + assert( p->lockState==SQLITE_SHM_READ + || p->lockState==SQLITE_SHM_READ_FULL + || p->lockState==SQLITE_SHM_CHECKPOINT ); + sqlite3_mutex_leave(pFile->mutex); + sqlite3_mutex_enter(pFile->mutexRecov); + sqlite3_mutex_enter(pFile->mutex); + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_MUTEX); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_RECOVER; + } + break; + } + } + sqlite3_mutex_leave(pFile->mutex); + *pGotLock = p->lockState; + return rc; } /* diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 9f6f1bc967..70e7d26a8c 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -848,7 +848,7 @@ struct sqlite3_vfs { int (*xShmRelease)(sqlite3_shm*); int (*xShmPush)(sqlite3_shm*); int (*xShmPull)(sqlite3_shm*); - int (*xShmLock)(sqlite3_shm*, int desiredLock, int *gotLock, int shouldBlock); + int (*xShmLock)(sqlite3_shm*, int desiredLock, int *gotLock); int (*xShmClose)(sqlite3_shm*); int (*xShmDelete)(sqlite3_vfs*, const char *zName); int (*xRename)(sqlite3_vfs*, const char *zOld, const char *zNew, int dirSync); @@ -887,7 +887,7 @@ struct sqlite3_vfs { ** state. */ #define SQLITE_SHM_UNLOCK 0 -#define SQLITE_SHM_READ_PREFIX 1 +#define SQLITE_SHM_READ 1 #define SQLITE_SHM_READ_FULL 2 #define SQLITE_SHM_WRITE 3 #define SQLITE_SHM_PENDING 4