After another few months I’m happy to announce a new release of QCoro, which brings several new features and a bunch of bugfixes.

  • .then() continuation for Task<T>
  • All asynchronous operations now return Task<T>
  • Timeouts for many operations
  • Support for QThread

.then() continuation for Task

Sometimes it’s not possible to co_await a coroutine - usually because you need to integrate with a 3rd party code that is not coroutine-ready. A good example might be implementing QAbstractItemModel, where none of the virtual methods are coroutines and thus it’s not possible to use co_await in them.

To still make it possible to all coroutines from such code, QCoro::Task<T> now has a new method: .then(), which allows attaching a continuation callback that will be invoked by QCoro when the coroutine represented by the Task finishes.

void notACoroutine() {
    someCoroutineReturningQString().then([](const QString &result) {
        // Will be invoked when the someCoroutine() finishes.
        // The result of the coroutine is passed as an argument to the continuation.
    });
}

The continuation itself might be a coroutine, and the result of the .then() member function is again a Task<R> (where R is the return type of the continuation callback), so it is possible to chain multiple continuations as well as co_awaiting the entire chain.

All asynchronous operations now return Task<T>

Up until now each operation from the QCoro wrapper types returned a special awaitable - for example, QCoroIODevice::read() returned QCoro::detail::QCoroIODevice::ReadOperation. In most cases users of QCoro do not need to concern themselves with that type, since they can still directly co_await the returned awaitable.

However, it unnecessarily leaks implementation details of QCoro into public API and it makes it harded to return a coroutine from a non-coroutine function.

As of QCoro 0.5.0, all the operations now return Task<T>, which makes the API consistent. As a secondary effect, all the operations can have a chained continuation using the .then() continuation, as described above.

Timeout support for many operations

Qt doesn’t allow specifying timeout for many operations, because they are typically non-blocking. But the timeout makes sense in most QCoro cases, because they are combination of wait + the non-blocking operation. Let’s take QIODevice::read() for example: the Qt version doesn’t have any timeout, because the call will never block - if there’s nothing to read, it simply returns an empty QByteArray.

On the other hand, QCoroIODevice::read() is an asynchronous operation, because under to hood, it’s a coroutine that asynchronously calls a sequence of

device->waitForReadyRead();
device->read();

Since QIODevice::waitForReadyRead() takes a timeout argument, it makes sense for QCoroIODevice::read() to also take (an optional) timeout argument. This and many other operations have gained support for timeout.

Support for QThread

It’s been a while since I added a new wrapper for a Qt class, so QCoro 0.5.0 adds wrapper for QThread. It’s now possible to co_await thread start and end:

std::unique_ptr<QThread> thread(QThread::create([]() {
    ...
});
ui->setLabel(tr("Starting thread...");
thread->start();
co_await qCoro(thread)->waitForStarted();
ui->setLabel(tr("Calculating..."));
co_await qCoro(thread)->waitForFinished();
ui->setLabel(tr("Finished!"));

Full changelog

  • .then() continuation for Task<T> (#39)
  • Fixed namespace scoping (#45)
  • Fixed QCoro::waitFor() getting stuck when coroutine returns synchronously (#46)
  • Fixed -pthread usage in CMake (#47)
  • Produce QMake config files (.pri) for each module (commit e215616)
  • Fix build on platforms where -latomic must be linked explicitly (#52)
  • Return Task<T> from all operations (#54)
  • Add QCoro wrapper for QThread (commit 832d931)
  • Many documentation updates

Thanks to everyone who contributed to QCoro!


Download

You can download QCoro 0.5.0 here or check the latest sources on QCoro GitHub.

More About QCoro

If you are interested in learning more about QCoro, go read the documentation, look at the first release announcement, which contains a nice explanation and example or watch recording of my talk about C++20 coroutines and QCoro this years’ Akademy.