0
0
mirror of https://github.com/tursodatabase/libsql.git synced 2024-12-16 03:19:00 +00:00
2023-10-16 13:58:16 +02:00

754 lines
19 KiB
C

/*
** 2011-12-03
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** Unix-specific run-time environment implementation for LSM.
*/
#ifndef _WIN32
#if defined(__GNUC__) || defined(__TINYC__)
/* workaround for ftruncate() visibility on gcc. */
# ifndef _XOPEN_SOURCE
# define _XOPEN_SOURCE 500
# endif
#endif
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include "lsmInt.h"
/* There is no fdatasync() call on Android */
#ifdef __ANDROID__
# define fdatasync(x) fsync(x)
#endif
/*
** An open file is an instance of the following object
*/
typedef struct PosixFile PosixFile;
struct PosixFile {
lsm_env *pEnv; /* The run-time environment */
const char *zName; /* Full path to file */
int fd; /* The open file descriptor */
int shmfd; /* Shared memory file-descriptor */
void *pMap; /* Pointer to mapping of file fd */
off_t nMap; /* Size of mapping at pMap in bytes */
int nShm; /* Number of entries in array apShm[] */
void **apShm; /* Array of 32K shared memory segments */
};
static char *posixShmFile(PosixFile *p){
char *zShm;
int nName = strlen(p->zName);
zShm = (char *)lsmMalloc(p->pEnv, nName+4+1);
if( zShm ){
memcpy(zShm, p->zName, nName);
memcpy(&zShm[nName], "-shm", 5);
}
return zShm;
}
static int lsmPosixOsOpen(
lsm_env *pEnv,
const char *zFile,
int flags,
lsm_file **ppFile
){
int rc = LSM_OK;
PosixFile *p;
p = lsm_malloc(pEnv, sizeof(PosixFile));
if( p==0 ){
rc = LSM_NOMEM;
}else{
int bReadonly = (flags & LSM_OPEN_READONLY);
int oflags = (bReadonly ? O_RDONLY : (O_RDWR|O_CREAT));
memset(p, 0, sizeof(PosixFile));
p->zName = zFile;
p->pEnv = pEnv;
p->fd = open(zFile, oflags, 0644);
if( p->fd<0 ){
lsm_free(pEnv, p);
p = 0;
if( errno==ENOENT ){
rc = lsmErrorBkpt(LSM_IOERR_NOENT);
}else{
rc = LSM_IOERR_BKPT;
}
}
}
*ppFile = (lsm_file *)p;
return rc;
}
static int lsmPosixOsWrite(
lsm_file *pFile, /* File to write to */
lsm_i64 iOff, /* Offset to write to */
void *pData, /* Write data from this buffer */
int nData /* Bytes of data to write */
){
int rc = LSM_OK;
PosixFile *p = (PosixFile *)pFile;
off_t offset;
offset = lseek(p->fd, (off_t)iOff, SEEK_SET);
if( offset!=iOff ){
rc = LSM_IOERR_BKPT;
}else{
ssize_t prc = write(p->fd, pData, (size_t)nData);
if( prc<0 ) rc = LSM_IOERR_BKPT;
}
return rc;
}
static int lsmPosixOsTruncate(
lsm_file *pFile, /* File to write to */
lsm_i64 nSize /* Size to truncate file to */
){
PosixFile *p = (PosixFile *)pFile;
int rc = LSM_OK; /* Return code */
int prc; /* Posix Return Code */
struct stat sStat; /* Result of fstat() invocation */
prc = fstat(p->fd, &sStat);
if( prc==0 && sStat.st_size>nSize ){
prc = ftruncate(p->fd, (off_t)nSize);
}
if( prc<0 ) rc = LSM_IOERR_BKPT;
return rc;
}
static int lsmPosixOsRead(
lsm_file *pFile, /* File to read from */
lsm_i64 iOff, /* Offset to read from */
void *pData, /* Read data into this buffer */
int nData /* Bytes of data to read */
){
int rc = LSM_OK;
PosixFile *p = (PosixFile *)pFile;
off_t offset;
offset = lseek(p->fd, (off_t)iOff, SEEK_SET);
if( offset!=iOff ){
rc = LSM_IOERR_BKPT;
}else{
ssize_t prc = read(p->fd, pData, (size_t)nData);
if( prc<0 ){
rc = LSM_IOERR_BKPT;
}else if( prc<nData ){
memset(&((u8 *)pData)[prc], 0, nData - prc);
}
}
return rc;
}
static int lsmPosixOsSync(lsm_file *pFile){
int rc = LSM_OK;
#ifndef LSM_NO_SYNC
PosixFile *p = (PosixFile *)pFile;
int prc = 0;
if( p->pMap ){
prc = msync(p->pMap, p->nMap, MS_SYNC);
}
if( prc==0 ) prc = fdatasync(p->fd);
if( prc<0 ) rc = LSM_IOERR_BKPT;
#else
(void)pFile;
#endif
return rc;
}
static int lsmPosixOsSectorSize(lsm_file *pFile){
return 512;
}
static int lsmPosixOsRemap(
lsm_file *pFile,
lsm_i64 iMin,
void **ppOut,
lsm_i64 *pnOut
){
off_t iSz;
int prc;
PosixFile *p = (PosixFile *)pFile;
struct stat buf;
/* If the file is between 0 and 2MB in size, extend it in chunks of 256K.
** Thereafter, in chunks of 1MB at a time. */
const int aIncrSz[] = {256*1024, 1024*1024};
int nIncrSz = aIncrSz[iMin>(2*1024*1024)];
if( p->pMap ){
munmap(p->pMap, p->nMap);
*ppOut = p->pMap = 0;
*pnOut = p->nMap = 0;
}
if( iMin>=0 ){
memset(&buf, 0, sizeof(buf));
prc = fstat(p->fd, &buf);
if( prc!=0 ) return LSM_IOERR_BKPT;
iSz = buf.st_size;
if( iSz<iMin ){
iSz = ((iMin + nIncrSz-1) / nIncrSz) * nIncrSz;
prc = ftruncate(p->fd, iSz);
if( prc!=0 ) return LSM_IOERR_BKPT;
}
p->pMap = mmap(0, iSz, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
if( p->pMap==MAP_FAILED ){
p->pMap = 0;
return LSM_IOERR_BKPT;
}
p->nMap = iSz;
}
*ppOut = p->pMap;
*pnOut = p->nMap;
return LSM_OK;
}
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 lsmPosixOsFileid(
lsm_file *pFile,
void *pBuf,
int *pnBuf
){
int prc;
int nBuf;
int nReq;
PosixFile *p = (PosixFile *)pFile;
struct stat buf;
nBuf = *pnBuf;
nReq = (sizeof(buf.st_dev) + sizeof(buf.st_ino));
*pnBuf = nReq;
if( nReq>nBuf ) return LSM_OK;
memset(&buf, 0, sizeof(buf));
prc = fstat(p->fd, &buf);
if( prc!=0 ) return LSM_IOERR_BKPT;
memcpy(pBuf, &buf.st_dev, sizeof(buf.st_dev));
memcpy(&(((u8 *)pBuf)[sizeof(buf.st_dev)]), &buf.st_ino, sizeof(buf.st_ino));
return LSM_OK;
}
static int lsmPosixOsUnlink(lsm_env *pEnv, const char *zFile){
int prc = unlink(zFile);
return prc ? LSM_IOERR_BKPT : LSM_OK;
}
static int lsmPosixOsLock(lsm_file *pFile, int iLock, int eType){
int rc = LSM_OK;
PosixFile *p = (PosixFile *)pFile;
static const short aType[3] = { F_UNLCK, F_RDLCK, F_WRLCK };
struct flock lock;
assert( aType[LSM_LOCK_UNLOCK]==F_UNLCK );
assert( aType[LSM_LOCK_SHARED]==F_RDLCK );
assert( aType[LSM_LOCK_EXCL]==F_WRLCK );
assert( eType>=0 && eType<array_size(aType) );
assert( iLock>0 && iLock<=32 );
memset(&lock, 0, sizeof(lock));
lock.l_whence = SEEK_SET;
lock.l_len = 1;
lock.l_type = aType[eType];
lock.l_start = (4096-iLock);
if( fcntl(p->fd, F_SETLK, &lock) ){
int e = errno;
if( e==EACCES || e==EAGAIN ){
rc = LSM_BUSY;
}else{
rc = LSM_IOERR_BKPT;
}
}
return rc;
}
static int lsmPosixOsTestLock(lsm_file *pFile, int iLock, int nLock, int eType){
int rc = LSM_OK;
PosixFile *p = (PosixFile *)pFile;
static const short aType[3] = { 0, F_RDLCK, F_WRLCK };
struct flock lock;
assert( eType==LSM_LOCK_SHARED || eType==LSM_LOCK_EXCL );
assert( aType[LSM_LOCK_SHARED]==F_RDLCK );
assert( aType[LSM_LOCK_EXCL]==F_WRLCK );
assert( eType>=0 && eType<array_size(aType) );
assert( iLock>0 && iLock<=32 );
memset(&lock, 0, sizeof(lock));
lock.l_whence = SEEK_SET;
lock.l_len = nLock;
lock.l_type = aType[eType];
lock.l_start = (4096-iLock-nLock+1);
if( fcntl(p->fd, F_GETLK, &lock) ){
rc = LSM_IOERR_BKPT;
}else if( lock.l_type!=F_UNLCK ){
rc = LSM_BUSY;
}
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]==MAP_FAILED ){
p->apShm[iChunk] = 0;
return LSM_IOERR_BKPT;
}
}
*ppShm = p->apShm[iChunk];
return LSM_OK;
}
static void lsmPosixOsShmBarrier(void){
}
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;
}
static int lsmPosixOsClose(lsm_file *pFile){
PosixFile *p = (PosixFile *)pFile;
lsmPosixOsShmUnmap(pFile, 0);
if( p->pMap ) munmap(p->pMap, p->nMap);
close(p->fd);
lsm_free(p->pEnv, p->apShm);
lsm_free(p->pEnv, p);
return LSM_OK;
}
static int lsmPosixOsSleep(lsm_env *pEnv, int us){
#if 0
/* Apparently on Android usleep() returns void */
if( usleep(us) ) return LSM_IOERR;
#endif
usleep(us);
return LSM_OK;
}
/****************************************************************************
** Memory allocation routines.
*/
#define BLOCK_HDR_SIZE ROUND8( sizeof(size_t) )
static void *lsmPosixOsMalloc(lsm_env *pEnv, size_t N){
unsigned char * m;
N += BLOCK_HDR_SIZE;
m = (unsigned char *)malloc(N);
*((size_t*)m) = N;
return m + BLOCK_HDR_SIZE;
}
static void lsmPosixOsFree(lsm_env *pEnv, void *p){
if(p){
free( ((unsigned char *)p) - BLOCK_HDR_SIZE );
}
}
static void *lsmPosixOsRealloc(lsm_env *pEnv, void *p, size_t N){
unsigned char * m = (unsigned char *)p;
if(1>N){
lsmPosixOsFree( pEnv, p );
return NULL;
}else if(NULL==p){
return lsmPosixOsMalloc(pEnv, N);
}else{
void * re = NULL;
m -= BLOCK_HDR_SIZE;
#if 0 /* arguable: don't shrink */
size_t * sz = (size_t*)m;
if(*sz >= (size_t)N){
return p;
}
#endif
re = realloc( m, N + BLOCK_HDR_SIZE );
if(re){
m = (unsigned char *)re;
*((size_t*)m) = N;
return m + BLOCK_HDR_SIZE;
}else{
return NULL;
}
}
}
static size_t lsmPosixOsMSize(lsm_env *pEnv, void *p){
unsigned char * m = (unsigned char *)p;
return *((size_t*)(m-BLOCK_HDR_SIZE));
}
#undef BLOCK_HDR_SIZE
#ifdef LSM_MUTEX_PTHREADS
/*************************************************************************
** Mutex methods for pthreads based systems. If LSM_MUTEX_PTHREADS is
** missing then a no-op implementation of mutexes found in lsm_mutex.c
** will be used instead.
*/
#include <pthread.h>
typedef struct PthreadMutex PthreadMutex;
struct PthreadMutex {
lsm_env *pEnv;
pthread_mutex_t mutex;
#ifdef LSM_DEBUG
pthread_t owner;
#endif
};
#ifdef LSM_DEBUG
# define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER, 0 }
#else
# define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER }
#endif
static int lsmPosixOsMutexStatic(
lsm_env *pEnv,
int iMutex,
lsm_mutex **ppStatic
){
static PthreadMutex sMutex[2] = {
LSM_PTHREAD_STATIC_MUTEX,
LSM_PTHREAD_STATIC_MUTEX
};
assert( iMutex==LSM_MUTEX_GLOBAL || iMutex==LSM_MUTEX_HEAP );
assert( LSM_MUTEX_GLOBAL==1 && LSM_MUTEX_HEAP==2 );
*ppStatic = (lsm_mutex *)&sMutex[iMutex-1];
return LSM_OK;
}
static int lsmPosixOsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
PthreadMutex *pMutex; /* Pointer to new mutex */
pthread_mutexattr_t attr; /* Attributes object */
pMutex = (PthreadMutex *)lsmMallocZero(pEnv, sizeof(PthreadMutex));
if( !pMutex ) return LSM_NOMEM_BKPT;
pMutex->pEnv = pEnv;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&pMutex->mutex, &attr);
pthread_mutexattr_destroy(&attr);
*ppNew = (lsm_mutex *)pMutex;
return LSM_OK;
}
static void lsmPosixOsMutexDel(lsm_mutex *p){
PthreadMutex *pMutex = (PthreadMutex *)p;
pthread_mutex_destroy(&pMutex->mutex);
lsmFree(pMutex->pEnv, pMutex);
}
static void lsmPosixOsMutexEnter(lsm_mutex *p){
PthreadMutex *pMutex = (PthreadMutex *)p;
pthread_mutex_lock(&pMutex->mutex);
#ifdef LSM_DEBUG
assert( !pthread_equal(pMutex->owner, pthread_self()) );
pMutex->owner = pthread_self();
assert( pthread_equal(pMutex->owner, pthread_self()) );
#endif
}
static int lsmPosixOsMutexTry(lsm_mutex *p){
int ret;
PthreadMutex *pMutex = (PthreadMutex *)p;
ret = pthread_mutex_trylock(&pMutex->mutex);
#ifdef LSM_DEBUG
if( ret==0 ){
assert( !pthread_equal(pMutex->owner, pthread_self()) );
pMutex->owner = pthread_self();
assert( pthread_equal(pMutex->owner, pthread_self()) );
}
#endif
return ret;
}
static void lsmPosixOsMutexLeave(lsm_mutex *p){
PthreadMutex *pMutex = (PthreadMutex *)p;
#ifdef LSM_DEBUG
assert( pthread_equal(pMutex->owner, pthread_self()) );
pMutex->owner = 0;
assert( !pthread_equal(pMutex->owner, pthread_self()) );
#endif
pthread_mutex_unlock(&pMutex->mutex);
}
#ifdef LSM_DEBUG
static int lsmPosixOsMutexHeld(lsm_mutex *p){
PthreadMutex *pMutex = (PthreadMutex *)p;
return pMutex ? pthread_equal(pMutex->owner, pthread_self()) : 1;
}
static int lsmPosixOsMutexNotHeld(lsm_mutex *p){
PthreadMutex *pMutex = (PthreadMutex *)p;
return pMutex ? !pthread_equal(pMutex->owner, pthread_self()) : 1;
}
#endif
/*
** End of pthreads mutex implementation.
*************************************************************************/
#else
/*************************************************************************
** Noop mutex implementation
*/
typedef struct NoopMutex NoopMutex;
struct NoopMutex {
lsm_env *pEnv; /* Environment handle (for xFree()) */
int bHeld; /* True if mutex is held */
int bStatic; /* True for a static mutex */
};
static NoopMutex aStaticNoopMutex[2] = {
{0, 0, 1},
{0, 0, 1},
};
static int lsmPosixOsMutexStatic(
lsm_env *pEnv,
int iMutex,
lsm_mutex **ppStatic
){
assert( iMutex>=1 && iMutex<=(int)array_size(aStaticNoopMutex) );
*ppStatic = (lsm_mutex *)&aStaticNoopMutex[iMutex-1];
return LSM_OK;
}
static int lsmPosixOsMutexNew(lsm_env *pEnv, lsm_mutex **ppNew){
NoopMutex *p;
p = (NoopMutex *)lsmMallocZero(pEnv, sizeof(NoopMutex));
if( p ) p->pEnv = pEnv;
*ppNew = (lsm_mutex *)p;
return (p ? LSM_OK : LSM_NOMEM_BKPT);
}
static void lsmPosixOsMutexDel(lsm_mutex *pMutex) {
NoopMutex *p = (NoopMutex *)pMutex;
assert( p->bStatic==0 && p->pEnv );
lsmFree(p->pEnv, p);
}
static void lsmPosixOsMutexEnter(lsm_mutex *pMutex){
NoopMutex *p = (NoopMutex *)pMutex;
assert( p->bHeld==0 );
p->bHeld = 1;
}
static int lsmPosixOsMutexTry(lsm_mutex *pMutex){
NoopMutex *p = (NoopMutex *)pMutex;
assert( p->bHeld==0 );
p->bHeld = 1;
return 0;
}
static void lsmPosixOsMutexLeave(lsm_mutex *pMutex){
NoopMutex *p = (NoopMutex *)pMutex;
assert( p->bHeld==1 );
p->bHeld = 0;
}
#ifdef LSM_DEBUG
static int lsmPosixOsMutexHeld(lsm_mutex *pMutex){
NoopMutex *p = (NoopMutex *)pMutex;
return p ? p->bHeld : 1;
}
static int lsmPosixOsMutexNotHeld(lsm_mutex *pMutex){
NoopMutex *p = (NoopMutex *)pMutex;
return p ? !p->bHeld : 1;
}
#endif
/***************************************************************************/
#endif /* else LSM_MUTEX_NONE */
/* Without LSM_DEBUG, the MutexHeld tests are never called */
#ifndef LSM_DEBUG
# define lsmPosixOsMutexHeld 0
# define lsmPosixOsMutexNotHeld 0
#endif
lsm_env *lsm_default_env(void){
static lsm_env posix_env = {
sizeof(lsm_env), /* nByte */
1, /* iVersion */
/***** file i/o ******************/
0, /* pVfsCtx */
lsmPosixOsFullpath, /* xFullpath */
lsmPosixOsOpen, /* xOpen */
lsmPosixOsRead, /* xRead */
lsmPosixOsWrite, /* xWrite */
lsmPosixOsTruncate, /* xTruncate */
lsmPosixOsSync, /* xSync */
lsmPosixOsSectorSize, /* xSectorSize */
lsmPosixOsRemap, /* xRemap */
lsmPosixOsFileid, /* xFileid */
lsmPosixOsClose, /* xClose */
lsmPosixOsUnlink, /* xUnlink */
lsmPosixOsLock, /* xLock */
lsmPosixOsTestLock, /* xTestLock */
lsmPosixOsShmMap, /* xShmMap */
lsmPosixOsShmBarrier, /* xShmBarrier */
lsmPosixOsShmUnmap, /* xShmUnmap */
/***** memory allocation *********/
0, /* pMemCtx */
lsmPosixOsMalloc, /* xMalloc */
lsmPosixOsRealloc, /* xRealloc */
lsmPosixOsFree, /* xFree */
lsmPosixOsMSize, /* xSize */
/***** mutexes *********************/
0, /* pMutexCtx */
lsmPosixOsMutexStatic, /* xMutexStatic */
lsmPosixOsMutexNew, /* xMutexNew */
lsmPosixOsMutexDel, /* xMutexDel */
lsmPosixOsMutexEnter, /* xMutexEnter */
lsmPosixOsMutexTry, /* xMutexTry */
lsmPosixOsMutexLeave, /* xMutexLeave */
lsmPosixOsMutexHeld, /* xMutexHeld */
lsmPosixOsMutexNotHeld, /* xMutexNotHeld */
/***** other *********************/
lsmPosixOsSleep, /* xSleep */
};
return &posix_env;
}
#endif