381bool QLockFile::tryLock(std::chrono::milliseconds timeout)
383 using namespace std::chrono_literals;
384 constexpr int Perturbation = 4096 * 1024;
385 using Msec = std::chrono::milliseconds;
388 QLockFilePrivate::LockFileInfo current(QLockFilePrivate::LockFileInfo::Current{});
391 QDeadlineTimer timer(timeout < 0ms ? Msec::max() : timeout);
395 auto sleepTime = 100ms +
396 QRandomGenerator::global()->bounded(-Perturbation, Perturbation) * 1ns;
399 d->lockError = d->tryLock_sys(current);
400 switch (d->lockError) {
404 case PermissionError:
407 case LockFailedError:
408 if (!d->isLocked && d->isApparentlyStale(current)) {
409 if (Q_UNLIKELY(QFileInfo(d->fileName).lastModified(QTimeZone::UTC) > QDateTime::currentDateTimeUtc()))
410 qInfo(
"QLockFile: Lock file '%ls' has a modification time in the future", qUtf16Printable(d->fileName));
413 QLockFile rmlock(d->fileName +
".rmlock"_L1);
414 if (rmlock.tryLock()) {
415 if (d->isApparentlyStale(current) && d->removeStaleLock())
422 auto remainingTime = timer.remainingTimeAsDuration();
423 if (remainingTime == 0ms)
426 if (sleepTime > remainingTime)
427 sleepTime = remainingTime;
429 QThread::sleep(sleepTime);
471bool QLockFile::getLockInfo(qint64 *pid, QString *hostname, QString *appname)
const
473 Q_D(
const QLockFile);
474 std::optional opt = QLockFilePrivate::getLockInfo_helper(d->fileName);
477 QLockFilePrivate::LockFileInfo &info = *opt;
481 *hostname = info.hostname;
483 *appname = info.appname;
505QLockFilePrivate::LockFileInfo::LockFileInfo(Current)
506 : pid(QCoreApplication::applicationPid()),
507 appname(processNameByPid(pid)),
508 hostname(machineName()),
509 hostid(QSysInfo::machineUniqueId()),
510 bootid(QSysInfo::bootUniqueId())
516 std::optional<QLockFilePrivate::LockFileInfo> info;
518 if (!reader.open(fd, QFile::ReadOnly | QFile::Text, QFile::AutoCloseHandle)) {
524 QByteArray pidLine = reader.readLine();
526 qint64 pid = pidLine.toLongLong(&ok);
529 QByteArray appNameLine = reader.readLine();
531 QByteArray hostNameLine = reader.readLine();
532 hostNameLine.chop(1);
535 QByteArray hostId = reader.readLine();
537 QByteArray bootId = reader.readLine();
541 info->appname = QString::fromUtf8(appNameLine);
542 info->hostname = QString::fromUtf8(hostNameLine);
543 info->hostid =
std::move(hostId);
544 info->bootid =
std::move(bootId);
555bool QLockFilePrivate::isApparentlyStale(
const LockFileInfo ¤t)
const
557 QUniqueFileDescriptorHandle fd(openNewFileDescriptor(fileName));
562 using namespace std::chrono;
563 if (staleLockTime > 0ms) {
564 QFileSystemMetaData md;
565 std::ignore = QFileSystemEngine::fillMetaData(fd.get(), md);
566 const QDateTime lastMod = md.fileTime(QFile::FileModificationTime);
567 const milliseconds age{lastMod.msecsTo(QDateTime::currentDateTimeUtc())};
568 if (abs(age) > staleLockTime)
572 if (std::optional opt = getLockInfo(fd.release())) {
573 LockFileInfo &info = *opt;
574 bool sameHost = info.hostname.isEmpty() || info.hostname == current.hostname;
575 if (!info.hostid.isEmpty()) {
577 if (!current.hostid.isEmpty())
578 sameHost = (current.hostid == info.hostid);
582 if (!info.bootid.isEmpty()) {
584 if (info.bootid != current.bootid)
587 if (!isProcessRunning(info.pid, info.appname))