📄 winos.c
字号:
return osType==2;
}
/*
** Windows file locking notes: [similar issues apply to MacOS]
**
** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
** those functions are not available. So we use only LockFile() and
** UnlockFile().
**
** LockFile() prevents not just writing but also reading by other processes.
** (This is a design error on the part of Windows, but there is nothing
** we can do about that.) So the region used for locking is at the
** end of the file where it is unlikely to ever interfere with an
** actual read attempt.
**
** A database read lock is obtained by locking a single randomly-chosen
** byte out of a specific range of bytes. The lock byte is obtained at
** random so two separate readers can probably access the file at the
** same time, unless they are unlucky and choose the same lock byte.
** A database write lock is obtained by locking all bytes in the range.
** There can only be one writer.
**
** A lock is obtained on the first byte of the lock range before acquiring
** either a read lock or a write lock. This prevents two processes from
** attempting to get a lock at a same time. The semantics of
** eDbOsReadLock() require that if there is already a write lock, that
** lock is converted into a read lock atomically. The lock on the first
** byte allows us to drop the old write lock and get the read lock without
** another process jumping into the middle and messing us up. The same
** argument applies to eDbOsWriteLock().
**
** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
** which means we can use reader/writer locks. When reader writer locks
** are used, the lock is placed on the same range of bytes that is used
** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
** will support two or more Win95 readers or two or more WinNT readers.
** But a single Win95 reader will lock out all WinNT readers and a single
** WinNT reader will lock out all other Win95 readers.
**
** Note: On MacOS we use the resource fork for locking.
**
** The following #defines specify the range of bytes used for locking.
** N_LOCKBYTE is the number of bytes available for doing the locking.
** The first byte used to hold the lock while the lock is changing does
** not count toward this number. FIRST_LOCKBYTE is the address of
** the first byte in the range of bytes used for locking.
*/
#define N_LOCKBYTE 10239
# define FIRST_LOCKBYTE (0xffffffff - N_LOCKBYTE)
/*
** Change the status of the lock on the file "id" to be a readlock.
** If the file was write locked, then this reduces the lock to a read.
** If the file was read locked, then this acquires a new read lock.
**
** Return eDb_OK on success and eDb_BUSY on failure. If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an eDb_NOLFS is returned.
*/
int eDbOsReadLock(OsFile *id){
int rc;
if( id->locked>0 ){
rc = eDb_OK;
}else{
int lk;
int res;
int cnt = 100;
eDbRandomness(sizeof(lk), &lk);
lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1;
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
Sleep(1);
}
if( res ){
UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
if( isNT() ){
OVERLAPPED ovlp;
ovlp.Offset = FIRST_LOCKBYTE+1;
ovlp.OffsetHigh = 0;
ovlp.hEvent = 0;
res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY,
0, N_LOCKBYTE, 0, &ovlp);
}else{
res = LockFile(id->h, FIRST_LOCKBYTE+lk, 0, 1, 0);
}
UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0);
}
if( res ){
id->locked = lk;
rc = eDb_OK;
}else{
rc = eDb_BUSY;
}
}
return rc;
}
/*
** Change the lock status to be an exclusive or write lock. Return
** eDb_OK on success and eDb_BUSY on a failure. If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an eDb_NOLFS is returned.
*/
int eDbOsWriteLock(OsFile *id){
int rc;
if( id->locked<0 ){
rc = eDb_OK;
}else{
int res;
int cnt = 100;
while( cnt-->0 && (res = LockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0))==0 ){
Sleep(1);
}
if( res ){
if( id->locked>0 ){
if( isNT() ){
UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
}else{
res = UnlockFile(id->h, FIRST_LOCKBYTE + id->locked, 0, 1, 0);
}
}
if( res ){
res = LockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
}else{
res = 0;
}
UnlockFile(id->h, FIRST_LOCKBYTE, 0, 1, 0);
}
if( res ){
id->locked = -1;
rc = eDb_OK;
}else{
rc = eDb_BUSY;
}
}
return rc;
}
/*
** Unlock the given file descriptor. If the file descriptor was
** not previously locked, then this routine is a no-op. If this
** library was compiled with large file support (LFS) but LFS is not
** available on the host, then an eDb_NOLFS is returned.
*/
int eDbOsUnlock(OsFile *id){
int rc;
if( id->locked==0 ){
rc = eDb_OK;
}else if( isNT() || id->locked<0 ){
UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0);
rc = eDb_OK;
id->locked = 0;
}else{
UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0);
rc = eDb_OK;
id->locked = 0;
}
return rc;
}
/*
** Get information to seed the random number generator. The seed
** is written into the buffer zBuf[256]. The calling function must
** supply a sufficiently large buffer.
*/
int eDbOsRandomSeed(char *zBuf){
/* We have to initialize zBuf to prevent valgrind from reporting
** errors. The reports issued by valgrind are incorrect - we would
** prefer that the randomness be increased by making use of the
** uninitialized space in zBuf - but valgrind errors tend to worry
** some users. Rather than argue, it seems easier just to initialize
** the whole array and silence valgrind, even if that means less randomness
** in the random seed.
**
** When testing, initializing zBuf[] to zero is all we do. That means
** that we always use the same random number sequence.* This makes the
** tests repeatable.
*/
memset(zBuf, 0, 256);
#if !defined(eDb_TEST)
GetSystemTime((LPSYSTEMTIME)zBuf);
#endif
return eDb_OK;
}
/*
** Sleep for a little while. Return the amount of time slept.
*/
int eDbOsSleep(int ms){
Sleep(ms);
return ms;
}
/*
** Static variables used for thread synchronization
*/
static int inMutex = 0;
#ifdef eDb_W32_THREADS
static CRITICAL_SECTION cs;
#endif
/*
** The following pair of routine implement mutual exclusion for
** multi-threaded processes. Only a single thread is allowed to
** executed code that is surrounded by EnterMutex() and LeaveMutex().
**
** eDb uses only a single Mutex. There is not much critical
** code and what little there is executes quickly and without blocking.
*/
void eDbOsEnterMutex(){
#ifdef eDb_W32_THREADS
static int isInit = 0;
while( !isInit ){
static long lock = 0;
if( InterlockedIncrement(&lock)==1 ){
InitializeCriticalSection(&cs);
isInit = 1;
}else{
Sleep(1);
}
}
EnterCriticalSection(&cs);
#endif
assert( !inMutex );
inMutex = 1;
}
void eDbOsLeaveMutex(){
assert( inMutex );
inMutex = 0;
#ifdef eDb_W32_THREADS
LeaveCriticalSection(&cs);
#endif
}
/*
** Turn a relative pathname into a full pathname. Return a pointer
** to the full pathname stored in space obtained from eDbMalloc().
** The calling function is responsible for freeing this space once it
** is no longer needed.
*/
char *eDbOsFullPathname(const char *zRelative){
char *zNotUsed;
char *zFull;
int nByte;
nByte = GetFullPathName(zRelative, 0, 0, &zNotUsed) + 1;
zFull = eDbMalloc( nByte );
if( zFull==0 ) return 0;
GetFullPathName(zRelative, nByte, zFull, &zNotUsed);
return zFull;
}
/*
** The following variable, if set to a now-zero value, become the result
** returned from eDbOsCurrentTime(). This is used for testing.
*/
#ifdef eDb_TEST
int eDb_current_time = 0;
#endif
/*
** Find the current time (in Universal Coordinated Time). Write the
** current time and date as a Julian Day number into *prNow and
** return 0. Return 1 if the time and date cannot be found.
*/
int eDbOsCurrentTime(double *prNow){
FILETIME ft;
/* FILETIME structure is a 64-bit value representing the number of
100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/
double now;
GetSystemTimeAsFileTime( &ft );
now = ((double)ft.dwHighDateTime) * 4294967296.0;
*prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5;
return 0;
}
/*
** Get a single 8-bit random value from the RC4 PRNG. The Mutex
** must be held while executing this routine.
**
** Why not just use a library random generator like lrand48() for this?
** Because the OP_NewRecno opcode in the VDBE depends on having a very
** good source of random numbers. The lrand48() library function may
** well be good enough. But maybe not. Or maybe lrand48() has some
** subtle problems on some systems that could cause problems. It is hard
** to know. To minimize the risk of problems due to bad lrand48()
** implementations, eDb uses this random number generator based
** on RC4, which we know works very well.
*/
static int randomByte(){
unsigned char t;
/* All threads share a single random number generator.
** This structure is the current state of the generator.
*/
static struct {
unsigned char isInit; /* True if initialized */
unsigned char i, j; /* State variables */
unsigned char s[256]; /* State variables */
} prng;
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
** not need to contain a lot of randomness since we are not
** trying to do secure encryption or anything like that...
**
** Nothing in this file or anywhere else in eDb does any kind of
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
if( !prng.isInit ){
int i;
char k[256];
prng.j = 0;
prng.i = 0;
eDbOsRandomSeed(k);
for(i=0; i<256; i++){
prng.s[i] = i;
}
for(i=0; i<256; i++){
prng.j += prng.s[i] + k[i];
t = prng.s[prng.j];
prng.s[prng.j] = prng.s[i];
prng.s[i] = t;
}
prng.isInit = 1;
}
/* Generate and return single random byte
*/
prng.i++;
t = prng.s[prng.i];
prng.j += t;
prng.s[prng.i] = prng.s[prng.j];
prng.s[prng.j] = t;
t += prng.s[prng.i];
return prng.s[t];
}
/*
** Return N random bytes.
*/
void eDbRandomness(int N, void *pBuf){
unsigned char *zBuf = pBuf;
eDbOsEnterMutex();
while( N-- ){
*(zBuf++) = randomByte();
}
eDbOsLeaveMutex();
}
/*
** get a random between 0 and 59
**
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
*/
int getRandom(int i)
{
/*
int re;
PstructTime timePtr;
InitRtc();
Get_Rtc(timePtr);
re=timePtr->second%i;
return re;
*/
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -