19#include <QtCore/private/qtcoreglobal_p.h>
21#include <QtCore/qstring.h>
22#include <QtCore/qspan.h>
23#include <QtCore/qhash.h>
24#include <QtCore/qfiledevice.h>
25#include <QtCore/qloggingcategory.h>
26#include <QtCore/qdeadlinetimer.h>
29# include <QtCore/qsocketnotifier.h>
32#elif defined(Q_OS_WIN)
33# include <QtCore/qwineventnotifier.h>
34# include <qt_windows.h>
35# include <ioringapi.h>
40#include <QtCore/qxpfunctional.h>
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
72#define FOREACH_IO_OPERATION(OP)
83#define DEFINE_ENTRY(OP) OP,
110 struct RequestHandleTag;
118 explicit QIORing(quint32 submissionQueueSize = DefaultSubmissionQueueSize,
119 quint32 completionQueueSize = DefaultCompletionQueueSize);
122 Q_DISABLE_COPY_MOVE(QIORing)
125 static QIORing *sharedInstance();
133 Q_ASSERT(supportsOperation(Op));
134 auto &r = pendingRequests.emplace_back(std::move(request));
135 addrItMap.emplace(&r, std::prev(pendingRequests.end()));
136 if (queueRequestInternal(r) == QueuedRequestStatus::CompletedImmediately)
149 std::list<GenericRequestType> pendingRequests;
150 using PendingRequestsIterator =
decltype(pendingRequests.begin());
151 QHash<
void *, PendingRequestsIterator> addrItMap;
152 std::optional<PendingRequestsIterator> lastUnqueuedIterator;
153 quint32 sqEntries = 0;
154 quint32 cqEntries = 0;
155 quint32 inFlightRequests = 0;
156 quint32 unstagedRequests = 0;
157 bool stagePending =
false;
158 bool preparingRequests =
false;
159 qsizetype ongoingSplitOperations = 0;
162 bool initializeIORing();
164 enum class QueuedRequestStatus :
bool {
166 CompletedImmediately =
true,
169 QueuedRequestStatus queueRequestInternal(GenericRequestType &request);
170 void prepareRequests();
171 void completionReady();
172 bool waitForCompletions(QDeadlineTimer deadline);
174 template <
typename Fun>
178 static void setFileErrorResult(
QIORingRequest<Op> &req, QFileDevice::FileError error)
180 req.result.
template emplace<QFileDevice::FileError>(error);
182 static void setFileErrorResult(
GenericRequestType &req, QFileDevice::FileError error);
183 static void finishRequestWithError(
GenericRequestType &req, QFileDevice::FileError error);
186 enum RequestPrepResult : quint8 {
192 enum class ReadWriteStatus :
bool {
197 using NativeResultType = qint32;
198 static constexpr bool isResultFailure(NativeResultType result) {
return result < 0; }
200 std::optional<QSocketNotifier> notifier;
202 void *submissionQueue =
nullptr;
203 io_uring_sqe *submissionQueueEntries =
nullptr;
204 const io_uring_cqe *completionQueueEntries =
nullptr;
208 const quint32 *sqHead =
nullptr;
209 quint32 *sqTail =
nullptr;
210 const quint32 *sqIndexMask =
nullptr;
211 quint32 *sqIndexArray =
nullptr;
212 quint32 *cqHead =
nullptr;
213 const quint32 *cqTail =
nullptr;
214 const quint32 *cqIndexMask =
nullptr;
219 bool flushInProgress =
false;
222 int eventDescriptor = -1;
224 RequestPrepResult prepareRequest(io_uring_sqe *sqe, GenericRequestType &request);
226 template <
typename SpanOfBytes>
227 auto getVectoredOpAddressAndSize(QIORing::GenericRequestType &request,
228 QSpan<SpanOfBytes> spans);
229#elif defined(Q_OS_WIN)
231 static constexpr qsizetype MaxReadWriteLen = std::numeric_limits<UINT32>::max();
232 using NativeResultType = HRESULT;
233 static constexpr bool isResultFailure(NativeResultType result)
235 return FAILED(result) && result != HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
238 std::optional<QWinEventNotifier> notifier;
239 HIORING ioRingHandle =
nullptr;
240 HANDLE eventHandle = INVALID_HANDLE_VALUE;
241 const QtPrivate::IORingApiTable *apiTable;
243 bool initialized =
false;
244 bool queueWasFull =
false;
247 RequestPrepResult prepareRequest(GenericRequestType &request);
249 static QFileDevice::FileError mapFileError(NativeResultType result,
250 QFileDevice::FileError defaultValue);
251 using SetResultFn = qxp::function_ref<qint64(qint64)>;
252 static ReadWriteStatus handleReadCompletion(size_t value, QSpan<std::byte> *destinations,
253 void *voidExtra, SetResultFn setResult);
255 ReadWriteStatus handleReadCompletion(NativeResultType result, size_t value,
257 static ReadWriteStatus handleWriteCompletion(size_t value,
258 const QSpan<
const std::byte> *sources,
259 void *voidExtra, SetResultFn setResult);
261 ReadWriteStatus handleWriteCompletion(NativeResultType result, size_t value,
263 void finalizeReadWriteCompletion(
GenericRequestType *request, ReadWriteStatus rwstatus);
276template <QtPrivate::Operation Op>
277using ExpectedResultType = std::variant<std::monostate, QIORingResult<Op>, QFileDevice::FileError>;
285template <QtPrivate::Operation Op,
typename Base = QIORingRequestOffsetFdBase>
290 template <
typename Func>
294 callback.reset(QtPrivate::makeCallableObject<Prototype>(std::forward<Func>(func)));
306 : QIORingRequestBase<QtPrivate::Operation::Open, QIORingRequestEmptyBase>
308 std::filesystem::path path;
317 : QIORingRequestBase<QtPrivate::Operation::Close, QIORingRequestEmptyBase>
330 : QIORingRequestBase<QtPrivate::Operation::Write>
341 : QIORingRequestBase<QtPrivate::Operation::VectoredWrite>
353 : QIORingRequestBase<QtPrivate::Operation::Read>
365 : QIORingRequestBase<QtPrivate::Operation::VectoredRead>
376struct QIORingRequest<
QtPrivate::
Operation::Flush>
final : QIORingRequestBase<QtPrivate::Operation::Flush, QIORingRequestEmptyBase>
389 : QIORingRequestBase<QtPrivate::Operation::Stat, QIORingRequestEmptyBase>
403 template <
typename Func>
408 callback.reset(QtPrivate::makeCallableObject<Prototype>(std::forward<Func>(func)));
415 if (!request.callback)
417 void *args[2] = {
nullptr,
const_cast<
QIORingRequest<Op> *>(&request) };
418 request.callback->call(
nullptr, args);
423 friend class QIORing;
425#define POPULATE_VARIANT(Op)
426 QIORingRequest<Operation::Op>,
434#undef POPULATE_VARIANT
436 void *extraData =
nullptr;
440 Q_ALWAYS_INLINE
void initializeStorage(
QIORingRequest<Op> &&t)
noexcept
443 taggedUnion.emplace<QIORingRequest<Op>>(std::move(t));
447 static void cleanupExtra(
Operation op,
void *extra);
448 template <
typename T>
449 T *getOrInitializeExtra()
453 return static_cast<T *>(extraData);
455 template <
typename T>
458 return static_cast<T *>(extraData);
460 void reset()
noexcept
463 taggedUnion.emplace<std::monostate>();
465 cleanupExtra(op, std::exchange(extraData,
nullptr));
472 initializeStorage(
std::move(t));
497 Q_ASSERT(
"Wrong operation requested, see operation()");
505 Q_ASSERT(
"Wrong operation requested, see operation()");
512template <
typename Fun>
515#define INVOKE_ON_OP(Op) case
516 QIORing::Operation::Op:
517 fn(req.template requestData<Operation::Op>());
521 switch (req.operation()) {
532QIORing::ReadWriteStatus QIORing::handleReadCompletion(NativeResultType result, size_t value,
537 Q_ASSERT(readRequest);
539 if (isResultFailure(result)) {
540 QFileDevice::FileError fileError = mapFileError(result, QFileDevice::ReadError);
541 QIORing::setFileErrorResult(*readRequest, fileError);
542 return ReadWriteStatus::Finished;
545 auto setResult = [readRequest](qint64 bytesRead) {
549 return readRequest->result.
template emplace<
QIORingResult<Op>>();
551 readResult.bytesRead += bytesRead;
552 return readResult.bytesRead;
555 auto *destinations = [&readRequest]() {
557 return &readRequest->destination;
559 return &readRequest->destinations[0];
562 QIORing::ReadWriteStatus rwstatus = handleReadCompletion(value, destinations,
563 request->getExtra<
void>(), setResult);
564 finalizeReadWriteCompletion(request, rwstatus);
569QIORing::ReadWriteStatus QIORing::handleWriteCompletion(NativeResultType result, size_t value,
574 Q_ASSERT(writeRequest);
576 if (isResultFailure(result)) {
577 QFileDevice::FileError fileError = mapFileError(result, QFileDevice::WriteError);
578 QIORing::setFileErrorResult(*writeRequest, fileError);
579 return ReadWriteStatus::Finished;
582 auto setResult = [writeRequest](qint64 bytesWritten) {
583 auto &writeResult = [&writeRequest]() ->
QIORingResult<Op> & {
586 return writeRequest->result.
template emplace<
QIORingResult<Op>>();
588 writeResult.bytesWritten += bytesWritten;
589 return writeResult.bytesWritten;
591 auto *sources = [&writeRequest]() {
593 return &writeRequest->source;
595 return &writeRequest->sources[0];
597 QIORing::ReadWriteStatus rwstatus = handleWriteCompletion(value, sources,
598 request->getExtra<
void>(), setResult);
599 finalizeReadWriteCompletion(request, rwstatus);
GenericRequestType(QIORingRequest< Op > &&t) noexcept
~GenericRequestType() noexcept
Q_CORE_EXPORT void submitRequests()
RequestHandleTag * RequestHandle
static Q_CORE_EXPORT bool supportsOperation(Operation op)
static constexpr quint32 DefaultSubmissionQueueSize
static constexpr quint32 DefaultCompletionQueueSize
quint32 completionQueueSize() const noexcept
quint32 submissionQueueSize() const noexcept
QIORing::RequestHandle queueRequest(QIORingRequest< Op > &&request)
Q_CORE_EXPORT bool waitForRequest(RequestHandle handle, QDeadlineTimer deadline=QDeadlineTimer::Forever)
QtPrivate::Operation Operation
Combined button and popup list for selecting options.
decltype(std::declval< const T & >().result) DetectResult
constexpr bool HasResultMember
Q_ALWAYS_INLINE void invokeCallback(const QIORingRequest< Op > &request)
#define FOREACH_IO_OPERATION(OP)
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)
ExpectedResultType< Op > result
Q_ALWAYS_INLINE void setCallback(Func &&func)
QtPrivate::SlotObjUniquePtr callback
Q_ALWAYS_INLINE void setCallback(Func &&func)
QtPrivate::SlotObjUniquePtr callback
QIORing::RequestHandle handle
QFileDevice::OpenMode flags
QSpan< std::byte > destination
QSpan< QSpan< std::byte > > destinations
QSpan< const QSpan< const std::byte > > sources
QSpan< const std::byte > source