Revert "App: Replace QtLockedFile usage with QLockFile"

This reverts commit 1fca05959f.

Reason for revert: Portale +2'ed under the assumption that this
goes into the master branch. This year old issue should definitely
be fixed, but typically not that urgently in a .3 patch release

Let's revert this and move it on master, please. I take the blame for the messy git history.

Change-Id: I8ae5a5e8e89ce38e7fe663b09cb6c2c3ab520ab0
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
Alessandro Portale
2023-08-24 17:03:13 +00:00
parent 1fca05959f
commit 637fdcc5ba
13 changed files with 566 additions and 15 deletions

View File

@@ -40,6 +40,7 @@ QtcProduct {
: ["$ORIGIN/../" + qtc.libDirName + "/qtcreator"]
cpp.includePaths: [
project.sharedSourcesDir + "/qtsingleapplication",
project.sharedSourcesDir + "/qtlockedfile",
]
cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["Foundation"] : [])
@@ -57,6 +58,7 @@ 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"
]
@@ -82,6 +84,22 @@ 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,6 +1,7 @@
add_subdirectory(designerintegrationv2)
add_subdirectory(proparser)
add_subdirectory(qtsingleapplication)
add_subdirectory(qtlockedfile)
add_subdirectory(help)
add_subdirectory(registryaccess)

View File

@@ -0,0 +1,17 @@
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

@@ -0,0 +1,10 @@
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

@@ -0,0 +1,70 @@
--- 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

@@ -0,0 +1,132 @@
// 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

@@ -0,0 +1,48 @@
// 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

@@ -0,0 +1,82 @@
// 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

@@ -0,0 +1,170 @@
// 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 Qt::Core Qt::Network Qt::Widgets)
target_link_libraries(shared_qtsingleapplication shared_qtlockedfile 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,15 +59,16 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
lockFile.reset(new QLockFile(lockName));
lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite);
}
bool QtLocalPeer::isClient()
{
if (lockFile->isLocked())
if (lockFile.isLocked())
return false;
if (!lockFile->tryLock())
if (!lockFile.lock(QtLockedFile::WriteLock, false))
return true;
if (!QLocalServer::removeServer(socketName))

View File

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

View File

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