1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
// 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
#ifndef QRANDOMACCESSASYNCFILE_P_P_H
#define QRANDOMACCESSASYNCFILE_P_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrandomaccessasyncfile_p.h"
#include <QtCore/private/qobject_p.h>
#include <QtCore/qstring.h>
#ifdef QT_RANDOMACCESSASYNCFILE_THREAD
#include <QtCore/private/qfsfileengine_p.h>
#include <QtCore/qfuturewatcher.h>
#include <QtCore/qmutex.h>
#include <QtCore/qqueue.h>
#endif // QT_RANDOMACCESSASYNCFILE_THREAD
#ifdef Q_OS_DARWIN
#include <QtCore/qlist.h>
#include <QtCore/qmutex.h>
#include <QtCore/qset.h>
#include <QtCore/qwaitcondition.h>
#include <dispatch/dispatch.h>
#endif // Q_OS_DARWIN
QT_BEGIN_NAMESPACE
class QRandomAccessAsyncFilePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QRandomAccessAsyncFile)
Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFilePrivate)
public:
QRandomAccessAsyncFilePrivate();
~QRandomAccessAsyncFilePrivate() override;
static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file)
{ return file->d_func(); }
void init();
void cancelAndWait(QIOOperation *op);
void close();
qint64 size() const;
[[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode);
[[nodiscard]] QIOOperation *flush();
[[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize);
[[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data);
[[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data);
[[nodiscard]] QIOVectoredReadOperation *
readInto(qint64 offset, QSpan<std::byte> buffer);
[[nodiscard]] QIOVectoredWriteOperation *
writeFrom(qint64 offset, QSpan<const std::byte> buffer);
[[nodiscard]] QIOVectoredReadOperation *
readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers);
[[nodiscard]] QIOVectoredWriteOperation *
writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers);
private:
// common for all backends
enum class FileState : quint8
{
Closed,
OpenPending, // already got an open request
Opened,
};
QString m_filePath;
QIODeviceBase::OpenMode m_openMode;
FileState m_fileState = FileState::Closed;
#ifdef QT_RANDOMACCESSASYNCFILE_THREAD
public:
struct OperationResult
{
qint64 bytesProcessed; // either read or written
QIOOperation::Error error;
};
private:
mutable QBasicMutex m_engineMutex;
std::unique_ptr<QFSFileEngine> m_engine;
QFutureWatcher<OperationResult> m_watcher;
QQueue<QPointer<QIOOperation>> m_operations;
QPointer<QIOOperation> m_currentOperation;
qsizetype numProcessedBuffers = 0;
void executeNextOperation();
void processBufferAt(qsizetype idx);
void processFlush();
void processOpen();
void operationComplete();
#endif
#ifdef Q_OS_DARWIN
using OperationId = quint64;
static constexpr OperationId kInvalidOperationId = 0;
static constexpr OperationId kAllOperationIds = std::numeric_limits<OperationId>::max();
struct OperationResult
{
OperationId opId;
qint64 result; // num bytes processed or file descriptor
int error;
};
enum class OpState : quint8
{
Pending,
Running,
};
struct OperationInfo
{
OperationId opId;
dispatch_io_t channel;
QPointer<QIOOperation> operation;
OpState state;
OperationInfo(OperationId _id, QIOOperation *_op)
: opId(_id),
channel(nullptr),
operation(_op),
state(OpState::Pending)
{}
};
// We need to maintain an actual queue of the operations, because
// certain operations (i.e. flush) should act like barriers. The docs
// for dispatch_io_barrier mention that it can synchronize between multiple
// channels handling the same file descriptor, but that DOES NOT work in
// practice. It works correctly only when there's a signle IO channel. But
// with a signle IO channel we're not able to cancel individual operations.
// As a result, we need to make sure that all previous operations are
// completed before starting a barrier operation. Similarly, we cannot start
// any other operation until a barrier operation is finished.
QList<OperationInfo> m_operations;
dispatch_io_t m_ioChannel = nullptr;
int m_fd = -1;
QMutex m_mutex;
// the members below should only be accessed with the mutex
OperationId m_opToCancel = kInvalidOperationId;
QSet<OperationId> m_runningOps;
QWaitCondition m_cancellationCondition;
static OperationId getNextId();
template <typename Operation, typename ...Args>
Operation *addOperation(QIOOperation::Type type, qint64 offset, Args &&...args);
dispatch_io_t createMainChannel(int fd);
dispatch_io_t duplicateIoChannel(OperationId opId);
void closeIoChannel(dispatch_io_t channel);
void releaseIoChannel(dispatch_io_t channel);
void handleOperationComplete(const OperationResult &opResult);
void queueCompletion(OperationId opId, int error);
void startOperationsUntilBarrier();
void executeRead(OperationInfo &opInfo);
void executeWrite(OperationInfo &opInfo);
void executeFlush(OperationInfo &opInfo);
void executeOpen(OperationInfo &opInfo);
void readOneBuffer(OperationId opId, qsizetype bufferIdx, qint64 alreadyRead);
void readOneBufferHelper(OperationId opId, dispatch_io_t channel, qint64 offset,
void *bytesPtr, qint64 maxSize, qsizetype currentBufferIdx,
qsizetype totalBuffers, qint64 alreadyRead);
void writeHelper(OperationId opId, dispatch_io_t channel, qint64 offset,
dispatch_data_t dataToWrite, qint64 dataSize);
#endif
};
QT_END_NAMESPACE
#endif // QRANDOMACCESSASYNCFILE_P_P_H
|