4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
13 ** Unix-specific run-time environment implementation for LSM.
18 #if defined(__GNUC__) || defined(__TINYC__)
19 /* workaround for ftruncate() visibility on gcc. */
20 # ifndef _XOPEN_SOURCE
21 # define _XOPEN_SOURCE 500
26 #include <sys/types.h>
44 /* There is no fdatasync() call on Android */
46 # define fdatasync(x) fsync(x)
50 ** An open file is an instance of the following object
52 typedef struct PosixFile PosixFile
;
54 lsm_env
*pEnv
; /* The run-time environment */
55 const char *zName
; /* Full path to file */
56 int fd
; /* The open file descriptor */
57 int shmfd
; /* Shared memory file-descriptor */
58 void *pMap
; /* Pointer to mapping of file fd */
59 off_t nMap
; /* Size of mapping at pMap in bytes */
60 int nShm
; /* Number of entries in array apShm[] */
61 void **apShm
; /* Array of 32K shared memory segments */
64 static char *posixShmFile(PosixFile
*p
){
66 int nName
= strlen(p
->zName
);
67 zShm
= (char *)lsmMalloc(p
->pEnv
, nName
+4+1);
69 memcpy(zShm
, p
->zName
, nName
);
70 memcpy(&zShm
[nName
], "-shm", 5);
75 static int lsmPosixOsOpen(
84 p
= lsm_malloc(pEnv
, sizeof(PosixFile
));
88 int bReadonly
= (flags
& LSM_OPEN_READONLY
);
89 int oflags
= (bReadonly
? O_RDONLY
: (O_RDWR
|O_CREAT
));
90 memset(p
, 0, sizeof(PosixFile
));
93 p
->fd
= open(zFile
, oflags
, 0644);
98 rc
= lsmErrorBkpt(LSM_IOERR_NOENT
);
105 *ppFile
= (lsm_file
*)p
;
109 static int lsmPosixOsWrite(
110 lsm_file
*pFile
, /* File to write to */
111 lsm_i64 iOff
, /* Offset to write to */
112 void *pData
, /* Write data from this buffer */
113 int nData
/* Bytes of data to write */
116 PosixFile
*p
= (PosixFile
*)pFile
;
119 offset
= lseek(p
->fd
, (off_t
)iOff
, SEEK_SET
);
123 ssize_t prc
= write(p
->fd
, pData
, (size_t)nData
);
124 if( prc
<0 ) rc
= LSM_IOERR_BKPT
;
130 static int lsmPosixOsTruncate(
131 lsm_file
*pFile
, /* File to write to */
132 lsm_i64 nSize
/* Size to truncate file to */
134 PosixFile
*p
= (PosixFile
*)pFile
;
135 int rc
= LSM_OK
; /* Return code */
136 int prc
; /* Posix Return Code */
137 struct stat sStat
; /* Result of fstat() invocation */
139 prc
= fstat(p
->fd
, &sStat
);
140 if( prc
==0 && sStat
.st_size
>nSize
){
141 prc
= ftruncate(p
->fd
, (off_t
)nSize
);
143 if( prc
<0 ) rc
= LSM_IOERR_BKPT
;
148 static int lsmPosixOsRead(
149 lsm_file
*pFile
, /* File to read from */
150 lsm_i64 iOff
, /* Offset to read from */
151 void *pData
, /* Read data into this buffer */
152 int nData
/* Bytes of data to read */
155 PosixFile
*p
= (PosixFile
*)pFile
;
158 offset
= lseek(p
->fd
, (off_t
)iOff
, SEEK_SET
);
162 ssize_t prc
= read(p
->fd
, pData
, (size_t)nData
);
165 }else if( prc
<nData
){
166 memset(&((u8
*)pData
)[prc
], 0, nData
- prc
);
174 static int lsmPosixOsSync(lsm_file
*pFile
){
178 PosixFile
*p
= (PosixFile
*)pFile
;
182 prc
= msync(p
->pMap
, p
->nMap
, MS_SYNC
);
184 if( prc
==0 ) prc
= fdatasync(p
->fd
);
185 if( prc
<0 ) rc
= LSM_IOERR_BKPT
;
193 static int lsmPosixOsSectorSize(lsm_file
*pFile
){
197 static int lsmPosixOsRemap(
205 PosixFile
*p
= (PosixFile
*)pFile
;
208 /* If the file is between 0 and 2MB in size, extend it in chunks of 256K.
209 ** Thereafter, in chunks of 1MB at a time. */
210 const int aIncrSz
[] = {256*1024, 1024*1024};
211 int nIncrSz
= aIncrSz
[iMin
>(2*1024*1024)];
214 munmap(p
->pMap
, p
->nMap
);
215 *ppOut
= p
->pMap
= 0;
216 *pnOut
= p
->nMap
= 0;
220 memset(&buf
, 0, sizeof(buf
));
221 prc
= fstat(p
->fd
, &buf
);
222 if( prc
!=0 ) return LSM_IOERR_BKPT
;
225 iSz
= ((iMin
+ nIncrSz
-1) / nIncrSz
) * nIncrSz
;
226 prc
= ftruncate(p
->fd
, iSz
);
227 if( prc
!=0 ) return LSM_IOERR_BKPT
;
230 p
->pMap
= mmap(0, iSz
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, p
->fd
, 0);
239 static int lsmPosixOsFullpath(
252 zTmp
= lsmMalloc(pEnv
, nTmp
);
254 z
= getcwd(zTmp
, nTmp
);
255 if( z
|| errno
!=ERANGE
) break;
257 zTmp
= lsmReallocOrFree(pEnv
, zTmp
, nTmp
);
259 if( zTmp
==0 ) return LSM_NOMEM_BKPT
;
260 if( z
==0 ) return LSM_IOERR_BKPT
;
264 nReq
= nTmp
+ 1 + strlen(zName
) + 1;
266 memcpy(zOut
, zTmp
, nTmp
);
268 memcpy(&zOut
[nTmp
+1], zName
, strlen(zName
)+1);
272 nReq
= strlen(zName
)+1;
274 memcpy(zOut
, zName
, strlen(zName
)+1);
282 static int lsmPosixOsFileid(
290 PosixFile
*p
= (PosixFile
*)pFile
;
294 nReq
= (sizeof(buf
.st_dev
) + sizeof(buf
.st_ino
));
296 if( nReq
>nBuf
) return LSM_OK
;
298 memset(&buf
, 0, sizeof(buf
));
299 prc
= fstat(p
->fd
, &buf
);
300 if( prc
!=0 ) return LSM_IOERR_BKPT
;
302 memcpy(pBuf
, &buf
.st_dev
, sizeof(buf
.st_dev
));
303 memcpy(&(((u8
*)pBuf
)[sizeof(buf
.st_dev
)]), &buf
.st_ino
, sizeof(buf
.st_ino
));
307 static int lsmPosixOsUnlink(lsm_env
*pEnv
, const char *zFile
){
308 int prc
= unlink(zFile
);
309 return prc
? LSM_IOERR_BKPT
: LSM_OK
;
312 static int lsmPosixOsLock(lsm_file
*pFile
, int iLock
, int eType
){
314 PosixFile
*p
= (PosixFile
*)pFile
;
315 static const short aType
[3] = { F_UNLCK
, F_RDLCK
, F_WRLCK
};
318 assert( aType
[LSM_LOCK_UNLOCK
]==F_UNLCK
);
319 assert( aType
[LSM_LOCK_SHARED
]==F_RDLCK
);
320 assert( aType
[LSM_LOCK_EXCL
]==F_WRLCK
);
321 assert( eType
>=0 && eType
<array_size(aType
) );
322 assert( iLock
>0 && iLock
<=32 );
324 memset(&lock
, 0, sizeof(lock
));
325 lock
.l_whence
= SEEK_SET
;
327 lock
.l_type
= aType
[eType
];
328 lock
.l_start
= (4096-iLock
);
330 if( fcntl(p
->fd
, F_SETLK
, &lock
) ){
332 if( e
==EACCES
|| e
==EAGAIN
){
342 static int lsmPosixOsTestLock(lsm_file
*pFile
, int iLock
, int nLock
, int eType
){
344 PosixFile
*p
= (PosixFile
*)pFile
;
345 static const short aType
[3] = { 0, F_RDLCK
, F_WRLCK
};
348 assert( eType
==LSM_LOCK_SHARED
|| eType
==LSM_LOCK_EXCL
);
349 assert( aType
[LSM_LOCK_SHARED
]==F_RDLCK
);
350 assert( aType
[LSM_LOCK_EXCL
]==F_WRLCK
);
351 assert( eType
>=0 && eType
<array_size(aType
) );
352 assert( iLock
>0 && iLock
<=32 );
354 memset(&lock
, 0, sizeof(lock
));
355 lock
.l_whence
= SEEK_SET
;
357 lock
.l_type
= aType
[eType
];
358 lock
.l_start
= (4096-iLock
-nLock
+1);
360 if( fcntl(p
->fd
, F_GETLK
, &lock
) ){
362 }else if( lock
.l_type
!=F_UNLCK
){
369 static int lsmPosixOsShmMap(lsm_file
*pFile
, int iChunk
, int sz
, void **ppShm
){
370 PosixFile
*p
= (PosixFile
*)pFile
;
373 assert( sz
==LSM_SHM_CHUNK_SIZE
);
374 if( iChunk
>=p
->nShm
){
378 off_t nReq
= nNew
* LSM_SHM_CHUNK_SIZE
;
381 /* If the shared-memory file has not been opened, open it now. */
383 char *zShm
= posixShmFile(p
);
384 if( !zShm
) return LSM_NOMEM_BKPT
;
385 p
->shmfd
= open(zShm
, O_RDWR
|O_CREAT
, 0644);
386 lsmFree(p
->pEnv
, zShm
);
388 return LSM_IOERR_BKPT
;
392 /* If the shared-memory file is not large enough to contain the
393 ** requested chunk, cause it to grow. */
394 if( fstat(p
->shmfd
, &sStat
) ){
395 return LSM_IOERR_BKPT
;
397 if( sStat
.st_size
<nReq
){
398 if( ftruncate(p
->shmfd
, nReq
) ){
399 return LSM_IOERR_BKPT
;
403 apNew
= (void **)lsmRealloc(p
->pEnv
, p
->apShm
, sizeof(void *) * nNew
);
404 if( !apNew
) return LSM_NOMEM_BKPT
;
405 for(i
=p
->nShm
; i
<nNew
; i
++){
412 if( p
->apShm
[iChunk
]==0 ){
413 p
->apShm
[iChunk
] = mmap(0, LSM_SHM_CHUNK_SIZE
,
414 PROT_READ
|PROT_WRITE
, MAP_SHARED
, p
->shmfd
, iChunk
*LSM_SHM_CHUNK_SIZE
416 if( p
->apShm
[iChunk
]==0 ) return LSM_IOERR_BKPT
;
419 *ppShm
= p
->apShm
[iChunk
];
423 static void lsmPosixOsShmBarrier(void){
426 static int lsmPosixOsShmUnmap(lsm_file
*pFile
, int bDelete
){
427 PosixFile
*p
= (PosixFile
*)pFile
;
430 for(i
=0; i
<p
->nShm
; i
++){
432 munmap(p
->apShm
[i
], LSM_SHM_CHUNK_SIZE
);
439 char *zShm
= posixShmFile(p
);
440 if( zShm
) unlink(zShm
);
441 lsmFree(p
->pEnv
, zShm
);
448 static int lsmPosixOsClose(lsm_file
*pFile
){
449 PosixFile
*p
= (PosixFile
*)pFile
;
450 lsmPosixOsShmUnmap(pFile
, 0);
451 if( p
->pMap
) munmap(p
->pMap
, p
->nMap
);
453 lsm_free(p
->pEnv
, p
->apShm
);
454 lsm_free(p
->pEnv
, p
);
458 static int lsmPosixOsSleep(lsm_env
*pEnv
, int us
){
460 /* Apparently on Android usleep() returns void */
461 if( usleep(us
) ) return LSM_IOERR
;
467 /****************************************************************************
468 ** Memory allocation routines.
470 #define BLOCK_HDR_SIZE ROUND8( sizeof(size_t) )
472 static void *lsmPosixOsMalloc(lsm_env
*pEnv
, size_t N
){
475 m
= (unsigned char *)malloc(N
);
477 return m
+ BLOCK_HDR_SIZE
;
480 static void lsmPosixOsFree(lsm_env
*pEnv
, void *p
){
482 free( ((unsigned char *)p
) - BLOCK_HDR_SIZE
);
486 static void *lsmPosixOsRealloc(lsm_env
*pEnv
, void *p
, size_t N
){
487 unsigned char * m
= (unsigned char *)p
;
489 lsmPosixOsFree( pEnv
, p
);
492 return lsmPosixOsMalloc(pEnv
, N
);
496 #if 0 /* arguable: don't shrink */
497 size_t * sz
= (size_t*)m
;
498 if(*sz
>= (size_t)N
){
502 re
= realloc( m
, N
+ BLOCK_HDR_SIZE
);
504 m
= (unsigned char *)re
;
506 return m
+ BLOCK_HDR_SIZE
;
513 static size_t lsmPosixOsMSize(lsm_env
*pEnv
, void *p
){
514 unsigned char * m
= (unsigned char *)p
;
515 return *((size_t*)(m
-BLOCK_HDR_SIZE
));
517 #undef BLOCK_HDR_SIZE
520 #ifdef LSM_MUTEX_PTHREADS
521 /*************************************************************************
522 ** Mutex methods for pthreads based systems. If LSM_MUTEX_PTHREADS is
523 ** missing then a no-op implementation of mutexes found in lsm_mutex.c
524 ** will be used instead.
528 typedef struct PthreadMutex PthreadMutex
;
529 struct PthreadMutex
{
531 pthread_mutex_t mutex
;
538 # define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER, 0 }
540 # define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER }
543 static int lsmPosixOsMutexStatic(
548 static PthreadMutex sMutex
[2] = {
549 LSM_PTHREAD_STATIC_MUTEX
,
550 LSM_PTHREAD_STATIC_MUTEX
553 assert( iMutex
==LSM_MUTEX_GLOBAL
|| iMutex
==LSM_MUTEX_HEAP
);
554 assert( LSM_MUTEX_GLOBAL
==1 && LSM_MUTEX_HEAP
==2 );
556 *ppStatic
= (lsm_mutex
*)&sMutex
[iMutex
-1];
560 static int lsmPosixOsMutexNew(lsm_env
*pEnv
, lsm_mutex
**ppNew
){
561 PthreadMutex
*pMutex
; /* Pointer to new mutex */
562 pthread_mutexattr_t attr
; /* Attributes object */
564 pMutex
= (PthreadMutex
*)lsmMallocZero(pEnv
, sizeof(PthreadMutex
));
565 if( !pMutex
) return LSM_NOMEM_BKPT
;
568 pthread_mutexattr_init(&attr
);
569 pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
570 pthread_mutex_init(&pMutex
->mutex
, &attr
);
571 pthread_mutexattr_destroy(&attr
);
573 *ppNew
= (lsm_mutex
*)pMutex
;
577 static void lsmPosixOsMutexDel(lsm_mutex
*p
){
578 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
579 pthread_mutex_destroy(&pMutex
->mutex
);
580 lsmFree(pMutex
->pEnv
, pMutex
);
583 static void lsmPosixOsMutexEnter(lsm_mutex
*p
){
584 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
585 pthread_mutex_lock(&pMutex
->mutex
);
588 assert( !pthread_equal(pMutex
->owner
, pthread_self()) );
589 pMutex
->owner
= pthread_self();
590 assert( pthread_equal(pMutex
->owner
, pthread_self()) );
594 static int lsmPosixOsMutexTry(lsm_mutex
*p
){
596 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
597 ret
= pthread_mutex_trylock(&pMutex
->mutex
);
600 assert( !pthread_equal(pMutex
->owner
, pthread_self()) );
601 pMutex
->owner
= pthread_self();
602 assert( pthread_equal(pMutex
->owner
, pthread_self()) );
608 static void lsmPosixOsMutexLeave(lsm_mutex
*p
){
609 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
611 assert( pthread_equal(pMutex
->owner
, pthread_self()) );
613 assert( !pthread_equal(pMutex
->owner
, pthread_self()) );
615 pthread_mutex_unlock(&pMutex
->mutex
);
619 static int lsmPosixOsMutexHeld(lsm_mutex
*p
){
620 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
621 return pMutex
? pthread_equal(pMutex
->owner
, pthread_self()) : 1;
623 static int lsmPosixOsMutexNotHeld(lsm_mutex
*p
){
624 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
625 return pMutex
? !pthread_equal(pMutex
->owner
, pthread_self()) : 1;
629 ** End of pthreads mutex implementation.
630 *************************************************************************/
632 /*************************************************************************
633 ** Noop mutex implementation
635 typedef struct NoopMutex NoopMutex
;
637 lsm_env
*pEnv
; /* Environment handle (for xFree()) */
638 int bHeld
; /* True if mutex is held */
639 int bStatic
; /* True for a static mutex */
641 static NoopMutex aStaticNoopMutex
[2] = {
646 static int lsmPosixOsMutexStatic(
651 assert( iMutex
>=1 && iMutex
<=(int)array_size(aStaticNoopMutex
) );
652 *ppStatic
= (lsm_mutex
*)&aStaticNoopMutex
[iMutex
-1];
655 static int lsmPosixOsMutexNew(lsm_env
*pEnv
, lsm_mutex
**ppNew
){
657 p
= (NoopMutex
*)lsmMallocZero(pEnv
, sizeof(NoopMutex
));
658 if( p
) p
->pEnv
= pEnv
;
659 *ppNew
= (lsm_mutex
*)p
;
660 return (p
? LSM_OK
: LSM_NOMEM_BKPT
);
662 static void lsmPosixOsMutexDel(lsm_mutex
*pMutex
) {
663 NoopMutex
*p
= (NoopMutex
*)pMutex
;
664 assert( p
->bStatic
==0 && p
->pEnv
);
667 static void lsmPosixOsMutexEnter(lsm_mutex
*pMutex
){
668 NoopMutex
*p
= (NoopMutex
*)pMutex
;
669 assert( p
->bHeld
==0 );
672 static int lsmPosixOsMutexTry(lsm_mutex
*pMutex
){
673 NoopMutex
*p
= (NoopMutex
*)pMutex
;
674 assert( p
->bHeld
==0 );
678 static void lsmPosixOsMutexLeave(lsm_mutex
*pMutex
){
679 NoopMutex
*p
= (NoopMutex
*)pMutex
;
680 assert( p
->bHeld
==1 );
684 static int lsmPosixOsMutexHeld(lsm_mutex
*pMutex
){
685 NoopMutex
*p
= (NoopMutex
*)pMutex
;
686 return p
? p
->bHeld
: 1;
688 static int lsmPosixOsMutexNotHeld(lsm_mutex
*pMutex
){
689 NoopMutex
*p
= (NoopMutex
*)pMutex
;
690 return p
? !p
->bHeld
: 1;
693 /***************************************************************************/
694 #endif /* else LSM_MUTEX_NONE */
696 /* Without LSM_DEBUG, the MutexHeld tests are never called */
698 # define lsmPosixOsMutexHeld 0
699 # define lsmPosixOsMutexNotHeld 0
702 lsm_env
*lsm_default_env(void){
703 static lsm_env posix_env
= {
704 sizeof(lsm_env
), /* nByte */
706 /***** file i/o ******************/
708 lsmPosixOsFullpath
, /* xFullpath */
709 lsmPosixOsOpen
, /* xOpen */
710 lsmPosixOsRead
, /* xRead */
711 lsmPosixOsWrite
, /* xWrite */
712 lsmPosixOsTruncate
, /* xTruncate */
713 lsmPosixOsSync
, /* xSync */
714 lsmPosixOsSectorSize
, /* xSectorSize */
715 lsmPosixOsRemap
, /* xRemap */
716 lsmPosixOsFileid
, /* xFileid */
717 lsmPosixOsClose
, /* xClose */
718 lsmPosixOsUnlink
, /* xUnlink */
719 lsmPosixOsLock
, /* xLock */
720 lsmPosixOsTestLock
, /* xTestLock */
721 lsmPosixOsShmMap
, /* xShmMap */
722 lsmPosixOsShmBarrier
, /* xShmBarrier */
723 lsmPosixOsShmUnmap
, /* xShmUnmap */
724 /***** memory allocation *********/
726 lsmPosixOsMalloc
, /* xMalloc */
727 lsmPosixOsRealloc
, /* xRealloc */
728 lsmPosixOsFree
, /* xFree */
729 lsmPosixOsMSize
, /* xSize */
730 /***** mutexes *********************/
732 lsmPosixOsMutexStatic
, /* xMutexStatic */
733 lsmPosixOsMutexNew
, /* xMutexNew */
734 lsmPosixOsMutexDel
, /* xMutexDel */
735 lsmPosixOsMutexEnter
, /* xMutexEnter */
736 lsmPosixOsMutexTry
, /* xMutexTry */
737 lsmPosixOsMutexLeave
, /* xMutexLeave */
738 lsmPosixOsMutexHeld
, /* xMutexHeld */
739 lsmPosixOsMutexNotHeld
, /* xMutexNotHeld */
740 /***** other *********************/
741 lsmPosixOsSleep
, /* xSleep */