2021SC@SDUSC
上次讲解了实现sqlite3_io_method对象指定的I/O方法的一组例程。接来下一组例程将执行检查所有进程是否在指定文件上有特定锁、设置锁、解锁等操作。
首先我们先了解LockFile和LockFileEX函数。过去,SQLite使用了LockFile和LockFileEx函数。当使用LockFile函数时,如果无法获得锁,则总是预期它会立刻失败。而且,它总是期望获得独占锁——但这并不是我们想要的。于是现在SQLite从不调用LockFileEx函数:如果无法获得锁,则不希望调用立即失败。
获取一个读锁
static int winGetReadLock(winFile *pFile){
int res;
OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
if( osIsNT() ){
#if SQLITE_OS_WINCE
res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
#else
res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
SHARED_SIZE, 0);
#endif
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
int lk;
sqlite3_randomness(sizeof(lk), &lk);
pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
#endif
if( res == 0 ){
pFile->lastErrno = osGetLastError();
/* 无需记录锁定失败 */
}
OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
撤销读锁
static int winUnlockReadLock(winFile *pFile){
int res;
DWORD lastErrno;
OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
if( osIsNT() ){
res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
#endif
if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
"winUnlockReadLock", pFile->zPath);
}
OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
用以下参数锁类型指定的锁来锁定文件:
(1) SHARED_LOCK
(2) RESERVED_LOCK
(3) PENDING_LOCK
(4) EXCLUSIVE_LOCK
确保锁顺序正确:
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
assert( locktype!=PENDING_LOCK );
assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
有时,当请求一个锁状态时,在中间插入额外的锁状态。锁定可能会在后面的转换中失败,下图显示了允许的转换和插入的中间状态:
解锁->共享锁
共享锁->保留锁
共享锁->(挂起锁)->独占锁
保留锁->挂机锁->独占锁
挂起锁->独占锁
下面这个例程只会增加一个锁。WinUnlock()例程一次擦除所有锁,并直接将我们返回到锁定级别0。(而不是一步一步降低锁定级别)
static int winLock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK; /* 从子例程返回代码*/
int res = 1; /* Windows锁调用的结果 */
int newLocktype; /* 在退出前将pFile->locktype设置为这个值 */
int gotPendingLock = 0; /* 此时若我们获取了挂起锁则返回True */
winFile *pFile = (winFile*)id;
DWORD lastErrno = NO_ERROR;
assert( id!=0 );
OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n",
pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
获取一个共享锁
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
res = winGetReadLock(pFile);
if( res ){
newLocktype = SHARED_LOCK;
}else{
lastErrno = osGetLastError();
}
}
获取一个保留锁
if( locktype==RESERVED_LOCK && res ){
assert( pFile->locktype==SHARED_LOCK );
res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0);
if( res ){
newLocktype = RESERVED_LOCK;
}else{
lastErrno = osGetLastError();
}
}
获取一个挂起锁
if( locktype==EXCLUSIVE_LOCK && res ){
newLocktype = PENDING_LOCK;
gotPendingLock = 0;
}
获取一个独占锁
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
res = winUnlockReadLock(pFile);
res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
SHARED_SIZE, 0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
lastErrno = osGetLastError();
winGetReadLock(pFile);
}
}
更新文件描述符中的锁状态,然后返回适当的结果代码。
if( res ){
rc = SQLITE_OK;
}else{
pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
pFile->h, locktype, newLocktype));
}
pFile->locktype = (u8)newLocktype;
OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n",
pFile->h, pFile->locktype, sqlite3ErrName(rc)));
return rc;
}
接下来这个例程检查此进程或任何其他进程是否在指定的文件上持有保留锁。如果持有这样的锁,则返回非零,否则返回零。
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
int res;
winFile *pFile = (winFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut));
assert( id!=0 );
if( pFile->locktype>=RESERVED_LOCK ){
res = 1;
OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
}else{
res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0);
if( res ){
winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
res = !res;
OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res));
}
*pResOut = res;
OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
pFile->h, pResOut, *pResOut));
return SQLITE_OK;
}
/*将文件描述符id上的锁定级别降低为锁类型。锁类型必须是NOLOCK或SharedLock。如果文件描述符的锁定级别已经处于或低于所请求的锁定级别,则此例程为非操作。*/
static int winUnlock(sqlite3_file *id, int locktype){
int type;
winFile *pFile = (winFile*)id;
int rc = SQLITE_OK;
assert( pFile!=0 );
assert( locktype<=SHARED_LOCK );
OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
}
pFile->locktype = (u8)locktype;
OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n",
pFile->h, pFile->locktype, sqlite3ErrName(rc)));
return rc;
}