summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2025-09-26 16:22:38 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2025-12-02 02:24:38 +0100
commit710847b3a5715bedbc7f30abafe8d55c58eafc9f (patch)
tree76eeb8ee064041306c1e767552dfd37e54952c1d
parent4371a6cff94c703caf54d45fcb1bd9511642861d (diff)
Add QIORing backend for QRandomAccessAsyncFile
This adds a backend for QRandomAccessAsyncFile to use the recently introduced QIORing. Since all uses of of QRandomAccessAsyncFile expects signals to be emitted after returning control to the caller, and QIORing may complete anything synchronously, we emit signals only after returning to the event loop. This could probably be optimized later to be a direct emission when it's not technically needed, but is not a priority right now. Task-number: QTBUG-136763 Change-Id: I8acfa7f716e5625da498cc4b6fbe493ebd783f99 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
-rw-r--r--src/corelib/CMakeLists.txt7
-rw-r--r--src/corelib/io/qiooperation_p_p.h4
-rw-r--r--src/corelib/io/qrandomaccessasyncfile_p_p.h14
-rw-r--r--src/corelib/io/qrandomaccessasyncfile_qioring.cpp438
-rw-r--r--tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp22
5 files changed, 479 insertions, 6 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index eda3152d9b7..e12d824cebb 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -583,6 +583,13 @@ if(QT_FEATURE_async_io)
SOURCES
io/qrandomaccessasyncfile_darwin.mm
)
+ elseif(LINUX AND QT_FEATURE_liburing)
+ qt_internal_extend_target(Core
+ SOURCES
+ io/qrandomaccessasyncfile_qioring.cpp
+ DEFINES
+ QT_RANDOMACCESSASYNCFILE_QIORING
+ )
elseif(QT_FEATURE_thread AND QT_FEATURE_future)
# TODO: This should become the last (fallback) condition later.
# We migth also want to rewrite it so that it does not depend on
diff --git a/src/corelib/io/qiooperation_p_p.h b/src/corelib/io/qiooperation_p_p.h
index 470e0858fd3..be780d4c785 100644
--- a/src/corelib/io/qiooperation_p_p.h
+++ b/src/corelib/io/qiooperation_p_p.h
@@ -24,6 +24,10 @@
#include <QtCore/qspan.h>
#include <QtCore/qvarlengtharray.h>
+#ifdef QT_RANDOMACCESSASYNCFILE_QIORING
+#include <QtCore/private/qioring_p.h>
+#endif
+
#include <variant>
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/io/qrandomaccessasyncfile_p_p.h b/src/corelib/io/qrandomaccessasyncfile_p_p.h
index 924c9f9ed83..11ad788c884 100644
--- a/src/corelib/io/qrandomaccessasyncfile_p_p.h
+++ b/src/corelib/io/qrandomaccessasyncfile_p_p.h
@@ -43,6 +43,11 @@
#endif // Q_OS_DARWIN
+#ifdef QT_RANDOMACCESSASYNCFILE_QIORING
+#include <QtCore/private/qioring_p.h>
+#include <QtCore/qlist.h>
+#endif
+
QT_BEGIN_NAMESPACE
class QRandomAccessAsyncFilePrivate : public QObjectPrivate
@@ -114,6 +119,15 @@ private:
void processFlush();
void processOpen();
void operationComplete();
+#elif defined(QT_RANDOMACCESSASYNCFILE_QIORING)
+ void queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error);
+ void startReadIntoSingle(QIOOperation *op, const QSpan<std::byte> &to);
+ void startWriteFromSingle(QIOOperation *op, const QSpan<const std::byte> &from);
+ QIORing::RequestHandle cancel(QIORing::RequestHandle handle);
+ QIORing *m_ioring = nullptr;
+ qintptr m_fd = -1;
+ QList<QPointer<QIOOperation>> m_operations;
+ QHash<QIOOperation *, QIORing::RequestHandle> m_opHandleMap;
#endif
#ifdef Q_OS_DARWIN
using OperationId = quint64;
diff --git a/src/corelib/io/qrandomaccessasyncfile_qioring.cpp b/src/corelib/io/qrandomaccessasyncfile_qioring.cpp
new file mode 100644
index 00000000000..c9783ea2856
--- /dev/null
+++ b/src/corelib/io/qrandomaccessasyncfile_qioring.cpp
@@ -0,0 +1,438 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
+
+#include "qrandomaccessasyncfile_p_p.h"
+
+#include "qiooperation_p.h"
+#include "qiooperation_p_p.h"
+
+#include <QtCore/qfile.h> // QtPrivate::toFilesystemPath
+#include <QtCore/qtypes.h>
+#include <QtCore/private/qioring_p.h>
+
+#include <QtCore/q26numeric.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_STATIC_LOGGING_CATEGORY(lcQRandomAccessIORing, "qt.core.qrandomaccessasyncfile.ioring",
+ QtCriticalMsg);
+
+QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() = default;
+
+QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() = default;
+
+void QRandomAccessAsyncFilePrivate::init()
+{
+ m_ioring = QIORing::sharedInstance();
+ if (!m_ioring)
+ qCCritical(lcQRandomAccessIORing, "QRandomAccessAsyncFile: ioring failed to initialize");
+}
+
+QIORing::RequestHandle QRandomAccessAsyncFilePrivate::cancel(QIORing::RequestHandle handle)
+{
+ if (handle) {
+ QIORingRequest<QIORing::Operation::Cancel> cancelRequest;
+ cancelRequest.handle = handle;
+ return m_ioring->queueRequest(std::move(cancelRequest));
+ }
+ return nullptr;
+}
+
+void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op)
+{
+ auto *opHandle = m_opHandleMap.value(op);
+ if (auto *handle = cancel(opHandle)) {
+ m_ioring->waitForRequest(handle);
+ m_ioring->waitForRequest(opHandle);
+ }
+}
+
+void QRandomAccessAsyncFilePrivate::queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error)
+{
+ // Remove the handle now in case the user cancels or deletes the io-operation
+ // before operationComplete is called - the null-handle will protect from
+ // nasty issues that may occur when trying to cancel an operation that's no
+ // longer in the queue:
+ m_opHandleMap.remove(priv->q_func());
+ // @todo: Look into making it emit only if synchronously completed
+ QMetaObject::invokeMethod(priv->q_ptr, [priv, error](){
+ priv->operationComplete(error);
+ }, Qt::QueuedConnection);
+}
+
+QIOOperation *QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode)
+{
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage();
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->type = QIOOperation::Type::Open;
+
+ auto *op = new QIOOperation(*priv, q_ptr);
+ if (m_fileState != FileState::Closed) {
+ queueCompletion(priv, QIOOperation::Error::Open);
+ return op;
+ }
+ m_operations.append(op);
+ m_fileState = FileState::OpenPending;
+
+ QIORingRequest<QIORing::Operation::Open> openOperation;
+ openOperation.path = QtPrivate::toFilesystemPath(path);
+ openOperation.flags = mode;
+ openOperation.setCallback([this, op,
+ priv](const QIORingRequest<QIORing::Operation::Open> &request) {
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ if (m_fileState != FileState::Opened) {
+ // We assume there was only one pending open() in flight.
+ m_fd = -1;
+ m_fileState = FileState::Closed;
+ }
+ if (priv->error == QIOOperation::Error::Aborted || *err == QFileDevice::AbortError)
+ queueCompletion(priv, QIOOperation::Error::Aborted);
+ else
+ queueCompletion(priv, QIOOperation::Error::Open);
+ } else if (const auto *result = std::get_if<QIORingResult<QIORing::Operation::Open>>(
+ &request.result)) {
+ if (m_fileState == FileState::OpenPending) {
+ m_fileState = FileState::Opened;
+ m_fd = result->fd;
+ queueCompletion(priv, QIOOperation::Error::None);
+ } else { // Something went wrong, we did not expect a callback:
+ // So we close the new handle:
+ QIORingRequest<QIORing::Operation::Close> closeRequest;
+ closeRequest.fd = result->fd;
+ QIORing::RequestHandle handle = m_ioring->queueRequest(std::move(closeRequest));
+ // Since the user issued multiple open() calls they get to wait for the close() to
+ // finish:
+ m_ioring->waitForRequest(handle);
+ queueCompletion(priv, QIOOperation::Error::Open);
+ }
+ }
+ m_operations.removeOne(op);
+ });
+ m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(openOperation)));
+
+ return op;
+}
+
+void QRandomAccessAsyncFilePrivate::close()
+{
+ // all the operations should be aborted
+ const auto ops = std::exchange(m_operations, {});
+ QList<QIORing::RequestHandle> tasksToAwait;
+ // Request to cancel all of the in-flight operations:
+ for (const auto &op : ops) {
+ if (op) {
+ op->d_func()->error = QIOOperation::Error::Aborted;
+ if (auto *opHandle = m_opHandleMap.value(op)) {
+ tasksToAwait.append(cancel(opHandle));
+ tasksToAwait.append(opHandle);
+ }
+ }
+ }
+
+ QIORingRequest<QIORing::Operation::Close> closeRequest;
+ closeRequest.fd = m_fd;
+ tasksToAwait.append(m_ioring->queueRequest(std::move(closeRequest)));
+
+ // Wait for completion:
+ for (const QIORing::RequestHandle &handle : tasksToAwait)
+ m_ioring->waitForRequest(handle);
+ m_fileState = FileState::Closed;
+ m_fd = -1;
+}
+
+qint64 QRandomAccessAsyncFilePrivate::size() const
+{
+ QIORingRequest<QIORing::Operation::Stat> statRequest;
+ statRequest.fd = m_fd;
+ qint64 finalSize = 0;
+ statRequest.setCallback([&finalSize](const QIORingRequest<QIORing::Operation::Stat> &request) {
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ Q_UNUSED(err);
+ finalSize = -1;
+ } else if (const auto *res = std::get_if<QIORingResult<QIORing::Operation::Stat>>(&request.result)) {
+ finalSize = q26::saturate_cast<qint64>(res->size);
+ }
+ });
+ auto *handle = m_ioring->queueRequest(std::move(statRequest));
+ m_ioring->waitForRequest(handle);
+
+ return finalSize;
+}
+
+QIOOperation *QRandomAccessAsyncFilePrivate::flush()
+{
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage();
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->type = QIOOperation::Type::Flush;
+
+ auto *op = new QIOOperation(*priv, q_ptr);
+ m_operations.append(op);
+
+ QIORingRequest<QIORing::Operation::Flush> flushRequest;
+ flushRequest.fd = m_fd;
+ flushRequest.setCallback([this, op](const QIORingRequest<QIORing::Operation::Flush> &request) {
+ auto *priv = QIOOperationPrivate::get(op);
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ if (priv->error == QIOOperation::Error::Aborted || *err == QFileDevice::AbortError)
+ queueCompletion(priv, QIOOperation::Error::Aborted);
+ else if (*err == QFileDevice::OpenError)
+ queueCompletion(priv, QIOOperation::Error::FileNotOpen);
+ else
+ queueCompletion(priv, QIOOperation::Error::Flush);
+ } else if (std::get_if<QIORingResult<QIORing::Operation::Flush>>(&request.result)) {
+ queueCompletion(priv, QIOOperation::Error::None);
+ }
+ m_operations.removeOne(op);
+ });
+ m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(flushRequest)));
+
+ return op;
+}
+
+void QRandomAccessAsyncFilePrivate::startReadIntoSingle(QIOOperation *op,
+ const QSpan<std::byte> &to)
+{
+ QIORingRequest<QIORing::Operation::Read> readRequest;
+ readRequest.fd = m_fd;
+ auto *priv = QIOOperationPrivate::get(op);
+ if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now
+ queueCompletion(priv, QIOOperation::Error::IncorrectOffset);
+ m_operations.removeOne(op);
+ return;
+ }
+ readRequest.offset = priv->offset;
+ readRequest.destination = to;
+ readRequest.setCallback([this, op](const QIORingRequest<QIORing::Operation::Read> &request) {
+ auto *priv = QIOOperationPrivate::get(op);
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ if (priv->error == QIOOperation::Error::Aborted || *err == QFileDevice::AbortError)
+ queueCompletion(priv, QIOOperation::Error::Aborted);
+ else if (*err == QFileDevice::OpenError)
+ queueCompletion(priv, QIOOperation::Error::FileNotOpen);
+ else if (*err == QFileDevice::PositionError)
+ queueCompletion(priv, QIOOperation::Error::IncorrectOffset);
+ else
+ queueCompletion(priv, QIOOperation::Error::Read);
+ } else if (const auto *result = std::get_if<QIORingResult<QIORing::Operation::Read>>(
+ &request.result)) {
+ priv->appendBytesProcessed(result->bytesRead);
+ if (priv->dataStorage->containsReadSpans())
+ priv->dataStorage->getReadSpans().first().slice(0, result->bytesRead);
+ else
+ priv->dataStorage->getByteArray().slice(0, result->bytesRead);
+
+ queueCompletion(priv, QIOOperation::Error::None);
+ }
+ m_operations.removeOne(op);
+ });
+ m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(readRequest)));
+}
+
+QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize)
+{
+ QByteArray array;
+ array.resizeForOverwrite(maxSize);
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage(std::move(array));
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->offset = offset;
+ priv->type = QIOOperation::Type::Read;
+
+ auto *op = new QIOReadOperation(*priv, q_ptr);
+ m_operations.append(op);
+
+ startReadIntoSingle(op, as_writable_bytes(QSpan(dataStorage->getByteArray())));
+
+ return op;
+}
+
+QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data)
+{
+ return write(offset, QByteArray(data));
+}
+
+void QRandomAccessAsyncFilePrivate::startWriteFromSingle(QIOOperation *op,
+ const QSpan<const std::byte> &from)
+{
+ QIORingRequest<QIORing::Operation::Write> writeRequest;
+ writeRequest.fd = m_fd;
+ auto *priv = QIOOperationPrivate::get(op);
+ if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now
+ queueCompletion(priv, QIOOperation::Error::IncorrectOffset);
+ m_operations.removeOne(op);
+ return;
+ }
+ writeRequest.offset = priv->offset;
+ writeRequest.source = from;
+ writeRequest.setCallback([this, op](const QIORingRequest<QIORing::Operation::Write> &request) {
+ auto *priv = QIOOperationPrivate::get(op);
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ if (priv->error == QIOOperation::Error::Aborted || *err == QFileDevice::AbortError)
+ queueCompletion(priv, QIOOperation::Error::Aborted);
+ else if (*err == QFileDevice::OpenError)
+ queueCompletion(priv, QIOOperation::Error::FileNotOpen);
+ else if (*err == QFileDevice::PositionError)
+ queueCompletion(priv, QIOOperation::Error::IncorrectOffset);
+ else
+ queueCompletion(priv, QIOOperation::Error::Write);
+ } else if (const auto *result = std::get_if<QIORingResult<QIORing::Operation::Write>>(
+ &request.result)) {
+ priv->appendBytesProcessed(result->bytesWritten);
+ queueCompletion(priv, QIOOperation::Error::None);
+ }
+ m_operations.removeOne(op);
+ });
+ m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(writeRequest)));
+}
+
+QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data)
+{
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage(std::move(data));
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->offset = offset;
+ priv->type = QIOOperation::Type::Write;
+
+ auto *op = new QIOWriteOperation(*priv, q_ptr);
+ m_operations.append(op);
+
+ startWriteFromSingle(op, as_bytes(QSpan(dataStorage->getByteArray())));
+
+ return op;
+}
+
+QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset,
+ QSpan<std::byte> buffer)
+{
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage(
+ QSpan<const QSpan<std::byte>>{ buffer });
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->offset = offset;
+ priv->type = QIOOperation::Type::Read;
+
+ auto *op = new QIOVectoredReadOperation(*priv, q_ptr);
+ m_operations.append(op);
+
+ startReadIntoSingle(op, dataStorage->getReadSpans().first());
+
+ return op;
+}
+
+QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset,
+ QSpan<const std::byte> buffer)
+{
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage(
+ QSpan<const QSpan<const std::byte>>{ buffer });
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->offset = offset;
+ priv->type = QIOOperation::Type::Write;
+
+ auto *op = new QIOVectoredWriteOperation(*priv, q_ptr);
+ m_operations.append(op);
+
+ startWriteFromSingle(op, dataStorage->getWriteSpans().first());
+
+ return op;
+}
+
+QIOVectoredReadOperation *
+QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers)
+{
+ if (!QIORing::supportsOperation(QtPrivate::Operation::VectoredRead))
+ return nullptr;
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage(buffers);
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->offset = offset;
+ priv->type = QIOOperation::Type::Read;
+
+ auto *op = new QIOVectoredReadOperation(*priv, q_ptr);
+ if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now
+ queueCompletion(priv, QIOOperation::Error::IncorrectOffset);
+ return op;
+ }
+ m_operations.append(op);
+
+ QIORingRequest<QIORing::Operation::VectoredRead> readRequest;
+ readRequest.fd = m_fd;
+ readRequest.offset = priv->offset;
+ readRequest.destinations = dataStorage->getReadSpans();
+ readRequest.setCallback([this,
+ op](const QIORingRequest<QIORing::Operation::VectoredRead> &request) {
+ auto *priv = QIOOperationPrivate::get(op);
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ if (priv->error == QIOOperation::Error::Aborted || *err == QFileDevice::AbortError)
+ queueCompletion(priv, QIOOperation::Error::Aborted);
+ else
+ queueCompletion(priv, QIOOperation::Error::Read);
+ } else if (const auto
+ *result = std::get_if<QIORingResult<QIORing::Operation::VectoredRead>>(
+ &request.result)) {
+ priv->appendBytesProcessed(result->bytesRead);
+ qint64 processed = result->bytesRead;
+ for (auto &span : priv->dataStorage->getReadSpans()) {
+ if (span.size() < processed) {
+ processed -= span.size();
+ } else { // span.size >= processed
+ span.slice(0, processed);
+ processed = 0;
+ }
+ }
+ queueCompletion(priv, QIOOperation::Error::None);
+ }
+ m_operations.removeOne(op);
+ });
+ m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(readRequest)));
+
+ return op;
+}
+
+QIOVectoredWriteOperation *
+QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers)
+{
+ if (!QIORing::supportsOperation(QtPrivate::Operation::VectoredWrite))
+ return nullptr;
+ auto *dataStorage = new QtPrivate::QIOOperationDataStorage(buffers);
+
+ auto *priv = new QIOOperationPrivate(dataStorage);
+ priv->offset = offset;
+ priv->type = QIOOperation::Type::Write;
+
+ auto *op = new QIOVectoredWriteOperation(*priv, q_ptr);
+ if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now
+ queueCompletion(priv, QIOOperation::Error::IncorrectOffset);
+ return op;
+ }
+ m_operations.append(op);
+
+ QIORingRequest<QIORing::Operation::VectoredWrite> writeRequest;
+ writeRequest.fd = m_fd;
+ writeRequest.offset = priv->offset;
+ writeRequest.sources = dataStorage->getWriteSpans();
+ writeRequest.setCallback(
+ [this, op](const QIORingRequest<QIORing::Operation::VectoredWrite> &request) {
+ auto *priv = QIOOperationPrivate::get(op);
+ if (const auto *err = std::get_if<QFileDevice::FileError>(&request.result)) {
+ if (priv->error == QIOOperation::Error::Aborted || *err == QFileDevice::AbortError)
+ queueCompletion(priv, QIOOperation::Error::Aborted);
+ else
+ queueCompletion(priv, QIOOperation::Error::Write);
+ } else if (const auto *result = std::get_if<
+ QIORingResult<QIORing::Operation::VectoredWrite>>(
+ &request.result)) {
+ priv->appendBytesProcessed(result->bytesWritten);
+ queueCompletion(priv, QIOOperation::Error::None);
+ }
+ m_operations.removeOne(op);
+ });
+ m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(writeRequest)));
+
+ return op;
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp b/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp
index 6dceb583469..bcc30f98700 100644
--- a/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp
+++ b/tests/auto/corelib/io/qrandomaccessasyncfile/tst_qrandomaccessasyncfile.cpp
@@ -78,8 +78,9 @@ void tst_QRandomAccessAsyncFile::initTestCase()
QVERIFY(m_file.open());
QByteArray data(FileSize, Qt::Uninitialized);
+ auto *ptr = data.data();
for (qsizetype i = 0; i < FileSize; ++i)
- data[i] = char(i % 256);
+ ptr[i] = char(i % 256);
qint64 written = m_file.write(data);
QCOMPARE_EQ(written, FileSize);
@@ -88,7 +89,13 @@ void tst_QRandomAccessAsyncFile::initTestCase()
void tst_QRandomAccessAsyncFile::cleanupTestCase()
{
m_file.close();
- QVERIFY(m_file.remove());
+ using namespace std::chrono_literals;
+ QDeadlineTimer dt(10s);
+ // Loop a little bit in case there is an access race on Windows:
+ bool success = false;
+ while (!(success = m_file.remove()) && !dt.hasExpired())
+ QThread::msleep(100);
+ QVERIFY2(success, qPrintable(m_file.errorString()));
}
void tst_QRandomAccessAsyncFile::size()
@@ -586,13 +593,16 @@ void tst_QRandomAccessAsyncFile::fileClosedInProgress()
}
file.close();
- auto isAbortedOrComplete = [](QIOOperation *op) {
- return op->error() == QIOOperation::Error::Aborted
- || op->error() == QIOOperation::Error::None;
+ auto isAbortedOrCompleteOrFailedToOpen = [](QIOOperation *op) {
+ return op->error() == QIOOperation::Error::Aborted // Aborted
+ || op->error() == QIOOperation::Error::Open // Failed, because other op is in progress
+ || op->error() == QIOOperation::Error::None; // Completed
};
for (auto op : operations) {
QTRY_VERIFY(op->isFinished());
- QVERIFY(isAbortedOrComplete(op));
+ QVERIFY2(isAbortedOrCompleteOrFailedToOpen(op),
+ qPrintable("Expected Aborted, Open or None, got %1"_L1.arg(
+ QDebug::toString(op->error()))));
}
}