forked from qt-creator/qt-creator
Add FSEngine FilePath Cache
To speed up file dialogs we introduce a 1 minute cache for the FilePathInfo. A new version of "IDevice::iterateDirectories" allows implementations to provide the FilePathInfo directly. DockerDevice implements fetching the filePathInfo during iterateDirectories which greatly improves the speed again. Change-Id: I24ac16adb2478cbf16a22012e72fcb8910dcdac5 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -67,12 +67,11 @@
|
||||
#include <QThread>
|
||||
#include <QToolButton>
|
||||
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace Core;
|
||||
@@ -92,13 +91,13 @@ public:
|
||||
: m_settings(settings)
|
||||
, m_containerId(containerId)
|
||||
, m_devicePath(devicePath)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
private:
|
||||
void setupShellProcess(QtcProcess *shellProcess) final
|
||||
{
|
||||
shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "start", "-i", "-a", m_containerId}});
|
||||
shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(),
|
||||
{"container", "start", "-i", "-a", m_containerId}});
|
||||
}
|
||||
|
||||
CommandLine createFallbackCommand(const CommandLine &cmdLine)
|
||||
@@ -162,7 +161,8 @@ public:
|
||||
DockerDeviceData m_data;
|
||||
DockerSettings *m_settings;
|
||||
|
||||
struct TemporaryMountInfo {
|
||||
struct TemporaryMountInfo
|
||||
{
|
||||
FilePath path;
|
||||
FilePath containerPath;
|
||||
};
|
||||
@@ -327,11 +327,9 @@ Tasks DockerDevice::validate() const
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
|
||||
: d(new DockerDevicePrivate(this, settings, data))
|
||||
{
|
||||
|
||||
setDisplayType(Tr::tr("Docker"));
|
||||
setOsType(OsTypeOtherUnix);
|
||||
setDefaultDisplayName(Tr::tr("Docker Image"));
|
||||
@@ -421,7 +419,11 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, bool
|
||||
|
||||
void DockerDevicePrivate::stopCurrentContainer()
|
||||
{
|
||||
if (!m_settings || m_container.isEmpty() || !DockerApi::isDockerDaemonAvailable(false).value_or(false))
|
||||
if (!m_settings)
|
||||
return;
|
||||
if (m_container.isEmpty())
|
||||
return;
|
||||
if (!DockerApi::isDockerDaemonAvailable(false).value_or(false))
|
||||
return;
|
||||
|
||||
m_shell.reset();
|
||||
@@ -589,8 +591,7 @@ void DockerDevice::fromMap(const QVariantMap &map)
|
||||
data.tag = map.value(DockerDeviceDataTagKey).toString();
|
||||
data.imageId = map.value(DockerDeviceDataImageIdKey).toString();
|
||||
data.size = map.value(DockerDeviceDataSizeKey).toString();
|
||||
data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost())
|
||||
.toBool();
|
||||
data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost()).toBool();
|
||||
data.mounts = map.value(DockerDeviceMappedPaths).toStringList();
|
||||
data.keepEntryPoint = map.value(DockerDeviceKeepEntryPoint).toBool();
|
||||
d->setData(data);
|
||||
@@ -623,26 +624,24 @@ bool DockerDevice::canAutoDetectPorts() const
|
||||
|
||||
PortsGatheringMethod DockerDevice::portsGatheringMethod() const
|
||||
{
|
||||
return {
|
||||
[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
|
||||
// We might encounter the situation that protocol is given IPv6
|
||||
// but the consumer of the free port information decides to open
|
||||
// an IPv4(only) port. As a result the next IPv6 scan will
|
||||
// report the port again as open (in IPv6 namespace), while the
|
||||
// same port in IPv4 namespace might still be blocked, and
|
||||
// re-use of this port fails.
|
||||
// GDBserver behaves exactly like this.
|
||||
return {[this](QAbstractSocket::NetworkLayerProtocol protocol) -> CommandLine {
|
||||
// We might encounter the situation that protocol is given IPv6
|
||||
// but the consumer of the free port information decides to open
|
||||
// an IPv4(only) port. As a result the next IPv6 scan will
|
||||
// report the port again as open (in IPv6 namespace), while the
|
||||
// same port in IPv4 namespace might still be blocked, and
|
||||
// re-use of this port fails.
|
||||
// GDBserver behaves exactly like this.
|
||||
|
||||
Q_UNUSED(protocol)
|
||||
Q_UNUSED(protocol)
|
||||
|
||||
// /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
|
||||
return {filePath("sed"),
|
||||
"-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
|
||||
CommandLine::Raw};
|
||||
},
|
||||
// /proc/net/tcp* covers /proc/net/tcp and /proc/net/tcp6
|
||||
return {filePath("sed"),
|
||||
"-e 's/.*: [[:xdigit:]]*:\\([[:xdigit:]]\\{4\\}\\).*/\\1/g' /proc/net/tcp*",
|
||||
CommandLine::Raw};
|
||||
},
|
||||
|
||||
&Port::parseFromSedOutput
|
||||
};
|
||||
&Port::parseFromSedOutput};
|
||||
};
|
||||
|
||||
DeviceProcessList *DockerDevice::createProcessListModel(QObject *) const
|
||||
@@ -714,12 +713,12 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const
|
||||
if (filePath.scheme() == u"device" && filePath.host() == id().toString())
|
||||
return true;
|
||||
|
||||
if (filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME
|
||||
&& filePath.host() == d->dockerImageId())
|
||||
const bool isDockerScheme = filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME;
|
||||
|
||||
if (isDockerScheme && filePath.host() == d->dockerImageId())
|
||||
return true;
|
||||
|
||||
if (filePath.scheme() == Constants::DOCKER_DEVICE_SCHEME
|
||||
&& filePath.host() == QString(d->repoAndTag()))
|
||||
if (isDockerScheme && filePath.host() == QString(d->repoAndTag()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -875,7 +874,8 @@ QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) con
|
||||
return perm;
|
||||
}
|
||||
|
||||
bool DockerDevice::setPermissions(const FilePath &filePath, QFileDevice::Permissions permissions) const
|
||||
bool DockerDevice::setPermissions(const FilePath &filePath,
|
||||
QFileDevice::Permissions permissions) const
|
||||
{
|
||||
Q_UNUSED(permissions)
|
||||
QTC_ASSERT(handlesFile(filePath), return {});
|
||||
@@ -894,7 +894,16 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
|
||||
}
|
||||
|
||||
void DockerDevice::iterateDirectory(const FilePath &filePath,
|
||||
const std::function<bool(const FilePath &)> &callBack,
|
||||
const FilePath::IterateDirCallback &callBack,
|
||||
const FileFilter &filter) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return);
|
||||
auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); };
|
||||
FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
|
||||
}
|
||||
|
||||
void DockerDevice::iterateDirectory(const FilePath &filePath,
|
||||
const FilePath::IterateDirWithInfoCallback &callBack,
|
||||
const FileFilter &filter) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return);
|
||||
@@ -923,6 +932,13 @@ bool DockerDevice::writeFileContents(const FilePath &filePath,
|
||||
return d->runInShellSuccess(cmd, data);
|
||||
}
|
||||
|
||||
FilePathInfo DockerDevice::filePathInfo(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(handlesFile(filePath), return {});
|
||||
const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}});
|
||||
return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
|
||||
}
|
||||
|
||||
Environment DockerDevice::systemEnvironment() const
|
||||
{
|
||||
return d->environment();
|
||||
@@ -973,8 +989,7 @@ void DockerDevicePrivate::fetchSystemEnviroment()
|
||||
proc.waitForFinished();
|
||||
const QString remoteOutput = proc.cleanedStdOut();
|
||||
|
||||
m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts),
|
||||
q->osType());
|
||||
m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType());
|
||||
|
||||
const QString remoteError = proc.cleanedStdErr();
|
||||
if (!remoteError.isEmpty())
|
||||
@@ -1078,6 +1093,7 @@ public:
|
||||
m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
|
||||
using namespace Layouting;
|
||||
|
||||
Column {
|
||||
Stack {
|
||||
statusLabel,
|
||||
@@ -1085,17 +1101,16 @@ public:
|
||||
},
|
||||
m_log,
|
||||
errorLabel,
|
||||
Row {
|
||||
showUnnamedContainers,
|
||||
m_buttons
|
||||
},
|
||||
}.attachTo(this);
|
||||
Row{showUnnamedContainers, m_buttons},
|
||||
}
|
||||
.attachTo(this);
|
||||
|
||||
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
CommandLine cmd{m_settings->dockerBinaryPath.filePath(), {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
|
||||
CommandLine cmd{m_settings->dockerBinaryPath.filePath(),
|
||||
{"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}};
|
||||
m_log->append(Tr::tr("Running \"%1\"\n").arg(cmd.toUserOutput()));
|
||||
|
||||
m_process = new QtcProcess(this);
|
||||
@@ -1146,7 +1161,8 @@ public:
|
||||
{
|
||||
const QModelIndexList selectedRows = m_view->selectionModel()->selectedRows();
|
||||
QTC_ASSERT(selectedRows.size() == 1, return {});
|
||||
DockerImageItem *item = m_model.itemForIndex(m_proxyModel->mapToSource(selectedRows.front()));
|
||||
DockerImageItem *item = m_model.itemForIndex(
|
||||
m_proxyModel->mapToSource(selectedRows.front()));
|
||||
QTC_ASSERT(item, return {});
|
||||
|
||||
auto device = DockerDevice::create(m_settings, *item);
|
||||
@@ -1201,10 +1217,9 @@ void DockerDeviceFactory::shutdownExistingDevices()
|
||||
|
||||
bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath &containerPath)
|
||||
{
|
||||
bool alreadyAdded = anyOf(m_temporaryMounts,
|
||||
[containerPath](const TemporaryMountInfo &info) {
|
||||
return info.containerPath == containerPath;
|
||||
});
|
||||
bool alreadyAdded = anyOf(m_temporaryMounts, [containerPath](const TemporaryMountInfo &info) {
|
||||
return info.containerPath == containerPath;
|
||||
});
|
||||
if (alreadyAdded)
|
||||
return false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user