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);
231 if( p
->pMap
==MAP_FAILED
){
233 return LSM_IOERR_BKPT
;
243 static int lsmPosixOsFullpath(
256 zTmp
= lsmMalloc(pEnv
, nTmp
);
258 z
= getcwd(zTmp
, nTmp
);
259 if( z
|| errno
!=ERANGE
) break;
261 zTmp
= lsmReallocOrFree(pEnv
, zTmp
, nTmp
);
263 if( zTmp
==0 ) return LSM_NOMEM_BKPT
;
264 if( z
==0 ) return LSM_IOERR_BKPT
;
268 nReq
= nTmp
+ 1 + strlen(zName
) + 1;
270 memcpy(zOut
, zTmp
, nTmp
);
272 memcpy(&zOut
[nTmp
+1], zName
, strlen(zName
)+1);
276 nReq
= strlen(zName
)+1;
278 memcpy(zOut
, zName
, strlen(zName
)+1);
286 static int lsmPosixOsFileid(
294 PosixFile
*p
= (PosixFile
*)pFile
;
298 nReq
= (sizeof(buf
.st_dev
) + sizeof(buf
.st_ino
));
300 if( nReq
>nBuf
) return LSM_OK
;
302 memset(&buf
, 0, sizeof(buf
));
303 prc
= fstat(p
->fd
, &buf
);
304 if( prc
!=0 ) return LSM_IOERR_BKPT
;
306 memcpy(pBuf
, &buf
.st_dev
, sizeof(buf
.st_dev
));
307 memcpy(&(((u8
*)pBuf
)[sizeof(buf
.st_dev
)]), &buf
.st_ino
, sizeof(buf
.st_ino
));
311 static int lsmPosixOsUnlink(lsm_env
*pEnv
, const char *zFile
){
312 int prc
= unlink(zFile
);
313 return prc
? LSM_IOERR_BKPT
: LSM_OK
;
316 static int lsmPosixOsLock(lsm_file
*pFile
, int iLock
, int eType
){
318 PosixFile
*p
= (PosixFile
*)pFile
;
319 static const short aType
[3] = { F_UNLCK
, F_RDLCK
, F_WRLCK
};
322 assert( aType
[LSM_LOCK_UNLOCK
]==F_UNLCK
);
323 assert( aType
[LSM_LOCK_SHARED
]==F_RDLCK
);
324 assert( aType
[LSM_LOCK_EXCL
]==F_WRLCK
);
325 assert( eType
>=0 && eType
<array_size(aType
) );
326 assert( iLock
>0 && iLock
<=32 );
328 memset(&lock
, 0, sizeof(lock
));
329 lock
.l_whence
= SEEK_SET
;
331 lock
.l_type
= aType
[eType
];
332 lock
.l_start
= (4096-iLock
);
334 if( fcntl(p
->fd
, F_SETLK
, &lock
) ){
336 if( e
==EACCES
|| e
==EAGAIN
){
346 static int lsmPosixOsTestLock(lsm_file
*pFile
, int iLock
, int nLock
, int eType
){
348 PosixFile
*p
= (PosixFile
*)pFile
;
349 static const short aType
[3] = { 0, F_RDLCK
, F_WRLCK
};
352 assert( eType
==LSM_LOCK_SHARED
|| eType
==LSM_LOCK_EXCL
);
353 assert( aType
[LSM_LOCK_SHARED
]==F_RDLCK
);
354 assert( aType
[LSM_LOCK_EXCL
]==F_WRLCK
);
355 assert( eType
>=0 && eType
<array_size(aType
) );
356 assert( iLock
>0 && iLock
<=32 );
358 memset(&lock
, 0, sizeof(lock
));
359 lock
.l_whence
= SEEK_SET
;
361 lock
.l_type
= aType
[eType
];
362 lock
.l_start
= (4096-iLock
-nLock
+1);
364 if( fcntl(p
->fd
, F_GETLK
, &lock
) ){
366 }else if( lock
.l_type
!=F_UNLCK
){
373 static int lsmPosixOsShmMap(lsm_file
*pFile
, int iChunk
, int sz
, void **ppShm
){
374 PosixFile
*p
= (PosixFile
*)pFile
;
377 assert( sz
==LSM_SHM_CHUNK_SIZE
);
378 if( iChunk
>=p
->nShm
){
382 off_t nReq
= nNew
* LSM_SHM_CHUNK_SIZE
;
385 /* If the shared-memory file has not been opened, open it now. */
387 char *zShm
= posixShmFile(p
);
388 if( !zShm
) return LSM_NOMEM_BKPT
;
389 p
->shmfd
= open(zShm
, O_RDWR
|O_CREAT
, 0644);
390 lsmFree(p
->pEnv
, zShm
);
392 return LSM_IOERR_BKPT
;
396 /* If the shared-memory file is not large enough to contain the
397 ** requested chunk, cause it to grow. */
398 if( fstat(p
->shmfd
, &sStat
) ){
399 return LSM_IOERR_BKPT
;
401 if( sStat
.st_size
<nReq
){
402 if( ftruncate(p
->shmfd
, nReq
) ){
403 return LSM_IOERR_BKPT
;
407 apNew
= (void **)lsmRealloc(p
->pEnv
, p
->apShm
, sizeof(void *) * nNew
);
408 if( !apNew
) return LSM_NOMEM_BKPT
;
409 for(i
=p
->nShm
; i
<nNew
; i
++){
416 if( p
->apShm
[iChunk
]==0 ){
417 p
->apShm
[iChunk
] = mmap(0, LSM_SHM_CHUNK_SIZE
,
418 PROT_READ
|PROT_WRITE
, MAP_SHARED
, p
->shmfd
, iChunk
*LSM_SHM_CHUNK_SIZE
420 if( p
->apShm
[iChunk
]==MAP_FAILED
){
421 p
->apShm
[iChunk
] = 0;
422 return LSM_IOERR_BKPT
;
426 *ppShm
= p
->apShm
[iChunk
];
430 static void lsmPosixOsShmBarrier(void){
433 static int lsmPosixOsShmUnmap(lsm_file
*pFile
, int bDelete
){
434 PosixFile
*p
= (PosixFile
*)pFile
;
437 for(i
=0; i
<p
->nShm
; i
++){
439 munmap(p
->apShm
[i
], LSM_SHM_CHUNK_SIZE
);
446 char *zShm
= posixShmFile(p
);
447 if( zShm
) unlink(zShm
);
448 lsmFree(p
->pEnv
, zShm
);
455 static int lsmPosixOsClose(lsm_file
*pFile
){
456 PosixFile
*p
= (PosixFile
*)pFile
;
457 lsmPosixOsShmUnmap(pFile
, 0);
458 if( p
->pMap
) munmap(p
->pMap
, p
->nMap
);
460 lsm_free(p
->pEnv
, p
->apShm
);
461 lsm_free(p
->pEnv
, p
);
465 static int lsmPosixOsSleep(lsm_env
*pEnv
, int us
){
467 /* Apparently on Android usleep() returns void */
468 if( usleep(us
) ) return LSM_IOERR
;
474 /****************************************************************************
475 ** Memory allocation routines.
477 #define BLOCK_HDR_SIZE ROUND8( sizeof(size_t) )
479 static void *lsmPosixOsMalloc(lsm_env
*pEnv
, size_t N
){
482 m
= (unsigned char *)malloc(N
);
484 return m
+ BLOCK_HDR_SIZE
;
487 static void lsmPosixOsFree(lsm_env
*pEnv
, void *p
){
489 free( ((unsigned char *)p
) - BLOCK_HDR_SIZE
);
493 static void *lsmPosixOsRealloc(lsm_env
*pEnv
, void *p
, size_t N
){
494 unsigned char * m
= (unsigned char *)p
;
496 lsmPosixOsFree( pEnv
, p
);
499 return lsmPosixOsMalloc(pEnv
, N
);
503 #if 0 /* arguable: don't shrink */
504 size_t * sz
= (size_t*)m
;
505 if(*sz
>= (size_t)N
){
509 re
= realloc( m
, N
+ BLOCK_HDR_SIZE
);
511 m
= (unsigned char *)re
;
513 return m
+ BLOCK_HDR_SIZE
;
520 static size_t lsmPosixOsMSize(lsm_env
*pEnv
, void *p
){
521 unsigned char * m
= (unsigned char *)p
;
522 return *((size_t*)(m
-BLOCK_HDR_SIZE
));
524 #undef BLOCK_HDR_SIZE
527 #ifdef LSM_MUTEX_PTHREADS
528 /*************************************************************************
529 ** Mutex methods for pthreads based systems. If LSM_MUTEX_PTHREADS is
530 ** missing then a no-op implementation of mutexes found in lsm_mutex.c
531 ** will be used instead.
535 typedef struct PthreadMutex PthreadMutex
;
536 struct PthreadMutex
{
538 pthread_mutex_t mutex
;
545 # define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER, 0 }
547 # define LSM_PTHREAD_STATIC_MUTEX { 0, PTHREAD_MUTEX_INITIALIZER }
550 static int lsmPosixOsMutexStatic(
555 static PthreadMutex sMutex
[2] = {
556 LSM_PTHREAD_STATIC_MUTEX
,
557 LSM_PTHREAD_STATIC_MUTEX
560 assert( iMutex
==LSM_MUTEX_GLOBAL
|| iMutex
==LSM_MUTEX_HEAP
);
561 assert( LSM_MUTEX_GLOBAL
==1 && LSM_MUTEX_HEAP
==2 );
563 *ppStatic
= (lsm_mutex
*)&sMutex
[iMutex
-1];
567 static int lsmPosixOsMutexNew(lsm_env
*pEnv
, lsm_mutex
**ppNew
){
568 PthreadMutex
*pMutex
; /* Pointer to new mutex */
569 pthread_mutexattr_t attr
; /* Attributes object */
571 pMutex
= (PthreadMutex
*)lsmMallocZero(pEnv
, sizeof(PthreadMutex
));
572 if( !pMutex
) return LSM_NOMEM_BKPT
;
575 pthread_mutexattr_init(&attr
);
576 pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
577 pthread_mutex_init(&pMutex
->mutex
, &attr
);
578 pthread_mutexattr_destroy(&attr
);
580 *ppNew
= (lsm_mutex
*)pMutex
;
584 static void lsmPosixOsMutexDel(lsm_mutex
*p
){
585 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
586 pthread_mutex_destroy(&pMutex
->mutex
);
587 lsmFree(pMutex
->pEnv
, pMutex
);
590 static void lsmPosixOsMutexEnter(lsm_mutex
*p
){
591 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
592 pthread_mutex_lock(&pMutex
->mutex
);
595 assert( !pthread_equal(pMutex
->owner
, pthread_self()) );
596 pMutex
->owner
= pthread_self();
597 assert( pthread_equal(pMutex
->owner
, pthread_self()) );
601 static int lsmPosixOsMutexTry(lsm_mutex
*p
){
603 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
604 ret
= pthread_mutex_trylock(&pMutex
->mutex
);
607 assert( !pthread_equal(pMutex
->owner
, pthread_self()) );
608 pMutex
->owner
= pthread_self();
609 assert( pthread_equal(pMutex
->owner
, pthread_self()) );
615 static void lsmPosixOsMutexLeave(lsm_mutex
*p
){
616 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
618 assert( pthread_equal(pMutex
->owner
, pthread_self()) );
620 assert( !pthread_equal(pMutex
->owner
, pthread_self()) );
622 pthread_mutex_unlock(&pMutex
->mutex
);
626 static int lsmPosixOsMutexHeld(lsm_mutex
*p
){
627 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
628 return pMutex
? pthread_equal(pMutex
->owner
, pthread_self()) : 1;
630 static int lsmPosixOsMutexNotHeld(lsm_mutex
*p
){
631 PthreadMutex
*pMutex
= (PthreadMutex
*)p
;
632 return pMutex
? !pthread_equal(pMutex
->owner
, pthread_self()) : 1;
636 ** End of pthreads mutex implementation.
637 *************************************************************************/
639 /*************************************************************************
640 ** Noop mutex implementation
642 typedef struct NoopMutex NoopMutex
;
644 lsm_env
*pEnv
; /* Environment handle (for xFree()) */
645 int bHeld
; /* True if mutex is held */
646 int bStatic
; /* True for a static mutex */
648 static NoopMutex aStaticNoopMutex
[2] = {
653 static int lsmPosixOsMutexStatic(
658 assert( iMutex
>=1 && iMutex
<=(int)array_size(aStaticNoopMutex
) );
659 *ppStatic
= (lsm_mutex
*)&aStaticNoopMutex
[iMutex
-1];
662 static int lsmPosixOsMutexNew(lsm_env
*pEnv
, lsm_mutex
**ppNew
){
664 p
= (NoopMutex
*)lsmMallocZero(pEnv
, sizeof(NoopMutex
));
665 if( p
) p
->pEnv
= pEnv
;
666 *ppNew
= (lsm_mutex
*)p
;
667 return (p
? LSM_OK
: LSM_NOMEM_BKPT
);
669 static void lsmPosixOsMutexDel(lsm_mutex
*pMutex
) {
670 NoopMutex
*p
= (NoopMutex
*)pMutex
;
671 assert( p
->bStatic
==0 && p
->pEnv
);
674 static void lsmPosixOsMutexEnter(lsm_mutex
*pMutex
){
675 NoopMutex
*p
= (NoopMutex
*)pMutex
;
676 assert( p
->bHeld
==0 );
679 static int lsmPosixOsMutexTry(lsm_mutex
*pMutex
){
680 NoopMutex
*p
= (NoopMutex
*)pMutex
;
681 assert( p
->bHeld
==0 );
685 static void lsmPosixOsMutexLeave(lsm_mutex
*pMutex
){
686 NoopMutex
*p
= (NoopMutex
*)pMutex
;
687 assert( p
->bHeld
==1 );
691 static int lsmPosixOsMutexHeld(lsm_mutex
*pMutex
){
692 NoopMutex
*p
= (NoopMutex
*)pMutex
;
693 return p
? p
->bHeld
: 1;
695 static int lsmPosixOsMutexNotHeld(lsm_mutex
*pMutex
){
696 NoopMutex
*p
= (NoopMutex
*)pMutex
;
697 return p
? !p
->bHeld
: 1;
700 /***************************************************************************/
701 #endif /* else LSM_MUTEX_NONE */
703 /* Without LSM_DEBUG, the MutexHeld tests are never called */
705 # define lsmPosixOsMutexHeld 0
706 # define lsmPosixOsMutexNotHeld 0
709 lsm_env
*lsm_default_env(void){
710 static lsm_env posix_env
= {
711 sizeof(lsm_env
), /* nByte */
713 /***** file i/o ******************/
715 lsmPosixOsFullpath
, /* xFullpath */
716 lsmPosixOsOpen
, /* xOpen */
717 lsmPosixOsRead
, /* xRead */
718 lsmPosixOsWrite
, /* xWrite */
719 lsmPosixOsTruncate
, /* xTruncate */
720 lsmPosixOsSync
, /* xSync */
721 lsmPosixOsSectorSize
, /* xSectorSize */
722 lsmPosixOsRemap
, /* xRemap */
723 lsmPosixOsFileid
, /* xFileid */
724 lsmPosixOsClose
, /* xClose */
725 lsmPosixOsUnlink
, /* xUnlink */
726 lsmPosixOsLock
, /* xLock */
727 lsmPosixOsTestLock
, /* xTestLock */
728 lsmPosixOsShmMap
, /* xShmMap */
729 lsmPosixOsShmBarrier
, /* xShmBarrier */
730 lsmPosixOsShmUnmap
, /* xShmUnmap */
731 /***** memory allocation *********/
733 lsmPosixOsMalloc
, /* xMalloc */
734 lsmPosixOsRealloc
, /* xRealloc */
735 lsmPosixOsFree
, /* xFree */
736 lsmPosixOsMSize
, /* xSize */
737 /***** mutexes *********************/
739 lsmPosixOsMutexStatic
, /* xMutexStatic */
740 lsmPosixOsMutexNew
, /* xMutexNew */
741 lsmPosixOsMutexDel
, /* xMutexDel */
742 lsmPosixOsMutexEnter
, /* xMutexEnter */
743 lsmPosixOsMutexTry
, /* xMutexTry */
744 lsmPosixOsMutexLeave
, /* xMutexLeave */
745 lsmPosixOsMutexHeld
, /* xMutexHeld */
746 lsmPosixOsMutexNotHeld
, /* xMutexNotHeld */
747 /***** other *********************/
748 lsmPosixOsSleep
, /* xSleep */