aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils/filestreamermanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils/filestreamermanager.cpp')
-rw-r--r--src/libs/utils/filestreamermanager.cpp201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/libs/utils/filestreamermanager.cpp b/src/libs/utils/filestreamermanager.cpp
new file mode 100644
index 0000000000..11d05faee5
--- /dev/null
+++ b/src/libs/utils/filestreamermanager.cpp
@@ -0,0 +1,201 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "filestreamermanager.h"
+
+#include "filestreamer.h"
+#include "threadutils.h"
+#include "utilstr.h"
+
+#include <QMutex>
+#include <QMutexLocker>
+#include <QThread>
+#include <QWaitCondition>
+
+#include <unordered_map>
+
+namespace Utils {
+
+// TODO: destruct the instance before destructing ProjectExplorer::DeviceManager (?)
+
+static FileStreamHandle generateUniqueHandle()
+{
+ static std::atomic_int handleCounter = 1;
+ return FileStreamHandle(handleCounter.fetch_add(1));
+}
+
+static QMutex s_mutex = {};
+static QWaitCondition s_waitCondition = {};
+static std::unordered_map<FileStreamHandle, FileStreamer *> s_fileStreamers = {};
+
+static void addStreamer(FileStreamHandle handle, FileStreamer *streamer)
+{
+ QMutexLocker locker(&s_mutex);
+ const bool added = s_fileStreamers.try_emplace(handle, streamer).second;
+ QTC_CHECK(added);
+}
+
+static void removeStreamer(FileStreamHandle handle)
+{
+ QMutexLocker locker(&s_mutex);
+ auto it = s_fileStreamers.find(handle);
+ QTC_ASSERT(it != s_fileStreamers.end(), return);
+ QTC_ASSERT(QThread::currentThread() == it->second->thread(), return);
+ s_fileStreamers.erase(it);
+ s_waitCondition.wakeAll();
+}
+
+static void deleteStreamer(FileStreamHandle handle)
+{
+ QMutexLocker locker(&s_mutex);
+ auto it = s_fileStreamers.find(handle);
+ if (it == s_fileStreamers.end())
+ return;
+ if (QThread::currentThread() == it->second->thread()) {
+ delete it->second;
+ s_fileStreamers.erase(it);
+ s_waitCondition.wakeAll();
+ } else {
+ QMetaObject::invokeMethod(it->second, [handle] {
+ deleteStreamer(handle);
+ });
+ s_waitCondition.wait(&s_mutex);
+ QTC_CHECK(s_fileStreamers.find(handle) == s_fileStreamers.end());
+ }
+}
+
+static void deleteAllStreamers()
+{
+ QMutexLocker locker(&s_mutex);
+ QTC_ASSERT(Utils::isMainThread(), return);
+ while (s_fileStreamers.size()) {
+ auto it = s_fileStreamers.begin();
+ if (QThread::currentThread() == it->second->thread()) {
+ delete it->second;
+ s_fileStreamers.erase(it);
+ s_waitCondition.wakeAll();
+ } else {
+ const FileStreamHandle handle = it->first;
+ QMetaObject::invokeMethod(it->second, [handle] {
+ deleteStreamer(handle);
+ });
+ s_waitCondition.wait(&s_mutex);
+ QTC_CHECK(s_fileStreamers.find(handle) == s_fileStreamers.end());
+ }
+ }
+}
+
+static FileStreamHandle checkHandle(FileStreamHandle handle)
+{
+ QMutexLocker locker(&s_mutex);
+ return s_fileStreamers.find(handle) != s_fileStreamers.end() ? handle : FileStreamHandle(0);
+}
+
+FileStreamHandle execute(const std::function<void(FileStreamer *)> &onSetup,
+ const std::function<void(FileStreamer *)> &onDone,
+ QObject *context)
+{
+ FileStreamer *streamer = new FileStreamer;
+ onSetup(streamer);
+ const FileStreamHandle handle = generateUniqueHandle();
+ QTC_CHECK(context == nullptr || context->thread() == QThread::currentThread());
+ if (onDone) {
+ QObject *finalContext = context ? context : streamer;
+ QObject::connect(streamer, &FileStreamer::done, finalContext, [=] { onDone(streamer); });
+ }
+ QObject::connect(streamer, &FileStreamer::done, streamer, [=] {
+ removeStreamer(handle);
+ streamer->deleteLater();
+ });
+ addStreamer(handle, streamer);
+ streamer->start();
+ return checkHandle(handle); // The handle could have been already removed
+}
+
+FileStreamHandle FileStreamerManager::copy(const FilePath &source, const FilePath &destination,
+ const CopyContinuation &cont)
+{
+ return copy(source, destination, nullptr, cont);
+}
+
+FileStreamHandle FileStreamerManager::copy(const FilePath &source, const FilePath &destination,
+ QObject *context, const CopyContinuation &cont)
+{
+ const auto onSetup = [=](FileStreamer *streamer) {
+ streamer->setSource(source);
+ streamer->setDestination(destination);
+ };
+ if (!cont)
+ return execute(onSetup, {}, context);
+
+ const auto onDone = [=](FileStreamer *streamer) {
+ if (streamer->result() == StreamResult::FinishedWithSuccess)
+ cont({});
+ else
+ cont(make_unexpected(Tr::tr("Failed copying file")));
+ };
+ return execute(onSetup, onDone, context);
+}
+
+FileStreamHandle FileStreamerManager::read(const FilePath &source, const ReadContinuation &cont)
+{
+ return read(source, nullptr, cont);
+}
+
+FileStreamHandle FileStreamerManager::read(const FilePath &source, QObject *context,
+ const ReadContinuation &cont)
+{
+ const auto onSetup = [=](FileStreamer *streamer) {
+ streamer->setStreamMode(StreamMode::Reader);
+ streamer->setSource(source);
+ };
+ if (!cont)
+ return execute(onSetup, {}, context);
+
+ const auto onDone = [=](FileStreamer *streamer) {
+ if (streamer->result() == StreamResult::FinishedWithSuccess)
+ cont(streamer->readData());
+ else
+ cont(make_unexpected(Tr::tr("Failed reading file")));
+ };
+ return execute(onSetup, onDone, context);
+}
+
+FileStreamHandle FileStreamerManager::write(const FilePath &destination, const QByteArray &data,
+ const WriteContinuation &cont)
+{
+ return write(destination, data, nullptr, cont);
+}
+
+FileStreamHandle FileStreamerManager::write(const FilePath &destination, const QByteArray &data,
+ QObject *context, const WriteContinuation &cont)
+{
+ const auto onSetup = [=](FileStreamer *streamer) {
+ streamer->setStreamMode(StreamMode::Writer);
+ streamer->setDestination(destination);
+ streamer->setWriteData(data);
+ };
+ if (!cont)
+ return execute(onSetup, {}, context);
+
+ const auto onDone = [=](FileStreamer *streamer) {
+ if (streamer->result() == StreamResult::FinishedWithSuccess)
+ cont(0); // TODO: return write count?
+ else
+ cont(make_unexpected(Tr::tr("Failed writing file")));
+ };
+ return execute(onSetup, onDone, context);
+}
+
+void FileStreamerManager::stop(FileStreamHandle handle)
+{
+ deleteStreamer(handle);
+}
+
+void FileStreamerManager::stopAll()
+{
+ deleteAllStreamers();
+}
+
+} // namespace Utils
+