Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -3600,19 +3600,16 @@ return SQLITE_OK; } /* -** Query and/or changes the size of the underlying storage for -** a shared-memory segment. The reqSize parameter is the new size -** of the underlying storage, or -1 to do just a query. The size -** of the underlying storage (after resizing if resizing occurs) is -** written into pNewSize. +** Changes the size of the underlying storage for a shared-memory segment. ** -** This routine does not (necessarily) change the size of the mapping -** of the underlying storage into memory. Use xShmGet() to change -** the mapping size. +** The reqSize parameter is the new requested size of the shared memory. +** This implementation is free to increase the shared memory size to +** any amount greater than or equal to reqSize. If the shared memory is +** already as big or bigger as reqSize, this routine is a no-op. ** ** The reqSize parameter is the minimum size requested. The implementation ** is free to expand the storage to some larger amount if it chooses. */ static int unixShmSize( @@ -3627,39 +3624,33 @@ struct stat sStat; assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); - /* On a query, this loop runs once. When reqSize>=0, the loop potentially - ** runs twice, except if the actual size is already greater than or equal - ** to the requested size, reqSize is set to -1 on the first iteration and - ** the loop only runs once. - */ while( 1 ){ if( fstat(pShmNode->h, &sStat)==0 ){ *pNewSize = (int)sStat.st_size; - if( reqSize>=0 && reqSize<=(int)sStat.st_size ) break; + if( reqSize<=(int)sStat.st_size ) break; }else{ *pNewSize = 0; rc = SQLITE_IOERR; break; } - if( reqSize<0 ) break; - reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR; - reqSize *= SQLITE_UNIX_SHM_INCR; rc = ftruncate(pShmNode->h, reqSize); reqSize = -1; } return rc; } /* -** Map the shared storage into memory. The minimum size of the -** mapping should be reqMapSize if reqMapSize is positive. If -** reqMapSize is zero or negative, the implementation can choose -** whatever mapping size is convenient. +** Map the shared storage into memory. +** +** If reqMapSize is positive, then an attempt is made to make the +** mapping at least reqMapSize bytes in size. However, the mapping +** will never be larger than the size of the underlying shared memory +** as set by prior calls to xShmSize(). ** ** *ppBuf is made to point to the memory which is a mapping of the ** underlying storage. A mutex is acquired to prevent other threads ** from running while *ppBuf is in use in order to prevent other threads ** remapping *ppBuf out from under this thread. The unixShmRelease() @@ -3672,13 +3663,16 @@ ** being remapped also prevents more than one thread from being in ** RECOVER at a time. But, RECOVER sometimes wants to remap itself. ** To prevent RECOVER from losing its lock while remapping, the ** mutex is not released by unixShmRelease() when in RECOVER. ** -** *pNewMapSize is set to the size of the mapping. +** *pNewMapSize is set to the size of the mapping. Usually *pNewMapSize +** will be reqMapSize or larger, though it could be smaller if the +** underlying shared memory has never been enlarged to reqMapSize bytes +** by prior calls to xShmSize(). ** -** *ppBuf and *pNewMapSize might be NULL and zero if no space has +** *ppBuf might be NULL and zero if no space has ** yet been allocated to the underlying storage. */ static int unixShmGet( sqlite3_file *fd, /* Database file holding shared memory */ int reqMapSize, /* Requested size of mapping. -1 means don't care */ @@ -3699,21 +3693,25 @@ p->hasMutexBuf = 1; } sqlite3_mutex_enter(pShmNode->mutex); if( pShmNode->szMap==0 || reqMapSize>pShmNode->szMap ){ int actualSize; - if( unixShmSize(fd, -1, &actualSize)==SQLITE_OK - && reqMapSizepMMapBuf ){ + reqMapSize = actualSize; + if( pShmNode->pMMapBuf || reqMapSize<=0 ){ munmap(pShmNode->pMMapBuf, pShmNode->szMap); } - pShmNode->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED, - pShmNode->h, 0); - pShmNode->szMap = pShmNode->pMMapBuf ? reqMapSize : 0; + if( reqMapSize>0 ){ + pShmNode->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED, + pShmNode->h, 0); + pShmNode->szMap = pShmNode->pMMapBuf ? reqMapSize : 0; + }else{ + pShmNode->pMMapBuf = 0; + pShmNode->szMap = 0; + } } *pNewMapSize = pShmNode->szMap; *ppBuf = pShmNode->pMMapBuf; sqlite3_mutex_leave(pShmNode->mutex); return rc; Index: src/os_win.c ================================================================== --- src/os_win.c +++ src/os_win.c @@ -1756,22 +1756,19 @@ return SQLITE_OK; } /* -** Query and/or changes the size of the underlying storage for -** a shared-memory segment. The reqSize parameter is the new size -** of the underlying storage, or -1 to do just a query. The size -** of the underlying storage (after resizing if resizing occurs) is -** written into pNewSize. -** -** This routine does not (necessarily) change the size of the mapping -** of the underlying storage into memory. Use xShmGet() to change -** the mapping size. -** -** The reqSize parameter is the minimum size requested. The implementation -** is free to expand the storage to some larger amount if it chooses. +** Increase the size of the underlying storage for a shared-memory segment. +** +** The reqSize parameter is the new requested minimum size of the underlying +** shared memory. This routine may choose to make the shared memory larger +** than this value (for example to round the shared memory size up to an +** operating-system dependent page size.) +** +** This routine will only grow the size of shared memory. A request for +** a smaller size is a no-op. */ static int winShmSize( sqlite3_file *fd, /* Database holding the shared memory */ int reqSize, /* Requested size. -1 for query only */ int *pNewSize /* Write new size here */ Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -242,10 +242,11 @@ ** only support mandatory file-locks, we do not read or write data ** from the region of the file on which locks are applied. */ #define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2) #define WALINDEX_LOCK_RESERVED 8 +#define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED) /* Size of header before each frame in wal */ #define WAL_FRAME_HDRSIZE 24 /* Size of write ahead log header */ @@ -560,62 +561,56 @@ ** it. */ static void walIndexUnmap(Wal *pWal){ if( pWal->pWiData ){ sqlite3OsShmRelease(pWal->pDbFd); - pWal->pWiData = 0; } + pWal->pWiData = 0; + pWal->szWIndex = -1; } /* ** Map the wal-index file into memory if it isn't already. ** -** The reqSize parameter is the minimum required size of the mapping. -** A value of -1 means "don't care". +** The reqSize parameter is the requested size of the mapping. The +** mapping will be at least this big if the underlying storage is +** that big. But the mapping will never grow larger than the underlying +** storage. Use the walIndexRemap() to enlarget the storage space. */ static int walIndexMap(Wal *pWal, int reqSize){ int rc = SQLITE_OK; if( pWal->pWiData==0 || reqSize>pWal->szWIndex ){ walIndexUnmap(pWal); rc = sqlite3OsShmGet(pWal->pDbFd, reqSize, &pWal->szWIndex, (void volatile**)(char volatile*)&pWal->pWiData); - if( rc==SQLITE_OK && pWal->pWiData==0 ){ - /* Make sure pWal->pWiData is not NULL while we are holding the - ** lock on the mapping. */ - assert( pWal->szWIndex==0 ); - pWal->pWiData = &pWal->iCallback; - } if( rc!=SQLITE_OK ){ walIndexUnmap(pWal); } } return rc; } /* +** Enlarge the wal-index to be at least enlargeTo bytes in size and ** Remap the wal-index so that the mapping covers the full size ** of the underlying file. ** ** If enlargeTo is non-negative, then increase the size of the underlying ** storage to be at least as big as enlargeTo before remapping. */ static int walIndexRemap(Wal *pWal, int enlargeTo){ int rc; int sz; + assert( pWal->lockState>=SQLITE_SHM_WRITE ); rc = sqlite3OsShmSize(pWal->pDbFd, enlargeTo, &sz); if( rc==SQLITE_OK && sz>pWal->szWIndex ){ walIndexUnmap(pWal); rc = walIndexMap(pWal, sz); } + assert( pWal->szWIndex>=enlargeTo || rc!=SQLITE_OK ); return rc; } - -/* -** Increment by which to increase the wal-index file size. -*/ -#define WALINDEX_MMAP_INCREMENT (64*1024) - /* ** Compute a hash on a page number. The resulting hash value must land ** between 0 and (HASHTABLE_NSLOT-1). */ @@ -736,14 +731,13 @@ int rc; /* Return code */ int nMapping; /* Required mapping size in bytes */ /* Make sure the wal-index is mapped. Enlarge the mapping if required. */ nMapping = walMappingSize(iFrame); - rc = walIndexMap(pWal, -1); + rc = walIndexMap(pWal, nMapping); while( rc==SQLITE_OK && nMapping>pWal->szWIndex ){ - int nByte = pWal->szWIndex + WALINDEX_MMAP_INCREMENT; - rc = walIndexRemap(pWal, nByte); + rc = walIndexRemap(pWal, nMapping); } /* Assuming the wal-index file was successfully mapped, find the hash ** table and section of of the page number array that pertain to frame ** iFrame of the WAL. Then populate the page number array and the hash @@ -905,11 +899,11 @@ sqlite3_free(aFrame); } finished: if( rc==SQLITE_OK && pWal->hdr.mxFrame==0 ){ - rc = walIndexRemap(pWal, WALINDEX_MMAP_INCREMENT); + rc = walIndexRemap(pWal, walMappingSize(1)); } if( rc==SQLITE_OK ){ pWal->hdr.aFrameCksum[0] = aFrameCksum[0]; pWal->hdr.aFrameCksum[1] = aFrameCksum[1]; walIndexWriteHdr(pWal); @@ -981,10 +975,11 @@ } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; + pRet->szWIndex = -1; sqlite3_randomness(8, &pRet->hdr.aSalt); pRet->zWalName = zWal = pVfs->szOsFile + (char*)pRet->pWalFd; sqlite3_snprintf(nWal, zWal, "%s-wal", zDbName); rc = sqlite3OsShmOpen(pDbFd); @@ -1307,18 +1302,16 @@ int walIndexTryHdr(Wal *pWal, int *pChanged){ u32 aCksum[2]; /* Checksum on the header content */ WalIndexHdr h1, h2; /* Two copies of the header content */ WalIndexHdr *aHdr; /* Header in shared memory */ - assert( pWal->pWiData ); - if( pWal->szWIndex==0 ){ - /* The wal-index is of size 0 bytes. This is handled in the same way - ** as an invalid header. The caller will run recovery to construct - ** a valid wal-index file before accessing the database. - */ + if( pWal->szWIndex < WALINDEX_HDR_SIZE ){ + /* The wal-index is not large enough to hold the header, then assume + ** header is invalid. */ return 1; } + assert( pWal->pWiData ); /* Read the header. The caller may or may not have an exclusive ** (WRITE, PENDING, CHECKPOINT or RECOVER) lock on the wal-index ** file, meaning it is possible that an inconsistent snapshot is read ** from the file. If this happens, return non-zero. @@ -1376,11 +1369,11 @@ int rc; /* Return code */ int lockState; /* pWal->lockState before running recovery */ assert( pWal->lockState>=SQLITE_SHM_READ ); assert( pChanged ); - rc = walIndexMap(pWal, -1); + rc = walIndexMap(pWal, walMappingSize(1)); if( rc!=SQLITE_OK ){ return rc; } /* First attempt to read the wal-index header. This may fail for one @@ -1626,11 +1619,12 @@ /* If this connection is not reading the most recent database snapshot, ** it is not possible to write to the database. In this case release ** the write locks and return SQLITE_BUSY. */ if( rc==SQLITE_OK ){ - rc = walIndexMap(pWal, sizeof(WalIndexHdr)); + rc = walIndexMap(pWal, walMappingSize(1)); + assert( pWal->szWIndex>=WALINDEX_HDR_SIZE || rc!=SQLITE_OK ); if( rc==SQLITE_OK && memcmp(&pWal->hdr, (void*)pWal->pWiData, sizeof(WalIndexHdr)) ){ rc = SQLITE_BUSY; }