int lsm_close(lsm_db *pDb){ int rc = LSM_OK; if( pDb ){ assert_db_state(pDb); if( pDb->pCsr || pDb->nTransOpen ){ rc = LSM_MISUSE_BKPT; }else{ lsmMCursorFreeCache(pDb); lsmFreeSnapshot(pDb->pEnv, pDb->pClient); pDb->pClient = 0; assertRwclientLockValue(pDb); lsmDbDatabaseRelease(pDb); lsmLogClose(pDb); lsmFsClose(pDb->pFS); assert( pDb->mLock==0 ); /* Invoke any destructors registered for the compression or ** compression factory callbacks. */ if( pDb->factory.xFree ) pDb->factory.xFree(pDb->factory.pCtx); if( pDb->compress.xFree ) pDb->compress.xFree(pDb->compress.pCtx); lsmFree(pDb->pEnv, pDb->rollback.aArray); lsmFree(pDb->pEnv, pDb->aTrans); lsmFree(pDb->pEnv, pDb->apShm); lsmFree(pDb->pEnv, pDb); } } return rc; }
static void freeClientSnapshot(lsm_env *pEnv, Snapshot *p){ Level *pLevel; assert( p->nRef==0 ); for(pLevel=p->pLevel; pLevel; pLevel=pLevel->pNext){ lsmFree(pEnv, pLevel->pSplitKey); } lsmFree(pEnv, p->pExport); lsmFree(pEnv, p); }
/* ** Release a reference to a Database object obtained from lsmDbDatabaseFind(). ** There should be exactly one call to this function for each successful ** call to Find(). */ void lsmDbDatabaseRelease(lsm_db *pDb){ Database *p = pDb->pDatabase; if( p ){ enterGlobalMutex(pDb->pEnv); p->nDbRef--; if( p->nDbRef==0 ){ int rc = LSM_OK; Database **pp; /* Remove the Database structure from the linked list. */ for(pp=&gShared.pDatabase; *pp!=p; pp=&((*pp)->pDbNext)); *pp = p->pDbNext; /* Flush the in-memory tree, if required. If there is data to flush, ** this will create a new client snapshot in Database.pClient. The ** checkpoint (serialization) of this snapshot may be written to disk ** by the following block. */ if( p->bDirty || 0==lsmTreeIsEmpty(p->pTree) ){ rc = lsmFlushToDisk(pDb); } /* Write a checkpoint, also if required */ if( rc==LSM_OK && p->pClient ){ rc = lsmCheckpointWrite(pDb); } /* If the checkpoint was written successfully, delete the log file */ if( rc==LSM_OK && pDb->pFS ){ lsmFsCloseAndDeleteLog(pDb->pFS); } /* Free the in-memory tree object */ lsmTreeRelease(pDb->pEnv, p->pTree); /* Free the contents of the worker snapshot */ lsmSortedFreeLevel(pDb->pEnv, p->worker.pLevel); lsmFree(pDb->pEnv, p->worker.freelist.aEntry); lsmFree(pDb->pEnv, p->append.aPoint); /* Free the client snapshot */ if( p->pClient ){ assert( p->pClient->nRef==1 ); p->pClient->nRef = 0; freeClientSnapshot(pDb->pEnv, p->pClient); } freeDatabase(pDb->pEnv, p); } leaveGlobalMutex(pDb->pEnv); } }
static int getFullpathname( lsm_env *pEnv, const char *zRel, char **pzAbs ){ int nAlloc = 0; char *zAlloc = 0; int nReq = 0; int rc; do{ nAlloc = nReq; rc = pEnv->xFullpath(pEnv, zRel, zAlloc, &nReq); if( nReq>nAlloc ){ zAlloc = lsmReallocOrFreeRc(pEnv, zAlloc, nReq, &rc); } }while( nReq>nAlloc && rc==LSM_OK ); if( rc!=LSM_OK ){ lsmFree(pEnv, zAlloc); zAlloc = 0; } *pzAbs = zAlloc; return rc; }
int lsm_close(lsm_db *pDb){ int rc = LSM_OK; if( pDb ){ assert_db_state(pDb); if( pDb->pCsr || pDb->nTransOpen ){ rc = LSM_MISUSE_BKPT; }else{ assert( pDb->pWorker==0 && pDb->pTV==0 ); lsmDbDatabaseRelease(pDb); lsmFsClose(pDb->pFS); lsmFree(pDb->pEnv, pDb->aTrans); lsmFree(pDb->pEnv, pDb); } } return rc; }
static int lsmPosixOsShmMap(lsm_file *pFile, int iChunk, int sz, void **ppShm){ PosixFile *p = (PosixFile *)pFile; *ppShm = 0; assert( sz==LSM_SHM_CHUNK_SIZE ); if( iChunk>=p->nShm ){ int i; void **apNew; int nNew = iChunk+1; off_t nReq = nNew * LSM_SHM_CHUNK_SIZE; struct stat sStat; /* If the shared-memory file has not been opened, open it now. */ if( p->shmfd<=0 ){ char *zShm = posixShmFile(p); if( !zShm ) return LSM_NOMEM_BKPT; p->shmfd = open(zShm, O_RDWR|O_CREAT, 0644); lsmFree(p->pEnv, zShm); if( p->shmfd<0 ){ return LSM_IOERR_BKPT; } } /* If the shared-memory file is not large enough to contain the ** requested chunk, cause it to grow. */ if( fstat(p->shmfd, &sStat) ){ return LSM_IOERR_BKPT; } if( sStat.st_size<nReq ){ if( ftruncate(p->shmfd, nReq) ){ return LSM_IOERR_BKPT; } } apNew = (void **)lsmRealloc(p->pEnv, p->apShm, sizeof(void *) * nNew); if( !apNew ) return LSM_NOMEM_BKPT; for(i=p->nShm; i<nNew; i++){ apNew[i] = 0; } p->apShm = apNew; p->nShm = nNew; } if( p->apShm[iChunk]==0 ){ p->apShm[iChunk] = mmap(0, LSM_SHM_CHUNK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->shmfd, iChunk*LSM_SHM_CHUNK_SIZE ); if( p->apShm[iChunk]==0 ) return LSM_IOERR_BKPT; } *ppShm = p->apShm[iChunk]; return LSM_OK; }
/* ** This function frees all resources held by the Database structure passed ** as the only argument. */ static void freeDatabase(lsm_env *pEnv, Database *p){ if( p ){ /* Free the mutexes */ lsmMutexDel(pEnv, p->pClientMutex); lsmMutexDel(pEnv, p->pWorkerMutex); /* Free the log buffer. */ lsmStringClear(&p->log.buf); /* Free the memory allocated for the Database struct itself */ lsmFree(pEnv, p); } }
/* ** Increase the memory allocated for holding the string. Realloc as needed. ** ** If a memory allocation error occurs, set pStr->n to -1 and free the existing ** allocation. If a prior memory allocation has occurred, this routine is a ** no-op. */ int lsmStringExtend(LsmString *pStr, int nNew){ assert( nNew>0 ); if( pStr->n<0 ) return LSM_NOMEM; if( pStr->n + nNew >= pStr->nAlloc ){ int nAlloc = pStr->n + nNew + 100; char *zNew = lsmRealloc(pStr->pEnv, pStr->z, nAlloc); if( zNew==0 ){ lsmFree(pStr->pEnv, pStr->z); nAlloc = 0; pStr->n = -1; pStr->z = 0; }else{ pStr->nAlloc = nAlloc; pStr->z = zNew; } } return (pStr->z ? LSM_OK : LSM_NOMEM_BKPT); }
/* ** Open a new connection to database zFilename. */ int lsm_open(lsm_db *pDb, const char *zFilename){ int rc; if( pDb->pDatabase ){ rc = LSM_MISUSE; }else{ char *zFull; /* Translate the possibly relative pathname supplied by the user into ** an absolute pathname. This is required because the supplied path ** is used (either directly or with "-log" appended to it) for more ** than one purpose - to open both the database and log files, and ** perhaps to unlink the log file during disconnection. An absolute ** path is required to ensure that the correct files are operated ** on even if the application changes the cwd. */ rc = getFullpathname(pDb->pEnv, zFilename, &zFull); assert( rc==LSM_OK || zFull==0 ); /* Connect to the database. */ if( rc==LSM_OK ){ rc = lsmDbDatabaseConnect(pDb, zFull); } if( pDb->bReadonly==0 ){ /* Configure the file-system connection with the page-size and block-size ** of this database. Even if the database file is zero bytes in size ** on disk, these values have been set in shared-memory by now, and so ** are guaranteed not to change during the lifetime of this connection. */ if( rc==LSM_OK && LSM_OK==(rc = lsmCheckpointLoad(pDb, 0)) ){ lsmFsSetPageSize(pDb->pFS, lsmCheckpointPgsz(pDb->aSnapshot)); lsmFsSetBlockSize(pDb->pFS, lsmCheckpointBlksz(pDb->aSnapshot)); } } lsmFree(pDb->pEnv, zFull); assertRwclientLockValue(pDb); } assert( pDb->bReadonly==0 || pDb->bReadonly==1 ); assert( rc!=LSM_OK || (pDb->pShmhdr==0)==(pDb->bReadonly==1) ); return rc; }
static int lsmPosixOsFullpath( lsm_env *pEnv, const char *zName, char *zOut, int *pnOut ){ int nBuf = *pnOut; int nReq; if( zName[0]!='/' ){ char *z; char *zTmp; int nTmp = 512; zTmp = lsmMalloc(pEnv, nTmp); while( zTmp ){ z = getcwd(zTmp, nTmp); if( z || errno!=ERANGE ) break; nTmp = nTmp*2; zTmp = lsmReallocOrFree(pEnv, zTmp, nTmp); } if( zTmp==0 ) return LSM_NOMEM_BKPT; if( z==0 ) return LSM_IOERR_BKPT; assert( z==zTmp ); nTmp = strlen(zTmp); nReq = nTmp + 1 + strlen(zName) + 1; if( nReq<=nBuf ){ memcpy(zOut, zTmp, nTmp); zOut[nTmp] = '/'; memcpy(&zOut[nTmp+1], zName, strlen(zName)+1); } lsmFree(pEnv, zTmp); }else{ nReq = strlen(zName)+1; if( nReq<=nBuf ){ memcpy(zOut, zName, strlen(zName)+1); } } *pnOut = nReq; return LSM_OK; }
static int lsmPosixOsShmUnmap(lsm_file *pFile, int bDelete){ PosixFile *p = (PosixFile *)pFile; if( p->shmfd>0 ){ int i; for(i=0; i<p->nShm; i++){ if( p->apShm[i] ){ munmap(p->apShm[i], LSM_SHM_CHUNK_SIZE); p->apShm[i] = 0; } } close(p->shmfd); p->shmfd = 0; if( bDelete ){ char *zShm = posixShmFile(p); if( zShm ) unlink(zShm); lsmFree(p->pEnv, zShm); } } return LSM_OK; }
int lsmInfoFreelist(lsm_db *pDb, char **pzOut){ Snapshot *pWorker; /* Worker snapshot */ int bUnlock = 0; LsmString s; int rc; /* Obtain the worker snapshot */ rc = infoGetWorker(pDb, &pWorker, &bUnlock); if( rc!=LSM_OK ) return rc; lsmStringInit(&s, pDb->pEnv); rc = lsmWalkFreelist(pDb, 0, infoFreelistCb, &s); if( rc!=LSM_OK ){ lsmFree(pDb->pEnv, s.z); }else{ *pzOut = s.z; } /* Release the snapshot and return */ infoFreeWorker(pDb, bUnlock); return rc; }
/* ** Open a new connection to database zFilename. */ int lsm_open(lsm_db *pDb, const char *zFilename){ int rc; if( pDb->pDatabase ){ rc = LSM_MISUSE; }else{ char *zFull; /* Translate the possibly relative pathname supplied by the user into ** an absolute pathname. This is required because the supplied path ** is used (either directly or with "-log" appended to it) for more ** than one purpose - to open both the database and log files, and ** perhaps to unlink the log file during disconnection. An absolute ** path is required to ensure that the correct files are operated ** on even if the application changes the cwd. */ rc = getFullpathname(pDb->pEnv, zFilename, &zFull); assert( rc==LSM_OK || zFull==0 ); /* Open the database file */ if( rc==LSM_OK ){ rc = lsmFsOpen(pDb, zFull); } /* Open the shared data handle. */ if( rc==LSM_OK ){ rc = lsmDbDatabaseFind(pDb, zFilename); } if( rc==LSM_OK ){ rc = dbRecoverIfRequired(pDb); } lsmFree(pDb->pEnv, zFull); } return rc; }
static void lsmPosixOsMutexDel(lsm_mutex *pMutex) { NoopMutex *p = (NoopMutex *)pMutex; assert( p->bStatic==0 && p->pEnv ); lsmFree(p->pEnv, p); }
static void lsmPosixOsMutexDel(lsm_mutex *p){ PthreadMutex *pMutex = (PthreadMutex *)p; pthread_mutex_destroy(&pMutex->mutex); lsmFree(pMutex->pEnv, pMutex); }
/* ** Return a reference to the shared Database handle for the database ** identified by canonical path zName. If this is the first connection to ** the named database, a new Database object is allocated. Otherwise, a ** pointer to an existing object is returned. ** ** If successful, *ppDatabase is set to point to the shared Database ** structure and LSM_OK returned. Otherwise, *ppDatabase is set to NULL ** and and LSM error code returned. ** ** Each successful call to this function should be (eventually) matched ** by a call to lsmDbDatabaseRelease(). */ int lsmDbDatabaseFind( lsm_db *pDb, /* Database handle */ const char *zName /* Path to db file */ ){ lsm_env *pEnv = pDb->pEnv; int rc; /* Return code */ Database *p = 0; /* Pointer returned via *ppDatabase */ int nId = 0; void *pId = 0; assert( pDb->pDatabase==0 ); rc = lsmFsFileid(pDb, &pId, &nId); if( rc!=LSM_OK ) return rc; rc = enterGlobalMutex(pEnv); if( rc==LSM_OK ){ /* Search the global list for an existing object. TODO: Need something ** better than the strcmp() below to figure out if a given Database ** object represents the requested file. */ for(p=gShared.pDatabase; p; p=p->pDbNext){ if( nId==p->nId && 0==memcmp(pId, p->pId, nId) ) break; } /* If no suitable Database object was found, allocate a new one. */ if( p==0 ){ int nName = strlen(zName); p = (Database *)lsmMallocZeroRc(pEnv, sizeof(Database)+nId+nName+1, &rc); /* Initialize the log handle */ if( rc==LSM_OK ){ p->log.cksum0 = LSM_CKSUM0_INIT; p->log.cksum1 = LSM_CKSUM1_INIT; lsmStringInit(&p->log.buf, pEnv); } /* Allocate the two mutexes */ if( rc==LSM_OK ) rc = lsmMutexNew(pEnv, &p->pWorkerMutex); if( rc==LSM_OK ) rc = lsmMutexNew(pEnv, &p->pClientMutex); /* If no error has occurred, fill in other fields and link the new ** Database structure into the global list starting at ** gShared.pDatabase. Otherwise, if an error has occurred, free any ** resources allocated and return without linking anything new into ** the gShared.pDatabase list. */ if( rc==LSM_OK ){ p->zName = (char *)&p[1]; memcpy((void *)p->zName, zName, nName+1); p->pId = (void *)&p->zName[nName+1]; memcpy(p->pId, pId, nId); p->nId = nId; p->worker.pDatabase = p; p->pDbNext = gShared.pDatabase; gShared.pDatabase = p; p->worker.iLogPg = LSM_INITIAL_LOGPG; p->worker.iId = LSM_INITIAL_SNAPSHOT_ID; p->worker.iSalt1 = LSM_INITIAL_SALT1; p->worker.iSalt2 = LSM_INITIAL_SALT2; p->nPgsz = pDb->nDfltPgsz; p->nBlksz = pDb->nDfltBlksz; }else{ freeDatabase(pEnv, p); p = 0; } } if( p ) p->nDbRef++; leaveGlobalMutex(pEnv); } lsmFree(pEnv, pId); pDb->pDatabase = p; return rc; }
/* ** Clear an LsmString object, releasing any allocated memory that it holds. ** This also clears the error indication (if any). */ void lsmStringClear(LsmString *pStr){ lsmFree(pStr->pEnv, pStr->z); lsmStringInit(pStr, pStr->pEnv); }