App: Replace QtLockedFile usage with QLockFile

We had an import of QtLockedFile from 2008. On Windows we could see a
lot of QtLockedFile cycles for qtcreator.exe. The 2008 version was using
a semaphore.

I've tried the latest QtSolutions version from 2016, which was using
only mutexes. qtcreator.exe would still show up lots of cycles for
QtLockedFile.

Then switched to QLockFile (added in Qt 5.1), and I couldn't see any
more CPU usage!

Task-number: QTCREATORBUG-29416
Change-Id: Ibfd102f3e90de39c807ff1140e597b79a7b6ca8e
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
Cristian Adam
2023-08-09 13:48:24 +02:00
parent 6a30ebd01e
commit 1fca05959f
13 changed files with 15 additions and 566 deletions

View File

@@ -40,7 +40,6 @@ QtcProduct {
: ["$ORIGIN/../" + qtc.libDirName + "/qtcreator"]
cpp.includePaths: [
project.sharedSourcesDir + "/qtsingleapplication",
project.sharedSourcesDir + "/qtlockedfile",
]
cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["Foundation"] : [])
@@ -58,7 +57,6 @@ QtcProduct {
"../shared/qtsingleapplication/qtsingleapplication.cpp",
"../shared/qtsingleapplication/qtlocalpeer.h",
"../shared/qtsingleapplication/qtlocalpeer.cpp",
"../shared/qtlockedfile/qtlockedfile.cpp",
"../tools/qtcreatorcrashhandler/crashhandlersetup.cpp",
"../tools/qtcreatorcrashhandler/crashhandlersetup.h"
]
@@ -84,22 +82,6 @@ QtcProduct {
qbs.installDir: "bin"
}
Group {
name: "QtLockedFile_unix"
condition: qbs.targetOS.contains("unix")
files: [
"../shared/qtlockedfile/qtlockedfile_unix.cpp"
]
}
Group {
name: "QtLockedFile_win"
condition: qbs.targetOS.contains("windows")
files: [
"../shared/qtlockedfile/qtlockedfile_win.cpp"
]
}
Group {
name: "main_macos"
condition: qbs.targetOS.contains("macos")

View File

@@ -1,7 +1,6 @@
add_subdirectory(designerintegrationv2)
add_subdirectory(proparser)
add_subdirectory(qtsingleapplication)
add_subdirectory(qtlockedfile)
add_subdirectory(help)
add_subdirectory(registryaccess)

View File

@@ -1,17 +0,0 @@
if (WIN32)
set(OS_SOURCES qtlockedfile_win.cpp)
else()
set(OS_SOURCES qtlockedfile_unix.cpp)
endif()
add_library(shared_qtlockedfile STATIC ${OS_SOURCES} qtlockedfile.cpp qtlockedfile.h)
target_link_libraries(shared_qtlockedfile Qt::Core)
target_include_directories(shared_qtlockedfile PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
if (WIN32)
target_compile_definitions(shared_qtlockedfile PRIVATE
"QT_QTLOCKEDFILE_EXPORT=__declspec(dllexport)" _UNICODE UNICODE)
endif()
if (WITH_SANITIZE)
qtc_enable_sanitize(shared_qtlockedfile ${SANITIZE_FLAGS})
endif()

View File

@@ -1,10 +0,0 @@
This is the src directory of the QtLockedFile
solution integrated over from addons/main/utils/qtlockedfile/src .
namespace.patch was applied to introduce the SharedTools namespace.
It is required by the QtSingleApplication solution.
History:
16.05.2008 Integrated

View File

@@ -1,70 +0,0 @@
--- qtlockedfile.cpp 1970-01-01 01:00:00.000000000
+++ qtlockedfile.cpp 2008/05/16 10:51:19.000000000
@@ -1,5 +1,7 @@
#include "qtlockedfile.h"
+namespace SharedTools {
+
/*!
\class QtLockedFile
@@ -123,3 +125,5 @@
Destroys the \e QtLockedFile object. If any locks were held, they are released.
*/
+
+}
--- qtlockedfile.h 1970-01-01 01:00:00.000000000
+++ qtlockedfile.h 2008/05/16 10:51:19.000000000
@@ -19,6 +19,8 @@
# define QT_QTLOCKEDFILE_EXPORT
#endif
+namespace SharedTools {
+
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
{
public:
@@ -41,4 +43,6 @@
LockMode m_lock_mode;
};
+}
+
#endif
--- qtlockedfile_unix.cpp 1970-01-01 01:00:00.000000000
+++ qtlockedfile_unix.cpp 2008/05/16 10:51:19.000000000
@@ -5,6 +5,8 @@
#include "qtlockedfile.h"
+namespace SharedTools {
+
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
@@ -73,3 +75,4 @@
unlock();
}
+}
--- qtlockedfile_win.cpp 1970-01-01 01:00:00.000000000
+++ qtlockedfile_win.cpp 2008/05/16 10:51:19.000000000
@@ -2,6 +2,8 @@
#include <qt_windows.h>
#include <QtCore/QFileInfo>
+namespace SharedTools {
+
#define SEMAPHORE_PREFIX "QtLockedFile semaphore "
#define MUTEX_PREFIX "QtLockedFile mutex "
#define SEMAPHORE_MAX 100
@@ -168,3 +170,4 @@
}
}
+}

View File

@@ -1,132 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qtlockedfile.h"
namespace SharedTools {
/*!
\class QtLockedFile
\brief The QtLockedFile class extends QFile with advisory locking functions.
A file may be locked in read or write mode. Multiple instances of
\e QtLockedFile, created in multiple processes running on the same
machine, may have a file locked in read mode. Exactly one instance
may have it locked in write mode. A read and a write lock cannot
exist simultaneously on the same file.
The file locks are advisory. This means that nothing prevents
another process from manipulating a locked file using QFile or
file system functions offered by the OS. Serialization is only
guaranteed if all processes that access the file use
QtLockedFile. Also, while holding a lock on a file, a process
must not open the same file again (through any API), or locks
can be unexpectedly lost.
The lock provided by an instance of \e QtLockedFile is released
whenever the program terminates. This is true even when the
program crashes and no destructors are called.
*/
/*! \enum QtLockedFile::LockMode
This enum describes the available lock modes.
\value ReadLock A read lock.
\value WriteLock A write lock.
\value NoLock Neither a read lock nor a write lock.
*/
/*!
Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way
as \e QFile::QFile().
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile()
: QFile()
{
#ifdef Q_OS_WIN
m_semaphore_hnd = 0;
m_mutex_hnd = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in
the same way as \e QFile::QFile(const QString&).
\sa QFile::QFile()
*/
QtLockedFile::QtLockedFile(const QString &name)
: QFile(name)
{
#ifdef Q_OS_WIN
m_semaphore_hnd = 0;
m_mutex_hnd = 0;
#endif
m_lock_mode = NoLock;
}
/*!
Returns \e true if this object has a in read or write lock;
otherwise returns \e false.
\sa lockMode()
*/
bool QtLockedFile::isLocked() const
{
return m_lock_mode != NoLock;
}
/*!
Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock.
\sa isLocked()
*/
QtLockedFile::LockMode QtLockedFile::lockMode() const
{
return m_lock_mode;
}
/*!
\fn bool QtLockedFile::lock(LockMode mode, bool block = true)
Obtains a lock of type \a mode.
If \a block is true, this
function will block until the lock is acquired. If \a block is
false, this function returns \e false immediately if the lock cannot
be acquired.
If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock
is first released and then a new lock is obtained.
This function returns \e true if, after it executes, the file is locked by this object,
and \e false otherwise.
\sa unlock(), isLocked(), lockMode()
*/
/*!
\fn bool QtLockedFile::unlock()
Releases a lock.
If the object has no lock, this function returns immediately.
This function returns \e true if, after it executes, the file is not locked by
this object, and \e false otherwise.
\sa lock(), isLocked(), lockMode()
*/
/*!
\fn QtLockedFile::~QtLockedFile()
Destroys the \e QtLockedFile object. If any locks were held, they are released.
*/
} // namespace SharedTools

View File

@@ -1,48 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QFile>
#if defined(Q_OS_WIN)
# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT)
# define QT_QTLOCKEDFILE_EXPORT
# elif defined(QT_QTLOCKEDFILE_IMPORT)
# if defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# endif
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport)
# elif defined(QT_QTLOCKEDFILE_EXPORT)
# undef QT_QTLOCKEDFILE_EXPORT
# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport)
# endif
#else
# define QT_QTLOCKEDFILE_EXPORT
#endif
namespace SharedTools {
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
{
public:
enum LockMode { NoLock = 0, ReadLock, WriteLock };
QtLockedFile();
QtLockedFile(const QString &name);
~QtLockedFile();
bool lock(LockMode mode, bool block = true);
bool unlock();
bool isLocked() const;
LockMode lockMode() const;
private:
#ifdef Q_OS_WIN
Qt::HANDLE m_semaphore_hnd;
Qt::HANDLE m_mutex_hnd;
#endif
LockMode m_lock_mode;
};
} // namespace SharedTools

View File

@@ -1,82 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qtlockedfile.h"
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
namespace SharedTools {
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == NoLock)
return unlock();
if (mode == m_lock_mode)
return true;
if (m_lock_mode != NoLock)
unlock();
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK;
int cmd = block ? F_SETLKW : F_SETLK;
int ret = fcntl(handle(), cmd, &fl);
if (ret == -1) {
if (errno != EINTR && errno != EAGAIN)
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = mode;
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_type = F_UNLCK;
int ret = fcntl(handle(), F_SETLKW, &fl);
if (ret == -1) {
qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno));
return false;
}
m_lock_mode = NoLock;
remove();
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
}
} // namespace SharedTools

View File

@@ -1,170 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qtlockedfile.h"
#include <qt_windows.h>
#include <QFileInfo>
namespace SharedTools {
#define SEMAPHORE_PREFIX "QtLockedFile semaphore "
#define MUTEX_PREFIX "QtLockedFile mutex "
#define SEMAPHORE_MAX 100
static QString errorCodeToString(DWORD errorCode)
{
QString result;
char *data = 0;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
0, errorCode, 0,
(char*)&data, 0, 0);
result = QString::fromLocal8Bit(data);
if (data != 0)
LocalFree(data);
if (result.endsWith(QLatin1Char('\n')))
result.truncate(result.length() - 1);
return result;
}
bool QtLockedFile::lock(LockMode mode, bool block)
{
if (!isOpen()) {
qWarning("QtLockedFile::lock(): file is not opened");
return false;
}
if (mode == m_lock_mode)
return true;
if (m_lock_mode != 0)
unlock();
if (m_semaphore_hnd == 0) {
QFileInfo fi(*this);
QString sem_name = QString::fromLatin1(SEMAPHORE_PREFIX)
+ fi.absoluteFilePath().toLower();
m_semaphore_hnd = CreateSemaphoreW(0, SEMAPHORE_MAX, SEMAPHORE_MAX,
(TCHAR*)sem_name.utf16());
if (m_semaphore_hnd == 0) {
qWarning("QtLockedFile::lock(): CreateSemaphore: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
}
bool gotMutex = false;
int decrement;
if (mode == ReadLock) {
decrement = 1;
} else {
decrement = SEMAPHORE_MAX;
if (m_mutex_hnd == 0) {
QFileInfo fi(*this);
QString mut_name = QString::fromLatin1(MUTEX_PREFIX)
+ fi.absoluteFilePath().toLower();
m_mutex_hnd = CreateMutexW(NULL, FALSE, (TCHAR*)mut_name.utf16());
if (m_mutex_hnd == 0) {
qWarning("QtLockedFile::lock(): CreateMutex: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
}
DWORD res = WaitForSingleObject(m_mutex_hnd, block ? INFINITE : 0);
if (res == WAIT_TIMEOUT)
return false;
if (res == WAIT_FAILED) {
qWarning("QtLockedFile::lock(): WaitForSingleObject (mutex): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
gotMutex = true;
}
for (int i = 0; i < decrement; ++i) {
DWORD res = WaitForSingleObject(m_semaphore_hnd, block ? INFINITE : 0);
if (res == WAIT_TIMEOUT) {
if (i) {
// A failed nonblocking rw locking. Undo changes to semaphore.
if (ReleaseSemaphore(m_semaphore_hnd, i, NULL) == 0) {
qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
// Fall through
}
}
if (gotMutex)
ReleaseMutex(m_mutex_hnd);
return false;
}
if (res != WAIT_OBJECT_0) {
if (gotMutex)
ReleaseMutex(m_mutex_hnd);
qWarning("QtLockedFile::lock(): WaitForSingleObject (semaphore): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
}
m_lock_mode = mode;
if (gotMutex)
ReleaseMutex(m_mutex_hnd);
return true;
}
bool QtLockedFile::unlock()
{
if (!isOpen()) {
qWarning("QtLockedFile::unlock(): file is not opened");
return false;
}
if (!isLocked())
return true;
int increment;
if (m_lock_mode == ReadLock)
increment = 1;
else
increment = SEMAPHORE_MAX;
DWORD ret = ReleaseSemaphore(m_semaphore_hnd, increment, 0);
if (ret == 0) {
qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s",
errorCodeToString(GetLastError()).toLatin1().constData());
return false;
}
m_lock_mode = QtLockedFile::NoLock;
remove();
return true;
}
QtLockedFile::~QtLockedFile()
{
if (isOpen())
unlock();
if (m_mutex_hnd != 0) {
DWORD ret = CloseHandle(m_mutex_hnd);
if (ret == 0) {
qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (mutex): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
}
m_mutex_hnd = 0;
}
if (m_semaphore_hnd != 0) {
DWORD ret = CloseHandle(m_semaphore_hnd);
if (ret == 0) {
qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (semaphore): %s",
errorCodeToString(GetLastError()).toLatin1().constData());
}
m_semaphore_hnd = 0;
}
}
} // namespace SharedTools

View File

@@ -2,7 +2,7 @@ add_library(shared_qtsingleapplication STATIC
qtsingleapplication.cpp qtsingleapplication.h
qtlocalpeer.cpp qtlocalpeer.h
)
target_link_libraries(shared_qtsingleapplication shared_qtlockedfile Qt::Core Qt::Network Qt::Widgets)
target_link_libraries(shared_qtsingleapplication Qt::Core Qt::Network Qt::Widgets)
target_include_directories(shared_qtsingleapplication PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
if (WIN32)
target_compile_definitions(shared_qtsingleapplication PRIVATE "QT_QTSINGLEAPPLICATION_EXPORT=__declspec(dllexport)")

View File

@@ -59,16 +59,15 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
lockFile.reset(new QLockFile(lockName));
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())
if (lockFile->isLocked())
return false;
if (!lockFile.lock(QtLockedFile::WriteLock, false))
if (!lockFile->tryLock())
return true;
if (!QLocalServer::removeServer(socketName))

View File

@@ -3,11 +3,12 @@
#pragma once
#include <qtlockedfile.h>
#include <QDir>
#include <QLocalServer>
#include <QLocalSocket>
#include <QDir>
#include <QLockFile>
#include <QScopedPointer>
namespace SharedTools {
@@ -31,8 +32,8 @@ protected:
QString id;
QString socketName;
QLocalServer* server;
QtLockedFile lockFile;
QLocalServer* server{nullptr};
QScopedPointer<QLockFile> lockFile;
};
} // namespace SharedTools

View File

@@ -4,10 +4,9 @@
#include "qtsingleapplication.h"
#include "qtlocalpeer.h"
#include <qtlockedfile.h>
#include <QDir>
#include <QFileOpenEvent>
#include <QLockFile>
#include <QSharedMemory>
#include <QWidget>
@@ -50,11 +49,10 @@ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char *
}
}
// QtLockedFile is used to workaround QTBUG-10364
QtLockedFile lockfile(instancesLockFilename(appSessionId));
// QLockFile is used to workaround QTBUG-10364
QLockFile lockfile(instancesLockFilename(appSessionId));
lockfile.open(QtLockedFile::ReadWrite);
lockfile.lock(QtLockedFile::WriteLock);
lockfile.lock();
qint64 *pids = static_cast<qint64 *>(instances->data());
if (!created) {
// Find the first instance that it still running
@@ -79,9 +77,8 @@ QtSingleApplication::~QtSingleApplication()
if (!instances)
return;
const qint64 appPid = QCoreApplication::applicationPid();
QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId)));
lockfile.open(QtLockedFile::ReadWrite);
lockfile.lock(QtLockedFile::WriteLock);
QLockFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId)));
lockfile.lock();
// Rewrite array, removing current pid and previously crashed ones
qint64 *pids = static_cast<qint64 *>(instances->data());
qint64 *newpids = pids;