QLockFile: tryLock(): use chrono first

I.e. tryLock(chrono::milliseconds) shouldn't call the int overload as
that truncates the timeout (milliseconds is typically int64_t).

Add a note to the tryLock(millisecons) docs that passing
milliseconds::max() will make it wait forever to obtain the lock.

Task-number: QTBUG-110059
Change-Id: Ib48d9b1b117ce816348625331543d6ba8a788973
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ahmad Samir 2023-02-27 21:50:23 +02:00
parent 425e635ecd
commit 5cea5fc80b
3 changed files with 65 additions and 38 deletions

View File

@ -216,7 +216,7 @@ bool QLockFile::isLocked() const
*/
bool QLockFile::lock()
{
return tryLock(-1);
return tryLock(std::chrono::milliseconds::max());
}
/*!
@ -241,10 +241,42 @@ bool QLockFile::lock()
*/
bool QLockFile::tryLock(int timeout)
{
return tryLock(std::chrono::milliseconds{ timeout });
}
/*! \fn bool QLockFile::tryLock(std::chrono::milliseconds timeout)
\overload
\since 6.2
Attempts to create the lock file. This function returns \c true if the
lock was obtained; otherwise it returns \c false. If another process (or
another thread) has created the lock file already, this function will
wait for at most \a timeout for the lock file to become available.
If the lock was obtained, it must be released with unlock()
before another process (or thread) can successfully lock it.
Calling this function multiple times on the same lock from the same
thread without unlocking first is not allowed, this function will
\e always return false when attempting to lock the file recursively.
\sa lock(), unlock()
*/
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
bool QLockFile::tryLock(std::chrono::milliseconds timeout)
#else
bool QLockFile::tryLock_impl(std::chrono::milliseconds timeout)
#endif
{
using namespace std::chrono_literals;
using Msec = std::chrono::milliseconds;
Q_D(QLockFile);
QDeadlineTimer timer(qMax(timeout, -1)); // QDT only takes -1 as "forever"
int sleepTime = 100;
forever {
QDeadlineTimer timer(timeout < 0ms ? Msec::max() : timeout);
Msec sleepTime = 100ms;
while (true) {
d->lockError = d->tryLock_sys();
switch (d->lockError) {
case NoError:
@ -268,39 +300,21 @@ bool QLockFile::tryLock(int timeout)
break;
}
int remainingTime = timer.remainingTime();
if (remainingTime == 0)
auto remainingTime = std::chrono::duration_cast<Msec>(timer.remainingTimeAsDuration());
if (remainingTime == 0ms)
return false;
else if (uint(sleepTime) > uint(remainingTime))
if (sleepTime > remainingTime)
sleepTime = remainingTime;
QThread::msleep(sleepTime);
if (sleepTime < 5 * 1000)
QThread::sleep(sleepTime);
if (sleepTime < 5s)
sleepTime *= 2;
}
// not reached
return false;
}
/*! \fn bool QLockFile::tryLock(std::chrono::milliseconds timeout)
\overload
\since 6.2
Attempts to create the lock file. This function returns \c true if the
lock was obtained; otherwise it returns \c false. If another process (or
another thread) has created the lock file already, this function will
wait for at most \a timeout for the lock file to become available.
If the lock was obtained, it must be released with unlock()
before another process (or thread) can successfully lock it.
Calling this function multiple times on the same lock from the same
thread without unlocking first is not allowed, this function will
\e always return false when attempting to lock the file recursively.
\sa lock(), unlock()
*/
/*!
\fn void QLockFile::unlock()
Releases the lock, by deleting the lock file.

View File

@ -22,13 +22,20 @@ public:
QString fileName() const;
bool lock();
bool tryLock(int timeout = 0);
bool tryLock(int timeout);
void unlock();
void setStaleLockTime(int);
int staleLockTime() const;
bool tryLock(std::chrono::milliseconds timeout) { return tryLock(int(timeout.count())); }
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
bool tryLock(std::chrono::milliseconds timeout = std::chrono::milliseconds::zero());
#else
bool tryLock(std::chrono::milliseconds timeout = std::chrono::milliseconds::zero())
{
return tryLock_impl(timeout);
}
#endif
void setStaleLockTime(std::chrono::milliseconds value) { setStaleLockTime(int(value.count())); }
@ -55,6 +62,10 @@ protected:
private:
Q_DECLARE_PRIVATE(QLockFile)
Q_DISABLE_COPY(QLockFile)
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool tryLock_impl(std::chrono::milliseconds timeout);
#endif
};
QT_END_NAMESPACE

View File

@ -26,6 +26,8 @@
#include <private/qlockfile_p.h> // for getLockFileHandle()
using namespace std::chrono_literals;
class tst_QLockFile : public QObject
{
Q_OBJECT
@ -96,7 +98,7 @@ void tst_QLockFile::lockUnlock()
QVERIFY(lockFile.getLockInfo(&pid, &hostname, &appname));
QCOMPARE(pid, QCoreApplication::applicationPid());
QCOMPARE(appname, qAppName());
QVERIFY(!lockFile.tryLock(200));
QVERIFY(!lockFile.tryLock(200ms));
QCOMPARE(int(lockFile.error()), int(QLockFile::LockFailedError));
// Unlock deletes the lock file
@ -341,8 +343,8 @@ void tst_QLockFile::staleLongLockFromBusyProcess()
QTRY_VERIFY(QFile::exists(fileName));
QLockFile secondLock(fileName);
secondLock.setStaleLockTime(0);
QVERIFY(!secondLock.tryLock(100)); // never stale
secondLock.setStaleLockTime(0ms);
QVERIFY(!secondLock.tryLock(100ms)); // never stale
QCOMPARE(int(secondLock.error()), int(QLockFile::LockFailedError));
qint64 pid;
QTRY_VERIFY(secondLock.getLockInfo(&pid, NULL, NULL));
@ -510,8 +512,8 @@ void tst_QLockFile::corruptedLockFile()
}
QLockFile secondLock(fileName);
secondLock.setStaleLockTime(100);
QVERIFY(secondLock.tryLock(10000));
secondLock.setStaleLockTime(100ms);
QVERIFY(secondLock.tryLock(10s));
QCOMPARE(int(secondLock.error()), int(QLockFile::NoError));
}
@ -564,7 +566,7 @@ void tst_QLockFile::hostnameChange()
{
// we should fail to lock
QLockFile lock2(lockFile);
QVERIFY(!lock2.tryLock(1000));
QVERIFY(!lock2.tryLock(1s));
}
}
@ -591,7 +593,7 @@ void tst_QLockFile::differentMachines()
{
// we should fail to lock
QLockFile lock2(lockFile);
QVERIFY(!lock2.tryLock(1000));
QVERIFY(!lock2.tryLock(1s));
}
}
@ -620,7 +622,7 @@ void tst_QLockFile::reboot()
f.close();
// we should succeed in locking
QVERIFY(lock1.tryLock(0));
QVERIFY(lock1.tryLock(0ms));
}
bool tst_QLockFile::overwritePidInLockFile(const QString &filePath, qint64 pid)