📄 os.c
字号:
** 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 ** sqliteOsReadLock() 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 sqliteOsWriteLock().**** 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#if OS_MAC# define FIRST_LOCKBYTE (0x000fffff - N_LOCKBYTE)#else# define FIRST_LOCKBYTE (0xffffffff - N_LOCKBYTE)#endif/*** 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 SQLITE_OK on success and SQLITE_BUSY on failure. If this** library was compiled with large file support (LFS) but LFS is not** available on the host, then an SQLITE_NOLFS is returned.*/int sqliteOsReadLock(OsFile *id){#if OS_UNIX int rc; sqliteOsEnterMutex(); if( id->pLock->cnt>0 ){ if( !id->locked ){ id->pLock->cnt++; id->locked = 1; } rc = SQLITE_OK; }else if( id->locked || id->pLock->cnt==0 ){ struct flock lock; int s; lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; s = fcntl(id->fd, F_SETLK, &lock); if( s!=0 ){ rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; }else{ rc = SQLITE_OK; id->pLock->cnt = 1; id->locked = 1; } }else{ rc = SQLITE_BUSY; } sqliteOsLeaveMutex(); return rc;#endif#if OS_WIN int rc; if( id->locked>0 ){ rc = SQLITE_OK; }else{ int lk = (sqliteRandomInteger() & 0x7ffffff)%N_LOCKBYTE+1; int res; int cnt = 100; 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 = SQLITE_OK; }else{ rc = SQLITE_BUSY; } } return rc;#endif#if OS_MAC int rc; if( id->locked>0 || id->refNumRF == -1 ){ rc = SQLITE_OK; }else{ int lk = (sqliteRandomInteger() & 0x7ffffff)%N_LOCKBYTE+1; OSErr res; int cnt = 5; ParamBlockRec params; memset(¶ms, 0, sizeof(params)); params.ioParam.ioRefNum = id->refNumRF; params.ioParam.ioPosMode = fsFromStart; params.ioParam.ioPosOffset = FIRST_LOCKBYTE; params.ioParam.ioReqCount = 1; while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ UInt32 finalTicks; Delay(1, &finalTicks); /* 1/60 sec */ } if( res == noErr ){ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; params.ioParam.ioReqCount = N_LOCKBYTE; PBUnlockRangeSync(¶ms); params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk; params.ioParam.ioReqCount = 1; res = PBLockRangeSync(¶ms); params.ioParam.ioPosOffset = FIRST_LOCKBYTE; params.ioParam.ioReqCount = 1; PBUnlockRangeSync(¶ms); } if( res == noErr ){ id->locked = lk; rc = SQLITE_OK; }else{ rc = SQLITE_BUSY; } } return rc;#endif}/*** Change the lock status to be an exclusive or write lock. Return** SQLITE_OK on success and SQLITE_BUSY on a failure. If this** library was compiled with large file support (LFS) but LFS is not** available on the host, then an SQLITE_NOLFS is returned.*/int sqliteOsWriteLock(OsFile *id){#if OS_UNIX int rc; sqliteOsEnterMutex(); if( id->pLock->cnt==0 || (id->pLock->cnt==1 && id->locked==1) ){ struct flock lock; int s; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; s = fcntl(id->fd, F_SETLK, &lock); if( s!=0 ){ rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; }else{ rc = SQLITE_OK; id->pLock->cnt = -1; id->locked = 1; } }else{ rc = SQLITE_BUSY; } sqliteOsLeaveMutex(); return rc;#endif#if OS_WIN int rc; if( id->locked<0 ){ rc = SQLITE_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 = SQLITE_OK; }else{ rc = SQLITE_BUSY; } } return rc;#endif#if OS_MAC int rc; if( id->locked<0 || id->refNumRF == -1 ){ rc = SQLITE_OK; }else{ OSErr res; int cnt = 5; ParamBlockRec params; memset(¶ms, 0, sizeof(params)); params.ioParam.ioRefNum = id->refNumRF; params.ioParam.ioPosMode = fsFromStart; params.ioParam.ioPosOffset = FIRST_LOCKBYTE; params.ioParam.ioReqCount = 1; while( cnt-->0 && (res = PBLockRangeSync(¶ms))!=noErr ){ UInt32 finalTicks; Delay(1, &finalTicks); /* 1/60 sec */ } if( res == noErr ){ params.ioParam.ioPosOffset = FIRST_LOCKBYTE + id->locked; params.ioParam.ioReqCount = 1; if( id->locked==0 || PBUnlockRangeSync(¶ms)==noErr ){ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; params.ioParam.ioReqCount = N_LOCKBYTE; res = PBLockRangeSync(¶ms); }else{ res = afpRangeNotLocked; } params.ioParam.ioPosOffset = FIRST_LOCKBYTE; params.ioParam.ioReqCount = 1; PBUnlockRangeSync(¶ms); } if( res == noErr ){ id->locked = -1; rc = SQLITE_OK; }else{ rc = SQLITE_BUSY; } } return rc;#endif}/*** 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 SQLITE_NOLFS is returned.*/int sqliteOsUnlock(OsFile *id){#if OS_UNIX int rc; if( !id->locked ) return SQLITE_OK; sqliteOsEnterMutex(); assert( id->pLock->cnt!=0 ); if( id->pLock->cnt>1 ){ id->pLock->cnt--; rc = SQLITE_OK; }else{ struct flock lock; int s; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; s = fcntl(id->fd, F_SETLK, &lock); if( s!=0 ){ rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY; }else{ rc = SQLITE_OK; id->pLock->cnt = 0; } } sqliteOsLeaveMutex(); id->locked = 0; return rc;#endif#if OS_WIN int rc; if( id->locked==0 ){ rc = SQLITE_OK; }else if( isNT() || id->locked<0 ){ UnlockFile(id->h, FIRST_LOCKBYTE+1, 0, N_LOCKBYTE, 0); rc = SQLITE_OK; id->locked = 0; }else{ UnlockFile(id->h, FIRST_LOCKBYTE+id->locked, 0, 1, 0); rc = SQLITE_OK; id->locked = 0; } return rc;#endif#if OS_MAC int rc; ParamBlockRec params; memset(¶ms, 0, sizeof(params)); params.ioParam.ioRefNum = id->refNumRF; params.ioParam.ioPosMode = fsFromStart; if( id->locked==0 || id->refNumRF == -1 ){ rc = SQLITE_OK; }else if( id->locked<0 ){ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1; params.ioParam.ioReqCount = N_LOCKBYTE; PBUnlockRangeSync(¶ms); rc = SQLITE_OK; id->locked = 0; }else{ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+id->locked; params.ioParam.ioReqCount = 1; PBUnlockRangeSync(¶ms); rc = SQLITE_OK; id->locked = 0; } return rc;#endif}/*** 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 sqliteOsRandomSeed(char *zBuf){#ifdef SQLITE_TEST /* When testing, always use the same random number sequence. ** This makes the tests repeatable. */ memset(zBuf, 0, 256);#endif#if OS_UNIX && !defined(SQLITE_TEST) int pid; time((time_t*)zBuf); pid = getpid(); memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid));#endif#if OS_WIN && !defined(SQLITE_TEST) GetSystemTime((LPSYSTEMTIME)zBuf);#endif#if OS_MAC int pid; Microseconds((UnsignedWide*)zBuf); pid = getpid(); memcpy(&zBuf[sizeof(UnsignedWide)], &pid, sizeof(pid));#endif return SQLITE_OK;}/*** Sleep for a little while. Return the amount of time slept.*/int sqliteOsSleep(int ms){#if OS_UNIX#if defined(HAVE_USLEEP) && HAVE_USLEEP usleep(ms*1000); return ms;#else sleep((ms+999)/1000); return 1000*((ms+999)/1000);#endif#endif#if OS_WIN Sleep(ms); return ms;#endif#if OS_MAC UInt32 finalTicks; UInt32 ticks = (((UInt32)ms+16)*3)/50; /* 1/60 sec per tick */ Delay(ticks, &finalTicks); return (int)((ticks*50)/3);#endif}/*** Macros used to determine whether or not to use threads. The** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for** Posix threads and SQLITE_W32_THREADS is defined if we are** synchronizing using Win32 threads.*/#if OS_UNIX && defined(THREADSAFE) && THREADSAFE# include <pthread.h># define SQLITE_UNIX_THREADS 1#endif#if OS_WIN && defined(THREADSAFE) && THREADSAFE# define SQLITE_W32_THREADS 1#endif#if OS_MAC && defined(THREADSAFE) && THREADSAFE# include <Multiprocessing.h># define SQLITE_MACOS_MULTITASKING 1#endif/*** Static variables used for thread synchronization*/static int inMutex = 0;#ifdef SQLITE_UNIX_THREADS static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;#endif#ifdef SQLITE_W32_THREADS static CRITICAL_SECTION cs;#endif#ifdef SQLITE_MACOS_MULTITASKING static MPCriticalRegionID criticalRegion;#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().**** SQLite uses only a single Mutex. There is not much critical** code and what little there is executes quickly and without blocking.*/void sqliteOsEnterMutex(){#ifdef SQLITE_UNIX_THREADS pthread_mutex_lock(&mutex);#endif#ifdef SQLITE_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#ifdef SQLITE_MACOS_MULTITASKING static volatile int notInit = 1; if( notInit ){ if( notInit == 2 ) /* as close as you can get to thread safe init */ MPYield(); else{ notInit = 2; MPCreateCriticalRegion(&criticalRegion); notInit = 0; } } MPEnterCriticalRegion(criticalRegion, kDurationForever);#endif assert( !inMutex ); inMutex = 1;}void sqliteOsLeaveMutex(){ assert( inMutex ); inMutex = 0;#ifdef SQLITE_UNIX_THREADS pthread_mutex_unlock(&mutex);#endif#ifdef SQLITE_W32_THREADS LeaveCriticalSection(&cs);#endif#ifdef SQLITE_MACOS_MULTITASKING MPExitCriticalRegion(criticalRegion);#endif}/*** Turn a relative pathname into a full pathname. Return a pointer** to the full pathname stored in space obtained from sqliteMalloc().** The calling function is responsible for freeing this space once it** is no longer needed.*/char *sqliteOsFullPathname(const char *zRelative){#if OS_UNIX char *zFull = 0; if( zRelative[0]=='/' ){ sqliteSetString(&zFull, zRelative, 0); }else{ char zBuf[5000]; sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), "/", zRelative, 0); } return zFull;#endif#if OS_WIN char *zNotUsed; char *zFull; int nByte; nByte = GetFullPathName(zRelative, 0, 0, &zNotUsed) + 1; zFull = sqliteMalloc( nByte ); if( zFull==0 ) return 0; GetFullPathName(zRelative, nByte, zFull, &zNotUsed); return zFull;#endif#if OS_MAC char *zFull = 0; if( zRelative[0]==':' ){ char zBuf[_MAX_PATH+1]; sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), &(zRelative[1]), 0); }else{ if( strchr(zRelative, ':') ){ sqliteSetString(&zFull, zRelative, 0); }else{ char zBuf[_MAX_PATH+1]; sqliteSetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, 0); } } return zFull;#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 sqliteOsCurrentTime(double *prNow){#if OS_UNIX time_t t; time(&t); *prNow = t/86400.0 + 2440587.5; return 0;#endif#if OS_WIN 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;#endif return 0; return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -