sqlite只有在多线程的程序中才会启用封锁,单线程的不需要
封锁有四种类型 SHARED、RESERVED、PENDING、EXCLUSIVE
锁的升级顺序
UNLOCKED -> SHARED
SHARED -> RESERVED
SHARED -> (PENDING) -> EXCLUSIVE
RESERVED -> (PENDING) -> EXCLUSIVE
PENDING -> EXCLUSIVE
SHARED: Any number of processes may hold a SHARED lock simultaneously.
RESERVED: A single process may hold a RESERVED lock on a file at any time. Other processes may hold and obtain new SHARED locks.
PENDING: A single process may hold a PENDING lock on a file at any one time. Existing SHARED locks may persist,but no new SHARED locks may be obtained by other processes.
EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
物理实现是通过LockFile在物理文件上加锁的,其实就是锁文件中一指定字节,不是在通过进程间通信,如互斥体、条件变理什么实现的,之所以要锁文件,主要原因是sqlite大部分是做为嵌入式数据库使用的,没有Daemon线程,只有通过锁文件来区分不同的线程
A SHARED_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.
An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. There can only be one writer.
A RESERVED_LOCK is obtained by locking a single byte of the file that is designated as the reserved lock byte.
A PENDING_LOCK is obtained by locking a designated byte different from the RESERVED_LOCK byte.
windows下面的封锁函数,从程序中可以看到,封锁函数在获得不到封锁时,直接返回sqlITE_BUSY,而不是挂起线程,等待别的线程释放锁,这和一般的关系数据库还是有区别的,同时也就需要用户的程序进行判断,不能忽视申请没成功的情况
static int winLock(sqlite3_file *id,int locktype){
int rc = sqlITE_OK; /* Return code from subroutines */
int res = 1; /* Result of a windows lock call */
int newLocktype; /* Set pFile->locktype to this value before exiting */
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
winFile *pFile = (winFile*)id;
DWORD error = NO_ERROR;
assert( id!=0 );
OSTRACE5("LOCK %d %d was %d(%d)/n",
pFile->h,locktype,pFile->locktype,pFile->sharedLockByte);
/* If there is already a lock of this type or more restrictive on the
** OsFile,do nothing. Don't use the end_lock: exit path,as
** sqlite3OsEnterMutex() hasn't been called yet.
*/
if( pFile->locktype>=locktype ){
return sqlITE_OK;
}
/* Make sure the locking sequence is correct
*/
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
assert( locktype!=PENDING_LOCK );
assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
/* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
** a SHARED lock. If we are acquiring a SHARED lock,the acquisition of
** the PENDING_LOCK byte is temporary.
*/
newLocktype = pFile->locktype;
if( (pFile->locktype==NO_LOCK)
|| ( (locktype==EXCLUSIVE_LOCK)
&& (pFile->locktype==RESERVED_LOCK))
){
int cnt = 3;
while( cnt-->0 && (res = LockFile(pFile->h,PENDING_BYTE,1,0))==0 ){
/* Try 3 times to get the pending lock. The pending lock might be
** held by another reader process who will release it momentarily.
*/
OSTRACE2("could not get a PENDING lock. cnt=%d/n",cnt);
Sleep(1);
}
gotPendingLock = res;
if( !res ){
error = GetLastError();
}
}
/* Acquire a shared lock
*/
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
res = getReadLock(pFile);
if( res ){
newLocktype = SHARED_LOCK;
}else{
error = GetLastError();
}
}
/* Acquire a RESERVED lock
*/
if( locktype==RESERVED_LOCK && res ){
assert( pFile->locktype==SHARED_LOCK );
res = LockFile(pFile->h,RESERVED_BYTE,0);
if( res ){
newLocktype = RESERVED_LOCK;
}else{
error = GetLastError();
}
}
/* Acquire a PENDING lock
*/
if( locktype==EXCLUSIVE_LOCK && res ){
newLocktype = PENDING_LOCK;
gotPendingLock = 0;
}
/* Acquire an EXCLUSIVE lock
*/
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
res = unlockReadLock(pFile);
OSTRACE2("unreadlock = %d/n",res);
res = LockFile(pFile->h,SHARED_FIRST,SHARED_SIZE,0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
error = GetLastError();
OSTRACE2("error-code = %d/n",error);
getReadLock(pFile);
}
}
/* If we are holding a PENDING lock that ought to be released,then
** release it now.
*/
if( gotPendingLock && locktype==SHARED_LOCK ){
UnlockFile(pFile->h,0);
}
/* Update the state of the lock has held in the file descriptor then ** return the appropriate result code. */ if( res ){ rc = sqlITE_OK; }else{ OSTRACE4("LOCK Failed %d trying for %d but got %d/n",pFile->h,newLocktype); pFile->lastErrno = error; rc = sqlITE_BUSY; } pFile->locktype = (u8)newLocktype; return rc;}