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
qioring.cpp
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#include "qioring_p.h"
6
7#include <QtCore/q26numeric.h>
8
10
11Q_LOGGING_CATEGORY(lcQIORing, "qt.core.ioring", QtCriticalMsg)
12
13QIORing *QIORing::sharedInstance()
14{
15 thread_local QIORing instance;
16 if (!instance.initializeIORing())
17 return nullptr;
18 return &instance;
19}
20
21QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize)
22 : sqEntries(submissionQueueSize), cqEntries(completionQueueSize)
23{
24 // Destructor in respective _<platform>.cpp
25}
26
27auto QIORing::queueRequestInternal(GenericRequestType &request) -> QueuedRequestStatus
28{
29 if (!ensureInitialized() || preparingRequests) { // preparingRequests protects against recursing
30 // inside callbacks of synchronous completions.
31 finishRequestWithError(request, QFileDevice::ResourceError);
32 addrItMap.remove(&request);
33 return QueuedRequestStatus::CompletedImmediately;
34 }
35 if (!lastUnqueuedIterator) {
36 lastUnqueuedIterator.emplace(addrItMap[&request]);
37 } else if (request.operation() == QtPrivate::Operation::Cancel) {
38 // We want to fast-track cancellations because they may be cancelling
39 // unqueued things, so we push it up front in the queue:
40 auto &it = addrItMap[&request];
41 const auto where = *lastUnqueuedIterator;
42 pendingRequests.splice(where, pendingRequests, it);
43 it = std::prev(where);
44 lastUnqueuedIterator.emplace(it);
45 }
46
47 qCDebug(lcQIORing) << "Trying to submit request" << request.operation()
48 << "user data:" << std::addressof(request);
49 prepareRequests();
50 // If this is now true we have, in some way, fulfilled the request:
51 const bool requestCompleted = !addrItMap.contains(&request);
52 const QueuedRequestStatus requestQueuedState = requestCompleted
53 ? QueuedRequestStatus::CompletedImmediately
54 : QueuedRequestStatus::Pending;
55 // We want to avoid notifying the kernel too often of tasks, so only do it if the queue is full,
56 // otherwise do it when we return to the event loop.
57 if (unstagedRequests == sqEntries && inFlightRequests <= cqEntries) {
58 submitRequests();
59 return requestQueuedState;
60 }
61 if (stagePending || unstagedRequests == 0)
62 return requestQueuedState;
63 stagePending = true;
64 // We are not a QObject, but we always have the notifier, so use that for context:
65 QMetaObject::invokeMethod(
66 std::addressof(*notifier), [this] { submitRequests(); }, Qt::QueuedConnection);
67 return requestQueuedState;
68}
69
70bool QIORing::waitForRequest(RequestHandle handle, QDeadlineTimer deadline)
71{
72 if (!handle || !addrItMap.contains(handle))
73 return true; // : It was never there to begin with (so it is finished)
74 if (unstagedRequests)
75 submitRequests();
76 completionReady(); // Try to process some pending completions
77 while (!deadline.hasExpired() && addrItMap.contains(handle)) {
78 if (!waitForCompletions(deadline))
79 return false;
80 completionReady();
81 }
82 return !addrItMap.contains(handle);
83}
84
85namespace QtPrivate {
86template <typename T>
87using DetectResult = decltype(std::declval<const T &>().result);
88
89template <typename T>
90constexpr bool HasResultMember = qxp::is_detected_v<DetectResult, T>;
91}
92
93void QIORing::setFileErrorResult(QIORing::GenericRequestType &req, QFileDevice::FileError error)
94{
95 invokeOnOp(req, [error](auto *concreteRequest) {
96 if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>)
97 setFileErrorResult(*concreteRequest, error);
98 });
99}
100
101void QIORing::finishRequestWithError(QIORing::GenericRequestType &req, QFileDevice::FileError error)
102{
103 invokeOnOp(req, [error](auto *concreteRequest) {
104 if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>)
105 setFileErrorResult(*concreteRequest, error);
106 invokeCallback(*concreteRequest);
107 });
108}
109
110QIORing::ReadWriteStatus QIORing::handleReadCompletion(size_t value, QSpan<std::byte> *destinations,
111 void *voidExtra, SetResultFn setResultFn)
112{
113 if (value == 0) {
114 // Since we are reading, presumably this indicates EOF.
115 // In case this is our only callback, notify that it at least wasn't a
116 // failure:
117 setResultFn(qint64(0));
118 return ReadWriteStatus::Finished;
119 }
120 if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) {
121 const qsizetype bytesRead = q26::saturate_cast<qsizetype>(value);
122 qCDebug(lcQIORing) << "Partial read of" << bytesRead << "bytes completed";
123 extra->totalProcessed = setResultFn(bytesRead);
124 // [0/1] Add the number of bytes processed to the spanOffset - we use this to test how many
125 // spans were fully processed, and/or how far into the last span we have data.
126 extra->spanOffset += bytesRead;
127 qCDebug(lcQIORing) << "Read operation progress: span" << extra->spanIndex << "offset"
128 << extra->spanOffset << "of" << destinations[extra->spanIndex].size()
129 << "bytes. Total read:" << extra->totalProcessed << "bytes";
130 // [1/1] We subtract the size of the spans, in order, here. When a span's size is larger
131 // than the "spanOffset" it means we have a partially-used span. Otherwise it stops
132 // processing when we run out of spans to check, or we hit spanOffset == 0.
133 while (extra->spanOffset >= destinations[extra->spanIndex].size()) {
134 extra->spanOffset -= destinations[extra->spanIndex].size();
135 // Move to next span
136 if (++extra->spanIndex == extra->numSpans)
137 return ReadWriteStatus::Finished;
138 }
139 return ReadWriteStatus::MoreToDo;
140 }
141 setResultFn(q26::saturate_cast<qsizetype>(value));
142 return ReadWriteStatus::Finished;
143}
144
145QIORing::ReadWriteStatus QIORing::handleWriteCompletion(size_t value,
146 const QSpan<const std::byte> *sources,
147 void *voidExtra, SetResultFn setResultFn)
148{
149 if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) {
150 const qsizetype bytesWritten = q26::saturate_cast<qsizetype>(value);
151 qCDebug(lcQIORing) << "Partial write of" << bytesWritten << "bytes completed";
152 extra->totalProcessed = setResultFn(bytesWritten);
153 // [0/1] Add the number of bytes processed to the spanOffset - we use this to test how many
154 // spans were fully processed, and/or how far into the last span we have data.
155 extra->spanOffset += bytesWritten;
156 qCDebug(lcQIORing) << "Write operation progress: span" << extra->spanIndex << "offset"
157 << extra->spanOffset << "of" << sources[extra->spanIndex].size()
158 << "bytes. Total written:" << extra->totalProcessed << "bytes";
159 // [1/1] We subtract the size of the spans, in order, here. When a span's size is larger
160 // than the "spanOffset" it means we have a partially-used span. Otherwise it stops
161 // processing when we run out of spans to check, or we hit spanOffset == 0.
162 while (extra->spanOffset >= sources[extra->spanIndex].size()) {
163 extra->spanOffset -= sources[extra->spanIndex].size();
164 // Move to next span
165 if (++extra->spanIndex == extra->numSpans)
166 return ReadWriteStatus::Finished;
167 }
168 return ReadWriteStatus::MoreToDo;
169 }
170 setResultFn(q26::saturate_cast<qsizetype>(value));
171 return ReadWriteStatus::Finished;
172}
173
174void QIORing::finalizeReadWriteCompletion(GenericRequestType *request, ReadWriteStatus rwstatus)
175{
176 switch (rwstatus) {
177 case ReadWriteStatus::Finished:
178 if (request->getExtra<void>())
179 --ongoingSplitOperations;
180 break;
181 case ReadWriteStatus::MoreToDo: {
182 // Move the request such that it is next in the list to be processed:
183 auto &it = addrItMap[request];
184 const auto where = lastUnqueuedIterator.value_or(pendingRequests.end());
185 pendingRequests.splice(where, pendingRequests, it);
186 it = std::prev(where);
187 lastUnqueuedIterator = it;
188 break;
189 }
190 }
191}
192
193QT_END_NAMESPACE
194
195#include "moc_qioring_p.cpp"
RequestHandleTag * RequestHandle
Definition qioring_p.h:115
bool ensureInitialized()
Definition qioring_p.h:126
Q_CORE_EXPORT bool waitForRequest(RequestHandle handle, QDeadlineTimer deadline=QDeadlineTimer::Forever)
Definition qioring.cpp:70
Combined button and popup list for selecting options.
decltype(std::declval< const T & >().result) DetectResult
Definition qioring.cpp:87
constexpr bool HasResultMember
Definition qioring.cpp:90