Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qrandomaccessasyncfile_p_p.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QRANDOMACCESSASYNCFILE_P_P_H
6#define QRANDOMACCESSASYNCFILE_P_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
20
21#include <QtCore/private/qobject_p.h>
22
23#include <QtCore/qstring.h>
24
25#if QT_CONFIG(future) && QT_CONFIG(thread)
26
27#include <QtCore/private/qfsfileengine_p.h>
28
29#include <QtCore/qfuturewatcher.h>
30#include <QtCore/qmutex.h>
31#include <QtCore/qqueue.h>
32
33#endif // future && thread
34
35#ifdef Q_OS_DARWIN
36
37#include <QtCore/qlist.h>
38#include <QtCore/qmutex.h>
39#include <QtCore/qset.h>
40#include <QtCore/qwaitcondition.h>
41
42#include <dispatch/dispatch.h>
43
44#endif // Q_OS_DARWIN
45
46#ifdef QT_RANDOMACCESSASYNCFILE_QIORING
47#include <QtCore/private/qioring_p.h>
48#include <QtCore/qlist.h>
49#endif
50
52
54{
56public:
59
60 virtual bool init() = 0;
61 virtual void cancelAndWait(QIOOperation *op) = 0;
62
63 virtual void close() = 0;
64 virtual qint64 size() const = 0;
65
66 [[nodiscard]] virtual QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) = 0;
67 [[nodiscard]] virtual QIOOperation *flush() = 0;
68
69 [[nodiscard]] virtual QIOReadOperation *read(qint64 offset, qint64 maxSize) = 0;
70 [[nodiscard]] virtual QIOWriteOperation *write(qint64 offset, const QByteArray &data) = 0;
71 [[nodiscard]] virtual QIOWriteOperation *write(qint64 offset, QByteArray &&data) = 0;
72
73 [[nodiscard]] virtual QIOVectoredReadOperation *
74 readInto(qint64 offset, QSpan<std::byte> buffer) = 0;
75 [[nodiscard]] virtual QIOVectoredWriteOperation *
76 writeFrom(qint64 offset, QSpan<const std::byte> buffer) = 0;
77
78 [[nodiscard]] virtual QIOVectoredReadOperation *
79 readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) = 0;
80 [[nodiscard]] virtual QIOVectoredWriteOperation *
81 writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) = 0;
82protected:
83 // common for all backends
84 enum class FileState : quint8
85 {
87 OpenPending, // already got an open request
89 };
90
95};
96
98{
99 Q_DECLARE_PUBLIC(QRandomAccessAsyncFile)
101public:
104
105 static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file)
106 { return file->d_func(); }
107
108 void init();
109 void cancelAndWait(QIOOperation *op)
110 {
111 checkValid();
112 m_backend->cancelAndWait(op);
113 }
114
115 void close()
116 {
117 checkValid();
118 m_backend->close();
119 }
120 qint64 size() const
121 {
122 checkValid();
123 return m_backend->size();
124 }
125
126 [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode)
127 {
128 checkValid();
129 return m_backend->open(path, mode);
130 }
131 [[nodiscard]] QIOOperation *flush()
132 {
133 checkValid();
134 return m_backend->flush();
135 }
136
137 [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize)
138 {
139 checkValid();
140 return m_backend->read(offset, maxSize);
141 }
142 [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data)
143 {
144 checkValid();
145 return m_backend->write(offset, data);
146 }
147 [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data)
148 {
149 checkValid();
150 return m_backend->write(offset, std::move(data));
151 }
152
153 [[nodiscard]] QIOVectoredReadOperation *readInto(qint64 offset, QSpan<std::byte> buffer)
154 {
155 checkValid();
156 return m_backend->readInto(offset, buffer);
157 }
158 [[nodiscard]] QIOVectoredWriteOperation *writeFrom(qint64 offset, QSpan<const std::byte> buffer)
159 {
160 checkValid();
161 return m_backend->writeFrom(offset, buffer);
162 }
163
164 [[nodiscard]] QIOVectoredReadOperation *readInto(qint64 offset,
165 QSpan<const QSpan<std::byte>> buffers)
166 {
167 checkValid();
168 return m_backend->readInto(offset, buffers);
169 }
170 [[nodiscard]] QIOVectoredWriteOperation *writeFrom(qint64 offset,
171 QSpan<const QSpan<const std::byte>> buffers)
172 {
173 checkValid();
174 return m_backend->writeFrom(offset, buffers);
175 }
176
177private:
178 void checkValid() const { Q_ASSERT(m_backend); }
179 std::unique_ptr<QRandomAccessAsyncFileBackend> m_backend;
180
181};
182
183
184#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) || defined(Q_OS_DARWIN)
185class QRandomAccessAsyncFileNativeBackend final : public QRandomAccessAsyncFileBackend
186{
187 Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileNativeBackend)
188public:
189 explicit QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner);
190 ~QRandomAccessAsyncFileNativeBackend();
191
192 bool init() override;
193 void cancelAndWait(QIOOperation *op) override;
194
195 void close() override;
196 qint64 size() const override;
197
198 [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) override;
199 [[nodiscard]] QIOOperation *flush() override;
200
201 [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) override;
202 [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) override;
203 [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) override;
204
205 [[nodiscard]] QIOVectoredReadOperation *
206 readInto(qint64 offset, QSpan<std::byte> buffer) override;
207 [[nodiscard]] QIOVectoredWriteOperation *
208 writeFrom(qint64 offset, QSpan<const std::byte> buffer) override;
209
210 [[nodiscard]] QIOVectoredReadOperation *
211 readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) override;
212 [[nodiscard]] QIOVectoredWriteOperation *
213 writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) override;
214
215private:
216#if defined(QT_RANDOMACCESSASYNCFILE_QIORING)
217 void queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error);
218 void startReadIntoSingle(QIOOperation *op, const QSpan<std::byte> &to);
219 void startWriteFromSingle(QIOOperation *op, const QSpan<const std::byte> &from);
220 QIORing::RequestHandle cancel(QIORing::RequestHandle handle);
221 QIORing *m_ioring = nullptr;
222 qintptr m_fd = -1;
223 QList<QPointer<QIOOperation>> m_operations;
224 QHash<QIOOperation *, QIORing::RequestHandle> m_opHandleMap;
225#endif
226#ifdef Q_OS_DARWIN
227 using OperationId = quint64;
228 static constexpr OperationId kInvalidOperationId = 0;
229 static constexpr OperationId kAllOperationIds = std::numeric_limits<OperationId>::max();
230
231 struct OperationResult
232 {
233 OperationId opId;
234 qint64 result; // num bytes processed or file descriptor
235 int error;
236 };
237
238 enum class OpState : quint8
239 {
240 Pending,
241 Running,
242 };
243
244 struct OperationInfo
245 {
246 OperationId opId;
247 dispatch_io_t channel;
248 QPointer<QIOOperation> operation;
249 OpState state;
250
251 OperationInfo(OperationId _id, QIOOperation *_op)
252 : opId(_id),
253 channel(nullptr),
254 operation(_op),
255 state(OpState::Pending)
256 {}
257 };
258
259 // We need to maintain an actual queue of the operations, because
260 // certain operations (i.e. flush) should act like barriers. The docs
261 // for dispatch_io_barrier mention that it can synchronize between multiple
262 // channels handling the same file descriptor, but that DOES NOT work in
263 // practice. It works correctly only when there's a signle IO channel. But
264 // with a signle IO channel we're not able to cancel individual operations.
265 // As a result, we need to make sure that all previous operations are
266 // completed before starting a barrier operation. Similarly, we cannot start
267 // any other operation until a barrier operation is finished.
268 QList<OperationInfo> m_operations;
269 dispatch_io_t m_ioChannel = nullptr;
270 int m_fd = -1;
271
272 QMutex m_mutex;
273 // the members below should only be accessed with the mutex
274 OperationId m_opToCancel = kInvalidOperationId;
275 QSet<OperationId> m_runningOps;
276 qsizetype m_numChannelsToClose = 0;
277 QWaitCondition m_cancellationCondition;
278
279 static OperationId getNextId();
280
281 template <typename Operation, typename ...Args>
282 Operation *addOperation(QIOOperation::Type type, qint64 offset, Args &&...args);
283
284 void notifyIfOperationsAreCompleted();
285 dispatch_io_t createMainChannel(int fd);
286 dispatch_io_t duplicateIoChannel(OperationId opId);
287 void closeIoChannel(dispatch_io_t channel);
288 void releaseIoChannel(dispatch_io_t channel);
289 void handleOperationComplete(const OperationResult &opResult);
290
291 void queueCompletion(OperationId opId, int error);
292
293 void startOperationsUntilBarrier();
294 void executeRead(OperationInfo &opInfo);
295 void executeWrite(OperationInfo &opInfo);
296 void executeFlush(OperationInfo &opInfo);
297 void executeOpen(OperationInfo &opInfo);
298
299 void readOneBuffer(OperationId opId, qsizetype bufferIdx, qint64 alreadyRead);
300 void readOneBufferHelper(OperationId opId, dispatch_io_t channel, qint64 offset,
301 void *bytesPtr, qint64 maxSize, qsizetype currentBufferIdx,
302 qsizetype totalBuffers, qint64 alreadyRead);
303 void writeHelper(OperationId opId, dispatch_io_t channel, qint64 offset,
304 dispatch_data_t dataToWrite, qint64 dataSize);
305#endif
306};
307#endif // QIORing || macOS
308
309#if QT_CONFIG(future) && QT_CONFIG(thread)
310class QRandomAccessAsyncFileThreadPoolBackend : public QRandomAccessAsyncFileBackend
311{
312 Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileThreadPoolBackend)
313public:
314 explicit QRandomAccessAsyncFileThreadPoolBackend(QRandomAccessAsyncFile *owner);
315 ~QRandomAccessAsyncFileThreadPoolBackend();
316
317 bool init() override;
318 void cancelAndWait(QIOOperation *op) override;
319
320 void close() override;
321 qint64 size() const override;
322
323 [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) override;
324 [[nodiscard]] QIOOperation *flush() override;
325
326 [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) override;
327 [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) override;
328 [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) override;
329
330 [[nodiscard]] QIOVectoredReadOperation *
331 readInto(qint64 offset, QSpan<std::byte> buffer) override;
332 [[nodiscard]] QIOVectoredWriteOperation *
333 writeFrom(qint64 offset, QSpan<const std::byte> buffer) override;
334
335 [[nodiscard]] QIOVectoredReadOperation *
336 readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) override;
337 [[nodiscard]] QIOVectoredWriteOperation *
338 writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) override;
339
340 struct OperationResult
341 {
342 qint64 bytesProcessed; // either read or written
343 QIOOperation::Error error;
344 };
345private:
346
347 mutable QBasicMutex m_engineMutex;
348 std::unique_ptr<QFSFileEngine> m_engine;
349 QFutureWatcher<OperationResult> m_watcher;
350
351 QQueue<QPointer<QIOOperation>> m_operations;
352 QPointer<QIOOperation> m_currentOperation;
353 qsizetype numProcessedBuffers = 0;
354
355 void executeNextOperation();
356 void processBufferAt(qsizetype idx);
357 void processFlush();
358 void processOpen();
359 void operationComplete();
360};
361#endif // future && thread
362
363QT_END_NAMESPACE
364
365#endif // QRANDOMACCESSASYNCFILE_P_P_H
virtual QIOOperation * open(const QString &path, QIODeviceBase::OpenMode mode)=0
virtual QIOReadOperation * read(qint64 offset, qint64 maxSize)=0
virtual QIOWriteOperation * write(qint64 offset, QByteArray &&data)=0
virtual QIOOperation * flush()=0
virtual qint64 size() const =0
virtual QIOVectoredReadOperation * readInto(qint64 offset, QSpan< std::byte > buffer)=0
virtual void cancelAndWait(QIOOperation *op)=0
virtual QIOVectoredWriteOperation * writeFrom(qint64 offset, QSpan< const std::byte > buffer)=0
virtual QIOWriteOperation * write(qint64 offset, const QByteArray &data)=0
static QRandomAccessAsyncFilePrivate * get(QRandomAccessAsyncFile *file)
QIOOperation * open(const QString &path, QIODeviceBase::OpenMode mode)
~QRandomAccessAsyncFilePrivate() override
QIOReadOperation * read(qint64 offset, qint64 maxSize)
QIOVectoredWriteOperation * writeFrom(qint64 offset, QSpan< const std::byte > buffer)
QIOWriteOperation * write(qint64 offset, QByteArray &&data)
QIOWriteOperation * write(qint64 offset, const QByteArray &data)
QIOVectoredReadOperation * readInto(qint64 offset, QSpan< std::byte > buffer)
Combined button and popup list for selecting options.