?? os.c
字號:
/*** 2001 September 16**** 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.************************************************************************************ This file contains code that is specific to particular operating** systems. The purpose of this file is to provide a uniform abstraction** on which the rest of SQLite can operate.*/#include "os.h" /* Must be first to enable large file support */#include "sqliteInt.h"#if OS_UNIX# include <time.h># include <errno.h># include <unistd.h># ifndef O_LARGEFILE# define O_LARGEFILE 0# endif# ifdef SQLITE_DISABLE_LFS# undef O_LARGEFILE# define O_LARGEFILE 0# endif# ifndef O_NOFOLLOW# define O_NOFOLLOW 0# endif# ifndef O_BINARY# define O_BINARY 0# endif#endif#if OS_WIN# include <winbase.h>
# include <time.h>#endif#if OS_MAC# include <extras.h># include <path2fss.h># include <TextUtils.h># include <FinderRegistry.h># include <Folders.h># include <Timer.h># include <OSUtils.h>#endif/*** The DJGPP compiler environment looks mostly like Unix, but it** lacks the fcntl() system call. So redefine fcntl() to be something** that always succeeds. This means that locking does not occur under** DJGPP. But its DOS - what did you expect?*/#ifdef __DJGPP__# define fcntl(A,B,C) 0#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/*** Macros for performance tracing. Normally turned off*/#if 0static int last_page = 0;__inline__ unsigned long long int hwtime(void){ unsigned long long int x; __asm__("rdtsc\n\t" "mov %%edx, %%ecx\n\t" :"=A" (x)); return x;}static unsigned long long int g_start;static unsigned int elapse;#define TIMER_START g_start=hwtime()#define TIMER_END elapse=hwtime()-g_start#define SEEK(X) last_page=(X)#define TRACE1(X) fprintf(stderr,X)#define TRACE2(X,Y) fprintf(stderr,X,Y)#define TRACE3(X,Y,Z) fprintf(stderr,X,Y,Z)#define TRACE4(X,Y,Z,A) fprintf(stderr,X,Y,Z,A)#define TRACE5(X,Y,Z,A,B) fprintf(stderr,X,Y,Z,A,B)#else#define TIMER_START#define TIMER_END#define SEEK(X)#define TRACE1(X)#define TRACE2(X,Y)#define TRACE3(X,Y,Z)#define TRACE4(X,Y,Z,A)#define TRACE5(X,Y,Z,A,B)#endif#if OS_UNIX/*** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996)** section 6.5.2.2 lines 483 through 490 specify that when a process** sets or clears a lock, that operation overrides any prior locks set** by the same process. It does not explicitly say so, but this implies** that it overrides locks set by the same process using a different** file descriptor. Consider this test case:**** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);**** Suppose ./file1 and ./file2 are really the same file (because** one is a hard or symbolic link to the other) then if you set** an exclusive lock on fd1, then try to get an exclusive lock** on fd2, it works. I would have expected the second lock to** fail since there was already a lock on the file due to fd1.** But not so. Since both locks came from the same process, the** second overrides the first, even though they were on different** file descriptors opened on different file names.**** Bummer. If you ask me, this is broken. Badly broken. It means** that we cannot use POSIX locks to synchronize file access among** competing threads of the same process. POSIX locks will work fine** to synchronize access for threads in separate processes, but not** threads within the same process.**** To work around the problem, SQLite has to manage file locks internally** on its own. Whenever a new database is opened, we have to find the** specific inode of the database file (the inode is determined by the** st_dev and st_ino fields of the stat structure that fstat() fills in)** and check for locks already existing on that inode. When locks are** created or removed, we have to look at our own internal record of the** locks to see if another thread has previously set a lock on that same** inode.**** The OsFile structure for POSIX is no longer just an integer file** descriptor. It is now a structure that holds the integer file** descriptor and a pointer to a structure that describes the internal** locks on the corresponding inode. There is one locking structure** per inode, so if the same inode is opened twice, both OsFile structures** point to the same locking structure. The locking structure keeps** a reference count (so we will know when to delete it) and a "cnt"** field that tells us its internal lock status. cnt==0 means the** file is unlocked. cnt==-1 means the file has an exclusive lock.** cnt>0 means there are cnt shared locks on the file.**** Any attempt to lock or unlock a file first checks the locking** structure. The fcntl() system call is only invoked to set a ** POSIX lock if the internal lock structure transitions between** a locked and an unlocked state.**** 2004-Jan-11:** More recent discoveries about POSIX advisory locks. (The more** I discover, the more I realize the a POSIX advisory locks are** an abomination.)**** If you close a file descriptor that points to a file that has locks,** all locks on that file that are owned by the current process are** released. To work around this problem, each OsFile structure contains** a pointer to an openCnt structure. There is one openCnt structure** per open inode, which means that multiple OsFiles can point to a single** openCnt. When an attempt is made to close an OsFile, if there are** other OsFiles open on the same inode that are holding locks, the call** to close() the file descriptor is deferred until all of the locks clear.** The openCnt structure keeps a list of file descriptors that need to** be closed and that list is walked (and cleared) when the last lock** clears.**** First, under Linux threads, because each thread has a separate** process ID, lock operations in one thread do not override locks** to the same file in other threads. Linux threads behave like** separate processes in this respect. But, if you close a file** descriptor in linux threads, all locks are cleared, even locks** on other threads and even though the other threads have different** process IDs. Linux threads is inconsistent in this respect.** (I'm beginning to think that linux threads is an abomination too.)** The consequence of this all is that the hash table for the lockInfo** structure has to include the process id as part of its key because** locks in different threads are treated as distinct. But the ** openCnt structure should not include the process id in its** key because close() clears lock on all threads, not just the current** thread. Were it not for this goofiness in linux threads, we could** combine the lockInfo and openCnt structures into a single structure.*//*** An instance of the following structure serves as the key used** to locate a particular lockInfo structure given its inode. Note** that we have to include the process ID as part of the key. On some** threading implementations (ex: linux), each thread has a separate** process ID.*/struct lockKey { dev_t dev; /* Device number */ ino_t ino; /* Inode number */ pid_t pid; /* Process ID */};/*** An instance of the following structure is allocated for each open** inode on each thread with a different process ID. (Threads have** different process IDs on linux, but not on most other unixes.)**** A single inode can have multiple file descriptors, so each OsFile** structure contains a pointer to an instance of this object and this** object keeps a count of the number of OsFiles pointing to it.*/struct lockInfo { struct lockKey key; /* The lookup key */ int cnt; /* 0: unlocked. -1: write lock. 1...: read lock. */ int nRef; /* Number of pointers to this structure */};/*** An instance of the following structure serves as the key used** to locate a particular openCnt structure given its inode. This** is the same as the lockKey except that the process ID is omitted.*/struct openKey { dev_t dev; /* Device number */ ino_t ino; /* Inode number */};/*** An instance of the following structure is allocated for each open** inode. This structure keeps track of the number of locks on that** inode. If a close is attempted against an inode that is holding** locks, the close is deferred until all locks clear by adding the** file descriptor to be closed to the pending list.*/struct openCnt { struct openKey key; /* The lookup key */ int nRef; /* Number of pointers to this structure */ int nLock; /* Number of outstanding locks */ int nPending; /* Number of pending close() operations */ int *aPending; /* Malloced space holding fd's awaiting a close() */};/* ** These hash table maps inodes and process IDs into lockInfo and openCnt** structures. Access to these hash tables must be protected by a mutex.*/static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };/*** Release a lockInfo structure previously allocated by findLockInfo().*/static void releaseLockInfo(struct lockInfo *pLock){ pLock->nRef--; if( pLock->nRef==0 ){ sqliteHashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0); sqliteFree(pLock); }}/*** Release a openCnt structure previously allocated by findLockInfo().*/static void releaseOpenCnt(struct openCnt *pOpen){ pOpen->nRef--; if( pOpen->nRef==0 ){ sqliteHashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0); sqliteFree(pOpen->aPending); sqliteFree(pOpen); }}/*** Given a file descriptor, locate lockInfo and openCnt structures that** describes that file descriptor. Create a new ones if necessary. The** return values might be unset if an error occurs.**** Return the number of errors.*/int findLockInfo( int fd, /* The file descriptor used in the key */ struct lockInfo **ppLock, /* Return the lockInfo structure here */ struct openCnt **ppOpen /* Return the openCnt structure here */){ int rc; struct lockKey key1; struct openKey key2; struct stat statbuf; struct lockInfo *pLock; struct openCnt *pOpen; rc = fstat(fd, &statbuf); if( rc!=0 ) return 1; memset(&key1, 0, sizeof(key1)); key1.dev = statbuf.st_dev; key1.ino = statbuf.st_ino; key1.pid = getpid(); memset(&key2, 0, sizeof(key2)); key2.dev = statbuf.st_dev; key2.ino = statbuf.st_ino; pLock = (struct lockInfo*)sqliteHashFind(&lockHash, &key1, sizeof(key1)); if( pLock==0 ){ struct lockInfo *pOld; pLock = sqliteMallocRaw( sizeof(*pLock) ); if( pLock==0 ) return 1; pLock->key = key1; pLock->nRef = 1; pLock->cnt = 0; pOld = sqliteHashInsert(&lockHash, &pLock->key, sizeof(key1), pLock); if( pOld!=0 ){ assert( pOld==pLock ); sqliteFree(pLock); return 1; } }else{ pLock->nRef++; } *ppLock = pLock; pOpen = (struct openCnt*)sqliteHashFind(&openHash, &key2, sizeof(key2)); if( pOpen==0 ){ struct openCnt *pOld; pOpen = sqliteMallocRaw( sizeof(*pOpen) ); if( pOpen==0 ){ releaseLockInfo(pLock); return 1; } pOpen->key = key2; pOpen->nRef = 1; pOpen->nLock = 0; pOpen->nPending = 0; pOpen->aPending = 0; pOld = sqliteHashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen); if( pOld!=0 ){ assert( pOld==pOpen ); sqliteFree(pOpen); releaseLockInfo(pLock); return 1; } }else{ pOpen->nRef++; } *ppOpen = pOpen; return 0;}#endif /** POSIX advisory lock work-around **//*** If we compile with the SQLITE_TEST macro set, then the following block** of code will give us the ability to simulate a disk I/O error. This** is used for testing the I/O recovery logic.*/#ifdef SQLITE_TESTint sqlite_io_error_pending = 0;#define SimulateIOError(A) \ if( sqlite_io_error_pending ) \ if( sqlite_io_error_pending-- == 1 ){ local_ioerr(); return A; }static void local_ioerr(){ sqlite_io_error_pending = 0; /* Really just a place to set a breakpoint */}#else#define SimulateIOError(A)#endif/*** When testing, keep a count of the number of open files.*/#ifdef SQLITE_TESTint sqlite_open_file_count = 0;#define OpenCounter(X) sqlite_open_file_count+=(X)#else#define OpenCounter(X)#endif/*** Windows CE helper routine.** Similar to strdup, but first converts the ANSI string to UNICODE** and then returns the UNICODE clone.*/#ifdef _WIN32_WCEstatic WCHAR * wStrDup( char const * str ){ size_t len = strlen(str) + 1; // +1 for terminating '\0' WCHAR * aux = (WCHAR *) sqliteMalloc( len*sizeof(WCHAR) ); mbstowcs( aux, str, len ); return aux;}#endif/*** Windows CE versions prior to 3.0 don't implement atof(), so we** implement it here*/#if SQLITE_WCE_USE_OWN_ATOFdouble sqlitewce_atof( const char *str ){ wchar_t wcs[64]; mbstowcs( wcs, str, 64 ); return wcstod( wcs, NULL );}#endif/*** This is needed for the command line version of sqlite to compile.**/int isatty( int handle ){ UNREFERENCED_PARAMETER(handle); return 1;}/*** The same applies for the assert() routine.*/void sqlitewce_assert( int x, char * test, char * file, int line ){ /* This should be fixed somehow, to avoid overflows. * Also, when an assert is caused by memory allocation faillure, this * will probably fail. */ WCHAR buf[2048]; if (x) return; swprintf( buf, L"assert( %hs )\r\n\r\nFile: '%hs' Line: %d", test, file, line ); MessageBoxW( 0, buf, L"Assertion Error", MB_ICONERROR );}
/*
** Implementation of the localtime function for systems not having it.
** Convert time_t to local time in tm struct format.
*/
struct tm * sqlitewce_localtime( const time_t *timer )
{
static struct tm s_tm;
FILETIME uf, lf;
SYSTEMTIME ls;
// Convert time_t to FILETIME
unsigned __int64 i64 = (__int64)timer*10000000 + 116444736000000000;
uf.dwLowDateTime = (DWORD) i64;
uf.dwHighDateTime = (DWORD) (i64 >> 32);
// Convert UTC(GMT) FILETIME to local FILETIME
FileTimeToLocalFileTime( &uf, &lf );
// Convert FILETIME to SYSTEMTIME
FileTimeToSystemTime( &lf, &ls );
// Convert SYSTEMTIME to tm
s_tm.tm_sec = ls.wSecond;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -