📄 os_unix.c
字号:
struct flock lock; int s; assert( pFile ); OSTRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", pFile->h, locktypeName(locktype), locktypeName(pFile->locktype), locktypeName(pLock->locktype), pLock->cnt , getpid()); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the end_lock: exit path, as ** unixEnterMutex() hasn't been called yet. */ if( pFile->locktype>=locktype ){ OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h, locktypeName(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 ); /* This mutex is needed because pFile->pLock is shared across threads */ unixEnterMutex(); /* Make sure the current thread owns the pFile. */ rc = transferOwnership(pFile); if( rc!=SQLITE_OK ){ unixLeaveMutex(); return rc; } pLock = pFile->pLock; /* If some thread using this PID has a lock via a different unixFile* ** handle that precludes the requested lock, return BUSY. */ if( (pFile->locktype!=pLock->locktype && (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK)) ){ rc = SQLITE_BUSY; goto end_lock; } /* If a SHARED lock is requested, and some thread using this PID already ** has a SHARED or RESERVED lock, then increment reference counts and ** return SQLITE_OK. */ if( locktype==SHARED_LOCK && (pLock->locktype==SHARED_LOCK || pLock->locktype==RESERVED_LOCK) ){ assert( locktype==SHARED_LOCK ); assert( pFile->locktype==0 ); assert( pLock->cnt>0 ); pFile->locktype = SHARED_LOCK; pLock->cnt++; pFile->pOpen->nLock++; goto end_lock; } lock.l_len = 1L; lock.l_whence = SEEK_SET; /* A PENDING lock is needed before acquiring a SHARED lock and before ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will ** be released. */ if( locktype==SHARED_LOCK || (locktype==EXCLUSIVE_LOCK && pFile->locktype<PENDING_LOCK) ){ lock.l_type = (locktype==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; s = fcntl(pFile->h, F_SETLK, &lock); if( s==(-1) ){ int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_lock; } } /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ if( locktype==SHARED_LOCK ){ int tErrno = 0; assert( pLock->cnt==0 ); assert( pLock->locktype==0 ); /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ tErrno = errno; } /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){ if( s != -1 ){ /* This could happen with a network mount */ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_lock; } } if( s==(-1) ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } }else{ pFile->locktype = SHARED_LOCK; pFile->pOpen->nLock++; pLock->cnt = 1; } }else if( locktype==EXCLUSIVE_LOCK && pLock->cnt>1 ){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file ** already. */ assert( 0!=pFile->locktype ); lock.l_type = F_WRLCK; switch( locktype ){ case RESERVED_LOCK: lock.l_start = RESERVED_BYTE; break; case EXCLUSIVE_LOCK: lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; break; default: assert(0); } s = fcntl(pFile->h, F_SETLK, &lock); if( s==(-1) ){ int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } } } #ifndef NDEBUG /* Set up the transaction-counter change checking flags when ** transitioning from a SHARED to a RESERVED lock. The change ** from SHARED to RESERVED marks the beginning of a normal ** write operation (not a hot journal rollback). */ if( rc==SQLITE_OK && pFile->locktype<=SHARED_LOCK && locktype==RESERVED_LOCK ){ pFile->transCntrChng = 0; pFile->dbUpdate = 0; pFile->inNormalWrite = 1; }#endif if( rc==SQLITE_OK ){ pFile->locktype = locktype; pLock->locktype = locktype; }else if( locktype==EXCLUSIVE_LOCK ){ pFile->locktype = PENDING_LOCK; pLock->locktype = PENDING_LOCK; }end_lock: unixLeaveMutex(); OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype), rc==SQLITE_OK ? "ok" : "failed"); return rc;}/*** Lower the locking level on file descriptor pFile to locktype. locktype** must be either NO_LOCK or SHARED_LOCK.**** If the locking level of the file descriptor is already at or below** the requested locking level, this routine is a no-op.*/static int unixUnlock(sqlite3_file *id, int locktype){ struct unixLockInfo *pLock; struct flock lock; int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; int h; assert( pFile ); OSTRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype, pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid()); assert( locktype<=SHARED_LOCK ); if( pFile->locktype<=locktype ){ return SQLITE_OK; } if( CHECK_THREADID(pFile) ){ return SQLITE_MISUSE; } unixEnterMutex(); h = pFile->h; pLock = pFile->pLock; assert( pLock->cnt!=0 ); if( pFile->locktype>SHARED_LOCK ){ assert( pLock->locktype==pFile->locktype ); SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0);#ifndef NDEBUG /* When reducing a lock such that other processes can start ** reading the database file again, make sure that the ** transaction counter was updated if any part of the database ** file changed. If the transaction counter is not updated, ** other connections to the same file might not realize that ** the file has changed and hence might not know to flush their ** cache. The use of a stale cache can lead to database corruption. */ assert( pFile->inNormalWrite==0 || pFile->dbUpdate==0 || pFile->transCntrChng==1 ); pFile->inNormalWrite = 0;#endif if( locktype==SHARED_LOCK ){ lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; if( fcntl(h, F_SETLK, &lock)==(-1) ){ int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pLock->locktype = SHARED_LOCK; }else{ int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } } if( locktype==NO_LOCK ){ struct unixOpenCnt *pOpen; /* Decrement the shared lock counter. Release the lock using an ** OS call only when all threads in this same process have released ** the lock. */ pLock->cnt--; if( pLock->cnt==0 ){ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pLock->locktype = NO_LOCK; }else{ int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } pLock->cnt = 1; goto end_unlock; } } /* Decrement the count of locks against this same file. When the ** count reaches zero, close any other file descriptors whose close ** was deferred because of outstanding locks. */ if( rc==SQLITE_OK ){ pOpen = pFile->pOpen; pOpen->nLock--; assert( pOpen->nLock>=0 ); if( pOpen->nLock==0 && pOpen->nPending>0 ){ int i; for(i=0; i<pOpen->nPending; i++){ /* close pending fds, but if closing fails don't free the array ** assign -1 to the successfully closed descriptors and record the ** error. The next attempt to unlock will try again. */ if( pOpen->aPending[i] < 0 ) continue; if( close(pOpen->aPending[i]) ){ pFile->lastErrno = errno; rc = SQLITE_IOERR_CLOSE; }else{ pOpen->aPending[i] = -1; } } if( rc==SQLITE_OK ){ sqlite3_free(pOpen->aPending); pOpen->nPending = 0; pOpen->aPending = 0; } } } } end_unlock: unixLeaveMutex(); if( rc==SQLITE_OK ) pFile->locktype = locktype; return rc;}/*** This function performs the parts of the "close file" operation ** common to all locking schemes. It closes the directory and file** handles, if they are valid, and sets all fields of the unixFile** structure to 0.**** It is *not* necessary to hold the mutex when this routine is called,** even on VxWorks. A mutex will be acquired on VxWorks by the** vxworksReleaseFileId() routine.*/static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; if( pFile ){ if( pFile->dirfd>=0 ){ int err = close(pFile->dirfd); if( err ){ pFile->lastErrno = errno; return SQLITE_IOERR_DIR_CLOSE; }else{ pFile->dirfd=-1; } } if( pFile->h>=0 ){ int err = close(pFile->h); if( err ){ pFile->lastErrno = errno; return SQLITE_IOERR_CLOSE; } }#if OS_VXWORKS if( pFile->pId ){ if( pFile->isDelete ){ unlink(pFile->pId->zCanonicalName); } vxworksReleaseFileId(pFile->pId); pFile->pId = 0; }#endif OSTRACE2("CLOSE %-3d\n", pFile->h); OpenCounter(-1); memset(pFile, 0, sizeof(unixFile)); } return SQLITE_OK;}/*** Close a file.*/static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; if( id ){ unixFile *pFile = (unixFile *)id; unixUnlock(id, NO_LOCK); unixEnterMutex(); if( pFile->pOpen && pFile->pOpen->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pOpen->aPending. It will be automatically closed when ** the last lock is cleared. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -