From ebf0917b9fce0274c4a3548af0e8e3eb813343e6 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 30 Aug 2021 15:42:21 +0200 Subject: [PATCH] Utils: Start adding some asynchronous API to FilePath Especially with the "remote" scenarios synchronous operations cannot be expected to work reasonably well. This here starts with adding asynchronous versions to some of the FilePath member functions, taking additional "Continuation" style. This is not necessarily the final syntax (sugar like .then(...) comes to mind...), but is simple enough for now for the few uses we have, and it is too early to see what will be needed in the end. Change-Id: Idf4dde1b77d04cafb81b6c024031145bdd91a762 Reviewed-by: Christian Stenger --- src/libs/utils/filepath.cpp | 35 +++++++++++++++++++ src/libs/utils/filepath.h | 7 ++++ src/libs/utils/fileutils.h | 5 +++ .../devicesupport/devicemanager.cpp | 9 +++++ .../projectexplorer/devicesupport/idevice.cpp | 14 ++++++++ .../projectexplorer/devicesupport/idevice.h | 8 +++++ .../callgrind/callgrindcontroller.cpp | 9 ++--- 7 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index df07f602440..22854fd5075 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -738,6 +738,17 @@ QByteArray FilePath::fileContents(qint64 maxSize, qint64 offset) const return f.readAll(); } +void FilePath::asyncFileContents(const Continuation &cont, + qint64 maxSize, qint64 offset) const +{ + if (needsDevice()) { + QTC_ASSERT(s_deviceHooks.asyncFileContents, return); + return s_deviceHooks.asyncFileContents(cont, *this, maxSize, offset); + } + + cont(fileContents(maxSize, offset)); +} + bool FilePath::writeFileContents(const QByteArray &data) const { if (needsDevice()) { @@ -751,6 +762,17 @@ bool FilePath::writeFileContents(const QByteArray &data) const return res == data.size(); } +void FilePath::asyncWriteFileContents(const Continuation &cont, const QByteArray &data) const +{ + if (needsDevice()) { + QTC_ASSERT(s_deviceHooks.asyncWriteFileContents, return); + s_deviceHooks.asyncWriteFileContents(cont, *this, data); + return; + } + + cont(writeFileContents(data)); +} + bool FilePath::needsDevice() const { return !m_scheme.isEmpty(); @@ -1319,6 +1341,19 @@ bool FilePath::copyFile(const FilePath &target) const return QFile::copy(path(), target.path()); } +void FilePath::asyncCopyFile(const std::function &cont, const FilePath &target) const +{ + if (host() != target.host()) { + asyncFileContents([cont, target](const QByteArray &ba) { + target.asyncWriteFileContents(cont, ba); + }); + } else if (needsDevice()) { + s_deviceHooks.asyncCopyFile(cont, *this, target); + } else { + cont(copyFile(target)); + } +} + bool FilePath::renameFile(const FilePath &target) const { if (needsDevice()) { diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 97e9b9af470..18c37170bbb 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -181,6 +181,13 @@ public: static void removeDuplicates(QList &files); static void sort(QList &files); + // Asynchronous interface + template using Continuation = std::function; + void asyncCopyFile(const Continuation &cont, const FilePath &target) const; + void asyncFileContents(const Continuation &cont, + qint64 maxSize = -1, qint64 offset = 0) const; + void asyncWriteFileContents(const Continuation &cont, const QByteArray &data) const; + private: friend class ::tst_fileutils; static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index ba2d74d0a8c..cc1ab8c87dd 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -88,6 +88,11 @@ public: std::function osType; std::function environment; std::function fileSize; + + template using Continuation = std::function; + std::function &, const FilePath &, const FilePath &)> asyncCopyFile; + std::function &, const FilePath &, qint64, qint64)> asyncFileContents; + std::function &, const FilePath &, const QByteArray &)> asyncWriteFileContents; }; class QTCREATOR_UTILS_EXPORT FileUtils diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index eee62a80eed..f7470cd079b 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -58,6 +58,8 @@ const char DeviceManagerKey[] = "DeviceManager"; const char DeviceListKey[] = "DeviceList"; const char DefaultDevicesKey[] = "DefaultDevices"; +template using Continuation = std::function; + class DeviceManagerPrivate { public: @@ -495,6 +497,13 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniquefileContents(filePath, maxSize, offset); }; + deviceHooks.asyncFileContents = [](const Continuation &cont, const FilePath &filePath, + qint64 maxSize, qint64 offset) { + auto device = DeviceManager::deviceForPath(filePath); + QTC_ASSERT(device, return); + device->asyncFileContents(cont, filePath, maxSize, offset); + }; + deviceHooks.writeFileContents = [](const FilePath &filePath, const QByteArray &data) { auto device = DeviceManager::deviceForPath(filePath); QTC_ASSERT(device, return false); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index e7de6bd8d60..03f7e8b281c 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -376,6 +376,13 @@ QByteArray IDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 return {}; } +void IDevice::asyncFileContents(const Continuation &cont, + const FilePath &filePath, + qint64 limit, qint64 offset) const +{ + cont(fileContents(filePath, limit, offset)); +} + bool IDevice::writeFileContents(const FilePath &filePath, const QByteArray &data) const { Q_UNUSED(filePath); @@ -384,6 +391,13 @@ bool IDevice::writeFileContents(const FilePath &filePath, const QByteArray &data return {}; } +void IDevice::asyncWriteFileContents(const Continuation &cont, + const FilePath &filePath, + const QByteArray &data) const +{ + cont(writeFileContents(filePath, data)); +} + QDateTime IDevice::lastModified(const FilePath &filePath) const { Q_UNUSED(filePath); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 4f399d8cc65..e9d488667f0 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -127,6 +127,7 @@ class PROJECTEXPLORER_EXPORT IDevice : public QEnableSharedFromThis public: using Ptr = QSharedPointer; using ConstPtr = QSharedPointer; + template using Continuation = std::function; enum Origin { ManuallyAdded, AutoDetected }; enum MachineType { Hardware, Emulator }; @@ -272,6 +273,13 @@ public: virtual void aboutToBeRemoved() const {} + virtual void asyncFileContents(const Continuation &cont, + const Utils::FilePath &filePath, + qint64 limit, qint64 offset) const; + virtual void asyncWriteFileContents(const Continuation &cont, + const Utils::FilePath &filePath, + const QByteArray &data) const; + protected: IDevice(); diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp index 31da33e13a1..4e570b2b05e 100644 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp +++ b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp @@ -223,10 +223,11 @@ void CallgrindController::getLocalDataFile() // this, &CallgrindController::sftpInitialized); // m_sftp->start(); - const bool res = m_valgrindOutputFile.copyFile(m_hostOutputFile); - QTC_CHECK(res); - - emit localParseDataAvailable(m_hostOutputFile); + const auto afterCopy = [this](bool res) { + QTC_CHECK(res); + emit localParseDataAvailable(m_hostOutputFile); + }; + m_valgrindOutputFile.asyncCopyFile(afterCopy, m_hostOutputFile); } void CallgrindController::sftpInitialized()