Update 3rdparty/qcoro

This commit is contained in:
Robert Griebl
2022-01-22 19:44:09 +01:00
parent c47c68f9dd
commit 14ba43b4b9
25 changed files with 475 additions and 134 deletions

21
3rdparty/qcoro/LICENSE.MIT vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Daniel Vrátil <dvratil@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -4,7 +4,6 @@
#include "config.h"
//#include "iodevice.h"
#include "qcoroiodevice.h"
#if !defined(Q_OS_IOS)
# include "qcoroprocess.h"
@@ -13,6 +12,6 @@
#include "qcorotimer.h"
#ifdef QCORO_QT_HAS_COMPAT_ABI
#include "future.h"
#include "qcorofuture.h"
#endif

View File

@@ -31,7 +31,7 @@ private:
return mFuture.isFinished() || mFuture.isCanceled();
}
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void await_suspend(std::coroutine_handle<> awaitingCoroutine) {
auto *watcher = new QFutureWatcher<T_>();
auto cb = [watcher, awaitingCoroutine]() mutable {
watcher->deleteLater();

View File

@@ -14,7 +14,7 @@ QCoroIODevice::OperationBase::OperationBase(QIODevice *device)
: mDevice(device)
{}
void QCoroIODevice::OperationBase::finish(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroIODevice::OperationBase::finish(std::coroutine_handle<> awaitingCoroutine) {
QObject::disconnect(mConn);
QObject::disconnect(mCloseConn);
// Delayed trigger
@@ -29,7 +29,7 @@ bool QCoroIODevice::ReadOperation::await_ready() const noexcept {
mDevice->bytesAvailable() > 0;
}
void QCoroIODevice::ReadOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroIODevice::ReadOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
Q_ASSERT(mDevice);
mConn = QObject::connect(mDevice, &QIODevice::readyRead,
std::bind(&ReadOperation::finish, this, awaitingCoroutine));
@@ -62,7 +62,7 @@ bool QCoroIODevice::WriteOperation::await_ready() const noexcept {
return false;
}
void QCoroIODevice::WriteOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroIODevice::WriteOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
Q_ASSERT(mDevice);
mConn = QObject::connect(mDevice, &QIODevice::bytesWritten,
[this, awaitingCoroutine](qint64 written) {

View File

@@ -28,7 +28,7 @@ private:
protected:
explicit OperationBase(QIODevice *device);
virtual void finish(QCORO_STD::coroutine_handle<> awaitingCoroutine);
virtual void finish(std::coroutine_handle<> awaitingCoroutine);
QPointer<QIODevice> mDevice;
QMetaObject::Connection mConn;
@@ -44,7 +44,7 @@ protected:
QCORO_DEFAULT_MOVE(ReadOperation)
virtual bool await_ready() const noexcept;
virtual void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
virtual void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
QByteArray await_resume();
private:
@@ -58,7 +58,7 @@ protected:
QCORO_DEFAULT_MOVE(WriteOperation)
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
qint64 await_resume() noexcept;
private:

View File

@@ -16,7 +16,7 @@ bool QCoroProcess::WaitForStartedOperation::await_ready() const noexcept {
return !mObj || mObj->state() == QProcess::Running;
}
void QCoroProcess::WaitForStartedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroProcess::WaitForStartedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
mConn = QObject::connect(mObj, &QProcess::stateChanged,
[this, awaitingCoroutine](auto newState) mutable {
switch (newState) {
@@ -44,7 +44,7 @@ bool QCoroProcess::WaitForFinishedOperation::await_ready() const noexcept {
return !mObj || mObj->state() == QProcess::NotRunning;
}
void QCoroProcess::WaitForFinishedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroProcess::WaitForFinishedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) {
mConn = QObject::connect(mObj, qOverload<int, QProcess::ExitStatus>(&QProcess::finished),
std::bind(&WaitForFinishedOperation::resume, this, awaitingCoroutine));
startTimeoutTimer(awaitingCoroutine);

View File

@@ -24,7 +24,7 @@ class QCoroProcess : public QCoroIODevice {
public:
WaitForStartedOperation(QProcess *process, int timeout_msecs = 30'000);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
};
//! An Awaitable that suspends the coroutine until the process is finished.
@@ -32,7 +32,7 @@ class QCoroProcess : public QCoroIODevice {
public:
WaitForFinishedOperation(QProcess *process, int timeout_msecs);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine);
void await_suspend(std::coroutine_handle<> awaitingCoroutine);
};
public:

View File

@@ -51,7 +51,7 @@ public:
return mObj.isNull();
}
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
mConn = QObject::connect(
mObj, mFuncPtr, mObj,
[this, awaitingCoroutine](auto &&...args) mutable {

View File

@@ -19,7 +19,7 @@ bool QCoroTimer::WaitForTimeoutOperation::await_ready() const noexcept {
return !mTimer || !mTimer->isActive();
}
void QCoroTimer::WaitForTimeoutOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroTimer::WaitForTimeoutOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) {
if (mTimer && mTimer->isActive()) {
mConn = QObject::connect(mTimer, &QTimer::timeout, [this, awaitingCoroutine]() mutable {
QObject::disconnect(mConn);

View File

@@ -22,7 +22,7 @@ private:
explicit WaitForTimeoutOperation(QTimer &timer);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine);
void await_suspend(std::coroutine_handle<> awaitingCoroutine);
void await_resume() const;
private:
QMetaObject::Connection mConn;

View File

@@ -3,15 +3,250 @@
// SPDX-License-Identifier: MIT
#pragma once
#if defined(__clang__)
#include <experimental/coroutine>
#define QCORO_STD std::experimental
#elif defined(__GNUC__) || defined(_MSC_VER)
#include <version>
// __cpp_lib_coroutine is not defined if the compiler doesn't support coroutines
// (__cpp_impl_coroutine), e.g. clang as of 13.0.
#if defined(__cpp_lib_coroutine)
#include <coroutine>
#elif defined(__clang__)
// Implement our own <coroutine> header in a way that is compatible with the standard.
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/n4849.pdf
#include <type_traits> // void_t
#include <cstddef> // size_t
// Intrinsincs for Clang
// https://clang.llvm.org/docs/LanguageExtensions.html#c-coroutines-support-builtins
extern "C" {
void __builtin_coro_destroy(void *addr);
void __builtin_coro_resume(void *addr);
bool __builtin_coro_done(void *addr);
void* __builtin_coro_promise(void *addr, int alignment, bool from_promise);
void *__builtin_coro_noop();
}
// 17.12.1 Header <coroutine> synopsis
namespace std {
// 17.12.2, coroutine traits
// (omitted, because we implement them in std::experimental namespace and import them into the std
// namespace).
// template<class R, class .. ArgTypes>
// struct coroutine_traits;
// 17.12.3, coroutine traits
template<class Promise = void>
struct coroutine_handle;
// 17.12.3.6, comparison operators
constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept;
// constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept;
// 17.12.3.7, hash support
//template<class T> struct hash;
//template<class P> struct hash<coroutine_handle<P>>;
// 17.12.4, n-op- coroutines
struct noop_coroutine_promise;
template<>
struct coroutine_handle<noop_coroutine_promise>;
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
noop_coroutine_handle noop_coroutine() noexcept;
// 17.12.5, trivial awaitables
struct suspend_never;
struct suspend_always;
} // namespace std
// Implementation
namespace std {
// Clang checks for std::experimental::coroutine_traits explicitly, so we must define the types
// in the experimental namespace.
namespace experimental {
template<class R, class = void>
struct __coroutine_traits_base {};
template<class R>
struct __coroutine_traits_base<R, void_t<typename R::promise_type>> {
using promise_type = typename R::promise_type;
};
// 17.12.2, coroutine traits
template<class R, class ... ArgTypes>
struct coroutine_traits : __coroutine_traits_base<R> {};
// Clang requires that std::experimental::coroutine_handle is a class template
template<typename Promise>
struct coroutine_handle : public std::coroutine_handle<Promise> {};
} // namespace experimental
// Import std::experimental::coroutine_traits into the std namespace
template<typename R, typename ... ArgTypes>
using coroutine_traits = std::experimental::coroutine_traits<R, ArgTypes ...>;
// 17.12.3, coroutine handle
template<>
struct coroutine_handle<void> {
// 17.12.3.1, construct/reset
constexpr coroutine_handle() noexcept {}
constexpr coroutine_handle(nullptr_t) noexcept {}
coroutine_handle &operator=(nullptr_t) noexcept {
m_ptr = nullptr;
return *this;
}
// 17.12.3.2, export/import
constexpr void *address() const noexcept {
return m_ptr;
}
static constexpr coroutine_handle from_address(void *addr) noexcept {
coroutine_handle handle;
handle.m_ptr = addr;
return handle;
}
// 17.12.3.3, observers
constexpr explicit operator bool() const noexcept {
return m_ptr != nullptr;
}
bool done() const {
return __builtin_coro_done(m_ptr);
}
// 17.12.3.4, resumption
void operator()() const {
resume();
}
void resume() const {
__builtin_coro_resume(m_ptr);
}
void destroy() const {
__builtin_coro_destroy(m_ptr);
}
protected:
void *m_ptr = nullptr;
};
template<class Promise>
struct coroutine_handle : public coroutine_handle<> {
// 17.12.3.1, construct, reset
using coroutine_handle<>::coroutine_handle;
static coroutine_handle from_promise(Promise &promise) {
coroutine_handle handle;
handle.m_ptr = __builtin_coro_promise(&promise, alignof(Promise), /* from-promise=*/ true);
return handle;
}
coroutine_handle &operator=(nullptr_t) noexcept {
this->m_ptr = nullptr;
return *this;
}
// 17.12.3.2, export/import
static constexpr coroutine_handle from_address(void *addr) noexcept {
coroutine_handle handle;
handle.m_ptr = addr;
return handle;
}
//17.12.3.5, promise access
Promise &promise() const {
return *reinterpret_cast<Promise *>(
__builtin_coro_promise(m_ptr, alignof(Promise), /*from-promise=*/false));
}
};
// 17.12.3.6, comparison operators
constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept {
return x.address() == y.address();
}
//constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept;
// 17.12.4, no-op coroutines
struct noop_coroutine_promise {};
template<>
struct coroutine_handle<noop_coroutine_promise>;
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
template<>
struct coroutine_handle<noop_coroutine_promise> : public coroutine_handle<> {
// 17.12.4.2.1, observers
constexpr explicit operator bool() const noexcept { return true; }
constexpr bool done() const noexcept { return false; }
constexpr void operator()() const noexcept {}
constexpr void resume() const noexcept {}
constexpr void destroy() const noexcept {}
noop_coroutine_promise &promise() const noexcept {
return *reinterpret_cast<noop_coroutine_promise *>(
__builtin_coro_promise(__builtin_coro_noop(),
alignof(noop_coroutine_promise), false));
}
private:
coroutine_handle() noexcept
: coroutine_handle<>(from_address(__builtin_coro_noop())) {}
friend noop_coroutine_handle noop_coroutine() noexcept;
};
inline noop_coroutine_handle noop_coroutine() noexcept {
return {};
}
// 17.12.5, trivial awaitables
struct suspend_never {
constexpr bool await_ready() const noexcept { return true; }
constexpr void await_resume() const noexcept {}
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
};
struct suspend_always {
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
} // namespace std
#else // defined(__clang__)
#pragma error "Current compiler does not support coroutines, or is not supported by QCoro."
#endif // defined(__cpp_lib_coroutine)
// The QCORO_STD macro is no longer needed (with the code above), but keep it for backwards
// compatibility.
#ifdef QCORO_NO_DEPRECATED_QCOROSTD
#define QCORO_STD std
#else
#pragma error "Current compiler doesn't support Coroutines."
#endif
#else // QCORO_NO_DEPRECATED_QCOROSTD
#ifdef _MSC_VER
#define _QCORO_STRINGIFY2(x) #x
#define _QCORO_STRINGIFY(x) _QCORO_STRINGIFY2(x)
#define QCORO_STD \
__pragma(message(__FILE__ "(" _QCORO_STRINGIFY(__LINE__) ") QCORO_STD macro is deprecated, use regular 'std' namespace instead, or pass /DQCORO_NO_DEPRECATED_QCOROSTD to suppress this warning.")) \
std
#else // GCC, clang
#define QCORO_STD \
_Pragma("GCC warning \"QCORO_STD macro is deprecated, use regular 'std' namespace instead, or pass -DQCORO_NO_DEPRECATED_QCOROSTD to suppress this warning.\"") \
std
#endif // _MSC_VER
#endif // QCORO_NO_DEPRECATED_QCOROSTD
// Moc doesn't seem to understand something in the <concepts> header...
#ifndef Q_MOC_RUN
@@ -25,7 +260,7 @@ namespace detail {
template<typename T>
concept has_await_methods = requires(T t) {
{ t.await_ready() } -> std::same_as<bool>;
{t.await_suspend(std::declval<QCORO_STD::coroutine_handle<>>())};
{t.await_suspend(std::declval<std::coroutine_handle<>>())};
{t.await_resume()};
};
@@ -48,4 +283,4 @@ concept Awaitable = detail::has_operator_coawait<T> ||
} // namespace QCoro
#endif
#endif // Q_MOC_RUN

View File

@@ -16,7 +16,7 @@ bool QCoroDBusPendingCall::WaitForFinishedOperation::await_ready() const noexcep
return mCall.isFinished();
}
void QCoroDBusPendingCall::WaitForFinishedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroDBusPendingCall::WaitForFinishedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
auto *watcher = new QDBusPendingCallWatcher{mCall};
QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
[awaitingCoroutine](auto *watcher) mutable {

View File

@@ -22,7 +22,7 @@ private:
explicit WaitForFinishedOperation(const QDBusPendingCall &call);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
QDBusMessage await_resume() const;
private:
const QDBusPendingCall &mCall;

View File

@@ -35,7 +35,7 @@ private:
return mReply.isFinished();
}
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void await_suspend(std::coroutine_handle<> awaitingCoroutine) {
auto *watcher = new QDBusPendingCallWatcher{mReply};
QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
[awaitingCoroutine](auto *watcher) mutable {

View File

@@ -15,7 +15,7 @@ bool QCoroAbstractSocket::WaitForConnectedOperation::await_ready() const noexcep
return !mObj || mObj->state() == QAbstractSocket::ConnectedState;
}
void QCoroAbstractSocket::WaitForConnectedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroAbstractSocket::WaitForConnectedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
mConn = QObject::connect(mObj, &QAbstractSocket::stateChanged,
[this, awaitingCoroutine](auto newState) mutable {
switch (newState) {
@@ -46,7 +46,7 @@ bool QCoroAbstractSocket::WaitForDisconnectedOperation::await_ready() const noex
return !mObj || mObj->state() == QAbstractSocket::UnconnectedState;
}
void QCoroAbstractSocket::WaitForDisconnectedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept{
void QCoroAbstractSocket::WaitForDisconnectedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept{
mConn = QObject::connect(
mObj, &QAbstractSocket::disconnected,
[this, awaitingCoroutine]() mutable { resume(awaitingCoroutine); });
@@ -59,7 +59,7 @@ bool QCoroAbstractSocket::ReadOperation::await_ready() const noexcept {
QAbstractSocket::UnconnectedState;
}
void QCoroAbstractSocket::ReadOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroAbstractSocket::ReadOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
QCoroIODevice::ReadOperation::await_suspend(awaitingCoroutine);
mStateConn = QObject::connect(
static_cast<QAbstractSocket *>(mDevice.data()), &QAbstractSocket::stateChanged,
@@ -71,7 +71,7 @@ void QCoroAbstractSocket::ReadOperation::await_suspend(QCORO_STD::coroutine_hand
});
}
void QCoroAbstractSocket::ReadOperation::finish(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroAbstractSocket::ReadOperation::finish(std::coroutine_handle<> awaitingCoroutine) {
QObject::disconnect(mStateConn);
QCoroIODevice::ReadOperation::finish(awaitingCoroutine);
}

View File

@@ -24,10 +24,10 @@ class QCoroAbstractSocket final : private QCoroIODevice {
public:
using QCoroIODevice::ReadOperation::ReadOperation;
bool await_ready() const noexcept final;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept final;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept final;
private:
void finish(QCORO_STD::coroutine_handle<> awaitingCoroutine);
void finish(std::coroutine_handle<> awaitingCoroutine);
QMetaObject::Connection mStateConn;
};
@@ -36,14 +36,14 @@ class QCoroAbstractSocket final : private QCoroIODevice {
public:
WaitForConnectedOperation(QAbstractSocket *socket, int timeout_msecs = 30'000);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
};
class WaitForDisconnectedOperation final : public WaitOperationBase<QAbstractSocket> {
public:
WaitForDisconnectedOperation(QAbstractSocket *socket, int timeout_msecs);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
};
public:

View File

@@ -19,7 +19,7 @@ bool QCoroLocalSocket::WaitForConnectedOperation::await_ready() const noexcept {
return !mObj || mObj->state() == QLocalSocket::ConnectedState;
}
void QCoroLocalSocket::WaitForConnectedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroLocalSocket::WaitForConnectedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
mConn = QObject::connect(mObj, &QLocalSocket::stateChanged,
[this, awaitingCoroutine](auto newState) mutable {
switch (newState) {
@@ -48,7 +48,7 @@ bool QCoroLocalSocket::WaitForDisconnectedOperation::await_ready() const noexcep
return !mObj || mObj->state() == QLocalSocket::UnconnectedState;
}
void QCoroLocalSocket::WaitForDisconnectedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroLocalSocket::WaitForDisconnectedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) {
mConn = QObject::connect(mObj, &QLocalSocket::disconnected,
std::bind(&WaitForDisconnectedOperation::resume, this, awaitingCoroutine));
startTimeoutTimer(awaitingCoroutine);
@@ -59,7 +59,7 @@ bool QCoroLocalSocket::ReadOperation::await_ready() const noexcept {
static_cast<const QLocalSocket *>(mDevice.data())->state() == QLocalSocket::UnconnectedState;
}
void QCoroLocalSocket::ReadOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroLocalSocket::ReadOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
QCoroIODevice::ReadOperation::await_suspend(awaitingCoroutine);
mStateConn = QObject::connect(
static_cast<QLocalSocket *>(mDevice.data()), &QLocalSocket::stateChanged,
@@ -70,7 +70,7 @@ void QCoroLocalSocket::ReadOperation::await_suspend(QCORO_STD::coroutine_handle<
});
}
void QCoroLocalSocket::ReadOperation::finish(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroLocalSocket::ReadOperation::finish(std::coroutine_handle<> awaitingCoroutine) {
QObject::disconnect(mStateConn);
QCoroIODevice::ReadOperation::finish(awaitingCoroutine);
}

View File

@@ -22,7 +22,7 @@ class QCoroLocalSocket : private QCoroIODevice {
public:
explicit WaitForConnectedOperation(QLocalSocket *socket, int timeout_msecs = 30'000);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
};
//! An Awaitable that suspends the coroutine until the socket is disconnected
@@ -30,7 +30,7 @@ class QCoroLocalSocket : private QCoroIODevice {
public:
WaitForDisconnectedOperation(QLocalSocket *socket, int timeout_msecs);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine);
void await_suspend(std::coroutine_handle<> awaitingCoroutine);
};
class ReadOperation final : public QCoroIODevice::ReadOperation {
@@ -38,10 +38,10 @@ class QCoroLocalSocket : private QCoroIODevice {
using QCoroIODevice::ReadOperation::ReadOperation;
bool await_ready() const noexcept final;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept final;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept final;
private:
void finish(QCORO_STD::coroutine_handle<> awaitingCoroutine) final;
void finish(std::coroutine_handle<> awaitingCoroutine) final;
QMetaObject::Connection mStateConn;
};

View File

@@ -11,7 +11,7 @@ bool QCoroNetworkReply::ReadOperation::await_ready() const noexcept {
static_cast<const QNetworkReply *>(mDevice.data())->isFinished();
}
void QCoroNetworkReply::ReadOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroNetworkReply::ReadOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
QCoroIODevice::ReadOperation::await_suspend(awaitingCoroutine);
mFinishedConn = QObject::connect(
@@ -19,7 +19,7 @@ void QCoroNetworkReply::ReadOperation::await_suspend(QCORO_STD::coroutine_handle
std::bind(&ReadOperation::finish, this, awaitingCoroutine));
}
void QCoroNetworkReply::ReadOperation::finish(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroNetworkReply::ReadOperation::finish(std::coroutine_handle<> awaitingCoroutine) {
QObject::disconnect(mFinishedConn);
QCoroIODevice::ReadOperation::finish(awaitingCoroutine);
}
@@ -32,7 +32,7 @@ bool QCoroNetworkReply::WaitForFinishedOperation::await_ready() const noexcept {
return !mReply || mReply->isFinished();
}
void QCoroNetworkReply::WaitForFinishedOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void QCoroNetworkReply::WaitForFinishedOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) {
if (mReply) {
QObject::connect(mReply, &QNetworkReply::finished,
[awaitingCoroutine]() mutable { awaitingCoroutine.resume(); });

View File

@@ -19,10 +19,10 @@ private:
using QCoroIODevice::ReadOperation::ReadOperation;
bool await_ready() const noexcept final;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept final;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept final;
private:
void finish(QCORO_STD::coroutine_handle<> awaitingCoroutine) final;
void finish(std::coroutine_handle<> awaitingCoroutine) final;
QMetaObject::Connection mFinishedConn;
};
@@ -32,7 +32,7 @@ private:
explicit WaitForFinishedOperation(QPointer<QNetworkReply> reply);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine);
void await_suspend(std::coroutine_handle<> awaitingCoroutine);
QNetworkReply *await_resume() const noexcept;
private:

View File

@@ -15,7 +15,7 @@ bool QCoroTcpServer::WaitForNewConnectionOperation::await_ready() const noexcept
return !mObj || mObj->hasPendingConnections();
}
void QCoroTcpServer::WaitForNewConnectionOperation::await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
void QCoroTcpServer::WaitForNewConnectionOperation::await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
mConn = QObject::connect(mObj, &QTcpServer::newConnection,
std::bind(&WaitForNewConnectionOperation::resume, this, awaitingCoroutine));
startTimeoutTimer(awaitingCoroutine);

View File

@@ -24,7 +24,7 @@ class QCoroTcpServer {
public:
WaitForNewConnectionOperation(QTcpServer *server, int timeout_msecs = 30'000);
bool await_ready() const noexcept;
void await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept;
void await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept;
QTcpSocket *await_resume();
};

View File

@@ -4,30 +4,6 @@
#pragma once
// vv workaround for QtCreator's clang code model warnings vv
// Bypass GCC / MSVC coroutine guards when using clang code model
#if !defined(__APPLE__) && !defined(__ANDROID__) && (defined(__GNUC__) || defined(_MSC_VER))
#if defined(__GNUC__) && defined(__clang__) && !defined(__cpp_impl_coroutine)
#define __cpp_impl_coroutine true
#elif defined(_MSC_VER) && defined(__clang__) && !defined(__cpp_lib_coroutine)
#define __cpp_lib_coroutine true
#endif
// Clang requires coroutine types in std::experimental
#include <coroutine>
#if defined(__clang__)
namespace std::experimental {
using std::coroutine_traits;
using std::coroutine_handle;
using std::suspend_always;
using std::suspend_never;
}
#endif
#endif
// ^^ workaround for QtCreator's clang code model warnings ^^
#include "task.h"
#include "qcorocore.h"
#if defined(QT_DBUS_LIB)

222
3rdparty/qcoro/task.h vendored
View File

@@ -10,6 +10,8 @@
#include <atomic>
#include <variant>
#include <memory>
#include <type_traits>
#include <optional>
#include <QDebug>
#include <QEventLoop>
@@ -39,7 +41,7 @@ public:
* \param[in] awaitingCoroutine handle of the coroutine that is co_awaiting the current
* coroutine (continuation).
*/
explicit TaskFinalSuspend(QCORO_STD::coroutine_handle<> awaitingCoroutine)
explicit TaskFinalSuspend(std::coroutine_handle<> awaitingCoroutine)
: mAwaitingCoroutine(awaitingCoroutine) {}
//! Returns whether the just finishing coroutine should do final suspend or not
@@ -64,18 +66,16 @@ public:
* \param[in] finishedCoroutine handle of the just finished coroutine
*/
template<typename _Promise>
void await_suspend(QCORO_STD::coroutine_handle<_Promise> finishedCoroutine) noexcept {
void await_suspend(std::coroutine_handle<_Promise> finishedCoroutine) noexcept {
auto &promise = finishedCoroutine.promise();
promise.mInFinalSuspend = true;
if (promise.mResumeAwaiter.exchange(true, std::memory_order_acq_rel)) {
promise.mAwaitingCoroutine.resume();
}
if (promise.mDestroyOnFinalSuspend) {
// The handle will be destroyed here only if the associated Task has already been destroyed
if (promise.setDestroyHandle()) {
finishedCoroutine.destroy();
} else {
promise.mInFinalSuspend = false;
}
}
@@ -88,7 +88,7 @@ public:
private:
//! Handle of the coroutine co_awaiting the current coroutine.
QCORO_STD::coroutine_handle<> mAwaitingCoroutine;
std::coroutine_handle<> mAwaitingCoroutine;
};
//! Base class for the \c Task<T> promise_type.
@@ -139,7 +139,7 @@ public:
* it as a regular function, therefore it returns `std::suspend_never` awaitable, which
* indicates that the coroutine should not be suspended.
* */
QCORO_STD::suspend_never initial_suspend() const noexcept {
std::suspend_never initial_suspend() const noexcept {
return {};
}
@@ -225,7 +225,7 @@ public:
* represented by this promise. When our coroutine finishes, it's
* our job to resume the awaiting coroutine.
*/
bool setAwaitingCoroutine(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
bool setAwaitingCoroutine(std::coroutine_handle<> awaitingCoroutine) {
mAwaitingCoroutine = awaitingCoroutine;
return !mResumeAwaiter.exchange(true, std::memory_order_acq_rel);
}
@@ -234,23 +234,20 @@ public:
return mAwaitingCoroutine != nullptr;
}
bool isInFinalSuspend() const {
return mInFinalSuspend;
}
void setDestroyOnFinalSuspend() {
mDestroyOnFinalSuspend = true;
bool setDestroyHandle() noexcept {
return mDestroyHandle.exchange(true, std::memory_order_acq_rel);
}
private:
friend class TaskFinalSuspend;
//! Handle of the coroutine that is currently co_awaiting this Awaitable
QCORO_STD::coroutine_handle<> mAwaitingCoroutine;
std::coroutine_handle<> mAwaitingCoroutine;
//! Indicates whether the awaiter should be resumed when it tries to co_await on us.
std::atomic<bool> mResumeAwaiter{false};
std::atomic<bool> mInFinalSuspend{false};
std::atomic<bool> mDestroyOnFinalSuspend{false};
//! Indicates whether we can destroy the coroutine handle
std::atomic<bool> mDestroyHandle{false};
};
//! The promise_type for Task<T>
@@ -291,12 +288,12 @@ public:
mValue = value;
}
template<typename U> requires QCoro::concepts::constructible_from<T, U>
template<typename U> requires concepts::constructible_from<T, U>
void return_value(U &&value) noexcept {
mValue = std::move(value);
}
template<typename U> requires QCoro::concepts::constructible_from<T, U>
template<typename U> requires concepts::constructible_from<T, U>
void return_value(const U &value) noexcept {
mValue = value;
}
@@ -308,6 +305,7 @@ public:
*/
T &result() & {
if (std::holds_alternative<std::exception_ptr>(mValue)) {
Q_ASSERT(std::get<std::exception_ptr>(mValue) != nullptr);
std::rethrow_exception(std::get<std::exception_ptr>(mValue));
}
@@ -317,6 +315,7 @@ public:
//! \copydoc T &QCoro::TaskPromise<T>::result() &
T &&result() && {
if (std::holds_alternative<std::exception_ptr>(mValue)) {
Q_ASSERT(std::get<std::exception_ptr>(mValue) != nullptr);
std::rethrow_exception(std::get<std::exception_ptr>(mValue));
}
@@ -407,7 +406,7 @@ public:
* co_awaited coroutine has finished synchronously and the co_awaiting coroutine doesn't
* have to suspend.
*/
bool await_suspend(QCORO_STD::coroutine_handle<> awaitingCoroutine) noexcept {
bool await_suspend(std::coroutine_handle<> awaitingCoroutine) noexcept {
return mAwaitedCoroutine.promise().setAwaitingCoroutine(awaitingCoroutine);
}
@@ -416,11 +415,11 @@ protected:
/*!
* \param[in] coroutine hande for the coroutine that is being co_awaited.
*/
TaskAwaiterBase(QCORO_STD::coroutine_handle<_Promise> awaitedCoroutine)
TaskAwaiterBase(std::coroutine_handle<_Promise> awaitedCoroutine)
: mAwaitedCoroutine(awaitedCoroutine) {}
//! Handle of the coroutine that is being co_awaited by this awaiter
QCORO_STD::coroutine_handle<_Promise> mAwaitedCoroutine = {};
std::coroutine_handle<_Promise> mAwaitedCoroutine = {};
};
} // namespace detail
@@ -459,7 +458,7 @@ public:
/*!
* \param[in] coroutine handle of the coroutine that has constructed the task.
*/
explicit Task(QCORO_STD::coroutine_handle<promise_type> coroutine) : mCoroutine(coroutine) {}
explicit Task(std::coroutine_handle<promise_type> coroutine) : mCoroutine(coroutine) {}
//! Task cannot be copy-constructed.
Task(const Task &) = delete;
@@ -475,7 +474,10 @@ public:
Task &operator=(Task &&other) noexcept {
if (std::addressof(other) != this) {
if (mCoroutine) {
mCoroutine.destroy();
// The coroutine handle will be destroyed only after TaskFinalSuspend
if (mCoroutine.promise().setDestroyHandle()) {
mCoroutine.destroy();
}
}
mCoroutine = other.mCoroutine;
@@ -486,13 +488,13 @@ public:
//! Destructor.
~Task() {
if (mCoroutine) {
if (mCoroutine.done() && !mCoroutine.promise().isInFinalSuspend())
mCoroutine.destroy();
else
mCoroutine.promise().setDestroyOnFinalSuspend();
if (!mCoroutine) return;
// The coroutine handle will be destroyed only after TaskFinalSuspend
if (mCoroutine.promise().setDestroyHandle()) {
mCoroutine.destroy();
}
}
};
//! Returns whether the task has finished.
/*!
@@ -510,11 +512,11 @@ public:
* object, that is an object that the co_await keyword uses to suspend and
* resume the coroutine.
*/
auto operator co_await() const &noexcept {
auto operator co_await() const noexcept {
//! Specialization of the TaskAwaiterBase that returns the promise result by value
class TaskAwaiter : public detail::TaskAwaiterBase<promise_type> {
public:
TaskAwaiter(QCORO_STD::coroutine_handle<promise_type> awaitedCoroutine)
TaskAwaiter(std::coroutine_handle<promise_type> awaitedCoroutine)
: detail::TaskAwaiterBase<promise_type>{awaitedCoroutine} {}
//! Called when the co_awaited coroutine is resumed.
@@ -523,36 +525,144 @@ public:
* value co_returned by the coroutine. */
auto await_resume() {
Q_ASSERT(this->mAwaitedCoroutine != nullptr);
return this->mAwaitedCoroutine.promise().result();
if constexpr (!std::is_void_v<T>) {
return std::move(this->mAwaitedCoroutine.promise().result());
} else {
// Wil re-throw exception, if any is stored
this->mAwaitedCoroutine.promise().result();
}
}
};
return TaskAwaiter{mCoroutine};
}
//! \copydoc QCoro::Task::operator co_await() const & noexcept
auto operator co_await() const &&noexcept {
//! Specialization of the TaskAwaiterBase that returns the promise result as an r-value reference.
class TaskAwaiter : public detail::TaskAwaiterBase<promise_type> {
public:
TaskAwaiter(QCORO_STD::coroutine_handle<promise_type> awaitedCoroutine)
: detail::TaskAwaiterBase<promise_type>{awaitedCoroutine} {}
//! A callback to be invoked when the asynchronous task finishes.
/*!
* In some scenarios it is not possible to co_await a coroutine (for example from
* a third-party code that cannot be changed to be a coroutine). In that case,
* chaining a then() callback is a possible solution how to handle a result
* of a coroutine without co_awaiting it.
*
* @param callback A function or a function object that can be invoked with a single
* argument of type T (that is type matching the return type of the coroutine).
*
* @return Returns Task<R> where R is the return type of the callback, so that the
* result of the then() action can be co_awaited, if desired. If the callback
* returns an awaitable (Task<R>) then the result of then is the awaitable.
*/
template<typename ThenCallback>
requires (std::is_void_v<T> && std::invocable<ThenCallback>) || std::invocable<ThenCallback, T>
auto then(ThenCallback &&callback) {
return thenImpl(std::forward<ThenCallback>(callback));
}
//! Called when the co_awaited coroutine is resumed.
/*
* \return an r-value reference to the coroutine's promise result, factically
* a value co_returned by the coroutine. */
auto await_resume() {
Q_ASSERT(this->mAwaitedCoroutine != nullptr);
if constexpr (std::is_void_v<T>) {
this->mAwaitedCoroutine.promise().result();
template<typename ThenCallback, typename ErrorCallback>
requires (((std::is_void_v<T> && std::invocable<ThenCallback>) || std::invocable<ThenCallback, T>) &&
std::invocable<ErrorCallback, const std::exception &>)
auto then(ThenCallback &&callback, ErrorCallback &&errorCallback) {
return thenImpl(std::forward<ThenCallback>(callback), std::forward<ErrorCallback>(errorCallback));
}
private:
template<typename ThenCallback, typename Arg = T>
struct invoke_result: std::invoke_result<ThenCallback, T> {};
template<typename ThenCallback>
struct invoke_result<ThenCallback, void>: std::invoke_result<ThenCallback> {};
template<typename ThenCallback, typename Arg>
using invoke_result_t = typename invoke_result<ThenCallback, Arg>::type;
// Implementation of then() for callbacks that return Task<R>
template<typename ThenCallback, typename R = invoke_result_t<ThenCallback, T>>
requires QCoro::Awaitable<R>
auto thenImpl(ThenCallback &&callback) -> R {
const auto cb = std::move(callback);
if constexpr (std::is_void_v<value_type>) {
co_await *this;
co_return co_await cb();
} else {
co_return co_await cb(co_await *this);
}
}
template<typename ThenCallback, typename ErrorCallback, typename R = invoke_result_t<ThenCallback, T>>
requires QCoro::Awaitable<R>
auto thenImpl(ThenCallback &&thenCallback, ErrorCallback &&errorCallback) -> R {
const auto thenCb = std::move(thenCallback);
const auto errCb = std::move(errorCallback);
if constexpr (std::is_void_v<value_type>) {
try {
co_await *this;
} catch (const std::exception &e) {
errCb(e);
if constexpr (!std::is_void_v<typename R::value_type>) {
co_return {};
} else {
return std::move(this->mAwaitedCoroutine.promise().result());
co_return;
}
}
};
co_return co_await thenCb();
} else {
std::optional<T> v;
try {
v = co_await *this;
} catch (const std::exception &e) {
errCb(e);
if constexpr (!std::is_void_v<typename R::value_type>) {
co_return {};
} else {
co_return;
}
}
co_return co_await thenCb(std::move(*v));
}
}
return TaskAwaiter{mCoroutine};
// Implementation of then() for callbacks that return R, which is not Task<S>
template<typename ThenCallback, typename R = invoke_result_t<ThenCallback, T>>
requires (!QCoro::Awaitable<R>)
auto thenImpl(ThenCallback &&callback) -> Task<R> {
const auto cb = std::move(callback);
if constexpr (std::is_void_v<value_type>) {
co_await *this;
co_return cb();
} else {
co_return cb(co_await *this);
}
}
template<typename ThenCallback, typename ErrorCallback, typename R = invoke_result_t<ThenCallback, T>>
requires (!QCoro::Awaitable<R>)
auto thenImpl(ThenCallback &&thenCallback, ErrorCallback &&errorCallback) -> Task<R> {
const auto thenCb = std::move(thenCallback);
const auto errCb = std::move(errorCallback);
if constexpr (std::is_void_v<value_type>) {
try {
co_await *this;
} catch (const std::exception &e) {
errCb(e);
if constexpr (!std::is_void_v<R>) {
co_return {};
} else {
co_return;
}
}
co_return thenCb();
} else {
std::optional<T> v;
try {
v = co_await *this;
} catch (const std::exception &e) {
errCb(e);
if constexpr (!std::is_void_v<R>) {
co_return {};
} else {
co_return;
}
}
co_return thenCb(std::move(*v));
}
}
private:
@@ -561,18 +671,18 @@ private:
* In other words, this is a handle to the coroutine that has constructed and
* returned this Task<T>.
* */
QCORO_STD::coroutine_handle<promise_type> mCoroutine = {};
std::coroutine_handle<promise_type> mCoroutine = {};
};
namespace detail {
template<typename T>
inline Task<T> TaskPromise<T>::get_return_object() noexcept {
return Task<T>{QCORO_STD::coroutine_handle<TaskPromise>::from_promise(*this)};
return Task<T>{std::coroutine_handle<TaskPromise>::from_promise(*this)};
}
Task<void> inline TaskPromise<void>::get_return_object() noexcept {
return Task<void>{QCORO_STD::coroutine_handle<TaskPromise>::from_promise(*this)};
return Task<void>{std::coroutine_handle<TaskPromise>::from_promise(*this)};
}
} // namespace detail

View File

@@ -34,7 +34,7 @@ protected:
}
}
void startTimeoutTimer(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void startTimeoutTimer(std::coroutine_handle<> awaitingCoroutine) {
if (!mTimeoutTimer) {
return;
}
@@ -47,7 +47,7 @@ protected:
mTimeoutTimer->start();
}
void resume(QCORO_STD::coroutine_handle<> awaitingCoroutine) {
void resume(std::coroutine_handle<> awaitingCoroutine) {
if (mTimeoutTimer) {
mTimeoutTimer->stop();
}