QLockFile/Unix: drop the use of fcntl(F_SETLK)
F_SETLK is bad. Explanation in the comment. And flock(2) does work with NFS on Linux, so let's just stick to that, which is simpler. We only use the file locks when we attempt to delete an apparently stale lock: that is, for a lock file that is at least staleLockTime old. Change-Id: I0b48fc8e90304e0dacc3fffd14e908c8c4c9d59b Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
parent
de3f764be8
commit
3399d79773
@ -85,10 +85,6 @@ public:
|
||||
// used in dbusmenu
|
||||
Q_CORE_EXPORT static QString processNameByPid(qint64 pid);
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
static int checkFcntlWorksAfterFlock(const QString &fn);
|
||||
#endif
|
||||
|
||||
QString fileName;
|
||||
#ifdef Q_OS_WIN
|
||||
Qt::HANDLE fileHandle;
|
||||
|
@ -94,79 +94,54 @@ static qint64 qt_write_loop(int fd, const char *data, qint64 len)
|
||||
return pos;
|
||||
}
|
||||
|
||||
int QLockFilePrivate::checkFcntlWorksAfterFlock(const QString &fn)
|
||||
/*
|
||||
* Details about file locking on Unix.
|
||||
*
|
||||
* There are three types of advisory locks on Unix systems:
|
||||
* 1) POSIX process-wide locks using fcntl(F_SETLK)
|
||||
* 2) BSD flock(2) system call
|
||||
* 3) Linux-specific file descriptor locks using fcntl(F_OFD_SETLK)
|
||||
* There's also a mandatory locking feature by POSIX, which is deprecated on
|
||||
* Linux and users are advised not to use it.
|
||||
*
|
||||
* The first problem is that the POSIX API is braindead. POSIX.1-2008 says:
|
||||
*
|
||||
* All locks associated with a file for a given process shall be removed when
|
||||
* a file descriptor for that file is closed by that process or the process
|
||||
* holding that file descriptor terminates.
|
||||
*
|
||||
* The Linux manpage is clearer:
|
||||
*
|
||||
* * If a process closes _any_ file descriptor referring to a file, then all
|
||||
* of the process's locks on that file are released, regardless of the file
|
||||
* descriptor(s) on which the locks were obtained. This is bad: [...]
|
||||
*
|
||||
* * The threads in a process share locks. In other words, a multithreaded
|
||||
* program can't use record locking to ensure that threads don't
|
||||
* simultaneously access the same region of a file.
|
||||
*
|
||||
* So in order to use POSIX locks, we'd need a global mutex that stays locked
|
||||
* while the QLockFile is locked. For that reason, Qt does not use POSIX
|
||||
* advisory locks anymore.
|
||||
*
|
||||
* The next problem is that POSIX leaves undefined the relationship between
|
||||
* locks with fcntl(), flock() and lockf(). In some systems (like the BSDs),
|
||||
* all three use the same record set, while on others (like Linux) the locks
|
||||
* are independent, except if locking over NFS mounts, in which case they're
|
||||
* actually the same. Therefore, it's a very bad idea to mix them in the same
|
||||
* process.
|
||||
*
|
||||
* We therefore use only flock(2).
|
||||
*/
|
||||
|
||||
static bool setNativeLocks(int fd)
|
||||
{
|
||||
#ifndef QT_NO_TEMPORARYFILE
|
||||
QTemporaryFile file(fn);
|
||||
if (!file.open())
|
||||
return 0;
|
||||
const int fd = file.d_func()->engine()->handle();
|
||||
#if defined(LOCK_EX) && defined(LOCK_NB)
|
||||
if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
|
||||
return 0;
|
||||
#endif
|
||||
struct flock flockData;
|
||||
flockData.l_type = F_WRLCK;
|
||||
flockData.l_whence = SEEK_SET;
|
||||
flockData.l_start = 0;
|
||||
flockData.l_len = 0; // 0 = entire file
|
||||
flockData.l_pid = getpid();
|
||||
if (fcntl(fd, F_SETLK, &flockData) == -1) // for networked filesystems
|
||||
return 0;
|
||||
return 1;
|
||||
return false;
|
||||
#else
|
||||
Q_UNUSED(fn);
|
||||
return 0;
|
||||
Q_UNUSED(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Cache the result of checkFcntlWorksAfterFlock for each directory a lock
|
||||
// file is created in because in some filesystems, like NFS, both locks
|
||||
// are the same. This does not take into account a filesystem changing.
|
||||
// QCache is set to hold a maximum of 10 entries, this is to avoid unbounded
|
||||
// growth, this is caching directories of files and it is assumed a low number
|
||||
// will be sufficient.
|
||||
typedef QCache<QString, bool> CacheType;
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(CacheType, fcntlOK, (10));
|
||||
static QBasicMutex fcntlLock;
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Checks that the OS isn't using POSIX locks to emulate flock().
|
||||
\macos is one of those.
|
||||
*/
|
||||
static bool fcntlWorksAfterFlock(const QString &fn)
|
||||
{
|
||||
QMutexLocker lock(&fcntlLock);
|
||||
if (fcntlOK.isDestroyed())
|
||||
return QLockFilePrivate::checkFcntlWorksAfterFlock(fn);
|
||||
bool *worksPtr = fcntlOK->object(fn);
|
||||
if (worksPtr)
|
||||
return *worksPtr;
|
||||
|
||||
const bool val = QLockFilePrivate::checkFcntlWorksAfterFlock(fn);
|
||||
worksPtr = new bool(val);
|
||||
fcntlOK->insert(fn, worksPtr);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static bool setNativeLocks(const QString &fileName, int fd)
|
||||
{
|
||||
#if defined(LOCK_EX) && defined(LOCK_NB)
|
||||
if (flock(fd, LOCK_EX | LOCK_NB) == -1) // other threads, and other processes on a local fs
|
||||
return false;
|
||||
#endif
|
||||
struct flock flockData;
|
||||
flockData.l_type = F_WRLCK;
|
||||
flockData.l_whence = SEEK_SET;
|
||||
flockData.l_start = 0;
|
||||
flockData.l_len = 0; // 0 = entire file
|
||||
flockData.l_pid = getpid();
|
||||
if (fcntlWorksAfterFlock(QDir::cleanPath(QFileInfo(fileName).absolutePath()) + QString('/'))
|
||||
&& fcntl(fd, F_SETLK, &flockData) == -1) { // for networked filesystems
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -193,7 +168,7 @@ QLockFile::LockError QLockFilePrivate::tryLock_sys()
|
||||
}
|
||||
}
|
||||
// Ensure nobody else can delete the file while we have it
|
||||
if (!setNativeLocks(fileName, fd)) {
|
||||
if (!setNativeLocks(fd)) {
|
||||
const int errnoSaved = errno;
|
||||
qWarning() << "setNativeLocks failed:" << qt_error_string(errnoSaved);
|
||||
}
|
||||
@ -224,7 +199,7 @@ bool QLockFilePrivate::removeStaleLock()
|
||||
const int fd = qt_safe_open(lockFileName.constData(), O_WRONLY, 0666);
|
||||
if (fd < 0) // gone already?
|
||||
return false;
|
||||
bool success = setNativeLocks(fileName, fd) && (::unlink(lockFileName) == 0);
|
||||
bool success = setNativeLocks(fd) && (::unlink(lockFileName) == 0);
|
||||
close(fd);
|
||||
return success;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user