Coroutine.h
1/*
2 * Copyright (C) 2026 by Marc Boris Duerner
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * As a special exception, you may use this file as part of a free
10 * software library without restriction. Specifically, if other files
11 * instantiate templates or use macros or inline functions from this
12 * file, or you compile this file and link it with other files to
13 * produce an executable, this file does not by itself cause the
14 * resulting executable to be covered by the GNU General Public
15 * License. This exception does not however invalidate any other
16 * reasons why the executable file might be covered by the GNU Library
17 * General Public License.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the:
26 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 * Boston, MA 02110-1301 USA
28 */
29
30#ifndef PT_COROUTINE_H
31#define PT_COROUTINE_H
32
33#include <Pt/Api.h>
34#include <Pt/Connectable.h>
35
36#if __cplusplus >= 202002L
37
38#include <coroutine>
39#include <exception>
40#include <stdexcept>
41
42namespace Pt {
43
54{
55 public:
58 virtual void cancel() = 0;
59
60 protected:
63 AwaiterBase() = default;
64
67 AwaiterBase(const AwaiterBase&) = delete;
68
72
75 virtual ~AwaiterBase() = default;
76};
77
125class Awaiter : public AwaiterBase
126{
127 public:
130 bool await_ready() const
131 { return false; }
132
135 template<typename P>
136 bool await_suspend(std::coroutine_handle<P> h)
137 {
138 _handle = h;
139 onBegin();
140 return true;
141 }
142
145 void cancel() override
146 {
147 _handle = nullptr;
148 onCancel();
149 }
150
151 protected:
155 {}
156
162 void setReady()
163 {
164 if( _handle )
165 {
166 std::coroutine_handle<> h = _handle;
167 _handle = nullptr;
168 h.resume();
169 }
170 }
171
172 protected:
178 virtual void onBegin() = 0;
179
185 virtual void onCancel() = 0;
186
187 protected:
188 std::coroutine_handle<> _handle;
189};
190
191
206template<typename R>
207class BasicAwaiter : public Awaiter
208{
209 public:
213 {
214 return onReady();
215 }
216
217 protected:
222 virtual R onReady() = 0;
223};
224
225
233template<>
234class BasicAwaiter<void> : public Awaiter
235{
236 public:
240 { onReady(); }
241
242 protected:
247 virtual void onReady() = 0;
248};
249
250
253class PromiseBase
254{
255 public:
256 void setFinished()
257 {
258 _pending = nullptr;
259 }
260
261 void cancel()
262 {
263 AwaiterBase* pending = _pending;
264 _pending = nullptr;
265 if(pending)
266 {
267 pending->cancel();
268 }
269 }
270
271 AwaiterBase* _pending = nullptr;
272 std::coroutine_handle<> _continuation = nullptr;
273 PromiseBase* _outer = nullptr;
274
275 protected:
276 PromiseBase() = default;
277 ~PromiseBase() = default;
278
279 PromiseBase(const PromiseBase&) = delete;
280 PromiseBase& operator=(const PromiseBase&) = delete;
281};
282
283
286template<typename T>
287class PromiseResult
288{
289 public:
290 void return_value(T v)
291 { _result = std::move(v); }
292
293 T getResult()
294 { return std::move(_result); }
295
296 T _result{};
297
298 protected:
299 PromiseResult() = default;
300 ~PromiseResult() = default;
301
302 PromiseResult(const PromiseResult&) = delete;
303 PromiseResult& operator=(const PromiseResult&) = delete;
304};
305
306
309template<typename T>
310class PromiseResult<T&>
311{
312 public:
313 void return_value(T& v)
314 { _result = &v; }
315
316 T& getResult()
317 { return *_result; }
318
319 T* _result = nullptr;
320
321 protected:
322 PromiseResult() = default;
323 ~PromiseResult() = default;
324
325 PromiseResult(const PromiseResult&) = delete;
326 PromiseResult& operator=(const PromiseResult&) = delete;
327};
328
329
332template<>
333class PromiseResult<void>
334{
335 public:
336 void return_void()
337 {}
338
339 void getResult()
340 {}
341
342 protected:
343 PromiseResult() = default;
344 ~PromiseResult() = default;
345
346 PromiseResult(const PromiseResult&) = delete;
347 PromiseResult& operator=(const PromiseResult&) = delete;
348};
349
350
353template<typename A>
354class AwaiterProxy
355{
356 public:
357 A&& _awaitable;
358 PromiseBase* _promise;
359
360 bool await_ready()
361 { return _awaitable.await_ready(); }
362
363 template<typename P>
364 auto await_suspend(std::coroutine_handle<P> h) -> decltype(_awaitable.await_suspend(h))
365 { return _awaitable.await_suspend(h); }
366
367 auto await_resume() -> decltype(_awaitable.await_resume())
368 {
369 _promise->setFinished();
370 return _awaitable.await_resume();
371 }
372};
373
374
377class FinalAwaiter
378{
379 public:
380 bool await_ready() const noexcept
381 { return false; }
382
383 template<typename P>
384 std::coroutine_handle<> await_suspend(std::coroutine_handle<P> h) noexcept
385 {
386 PromiseBase* outer = h.promise()._outer;
387
388 if( outer && outer != &h.promise() )
389 outer->setFinished();
390
391 h.promise()._outer = nullptr;
392
393 if( h.promise()._continuation )
394 return h.promise()._continuation;
395
396 return std::noop_coroutine();
397 }
398
399 void await_resume() noexcept
400 {}
401};
402
434template<typename T = void>
435class Task : public AwaiterBase
436{
437 public:
440 class Promise : public PromiseResult<T>
441 , public PromiseBase
442 {
443 public:
444 std::exception_ptr _exception;
445
446 template<typename A>
447 AwaiterProxy<A> await_transform(A&& a)
448 {
449 _pending = &a;
450 return AwaiterProxy<A>{ std::forward<A>(a), this };
451 }
452
453 Task get_return_object()
454 {
455 return Task(std::coroutine_handle<promise_type>::from_promise(*this));
456 }
457
458 std::suspend_always initial_suspend() noexcept
459 { return {}; }
460
461 FinalAwaiter final_suspend() noexcept
462 { return {}; }
463
464 void unhandled_exception()
465 { _exception = std::current_exception(); }
466 };
467
468 using promise_type = Promise;
469 using handle_type = std::coroutine_handle<promise_type>;
470
471 public:
474 Task() noexcept
475 : _handle(nullptr)
476 {}
477
480 explicit Task(handle_type h)
481 : _handle(h)
482 {}
483
486 Task(Task&& other) noexcept
487 : _handle(other._handle)
488 {
489 other._handle = nullptr;
490 }
491
496 Task& operator=(Task&& other) noexcept
497 {
498 if(this != &other)
499 {
500 cancel();
501 _handle = other._handle;
502 other._handle = nullptr;
503 }
504 return *this;
505 }
506
510 { cancel(); }
511
518 void run()
519 {
520 if( _handle && ! _handle.done() )
521 {
522 if (_handle.promise()._outer)
523 throw std::logic_error("task pending");
524
525 _handle.promise()._outer = &_handle.promise();
526 _handle.resume();
527 }
528 }
529
535 void cancel() override
536 {
537 if( _handle )
538 {
539 _handle.promise().cancel();
540 handle_type handle = _handle;
541 _handle = nullptr;
542 handle.destroy();
543 }
544 }
545
548 bool done() const
549 { return _handle && _handle.done(); }
550
553 explicit operator bool() const
554 { return _handle != nullptr; }
555
562 {
563 if( _handle.promise()._exception )
564 std::rethrow_exception(_handle.promise()._exception);
565
566 return _handle.promise().getResult();
567 }
568
571 bool await_ready() const noexcept
572 { return done(); }
573
578 template<typename P>
579 std::coroutine_handle<> await_suspend(std::coroutine_handle<P> outer)
580 {
581 if(_handle.promise()._outer)
582 {
583 throw std::logic_error("task pending");
584 }
585
586 _handle.promise()._continuation = outer;
587 _handle.promise()._outer = &outer.promise();
588 return _handle;
589 }
590
596 {
597 if( _handle.promise()._exception )
598 std::rethrow_exception(_handle.promise()._exception);
599
600 return _handle.promise().getResult();
601 }
602
603 private:
604 Task(const Task&) = delete;
605 Task& operator=(const Task&) = delete;
606
607 handle_type _handle;
608
609};
610
611} // namespace Pt
612
613#endif // __cplusplus >= 202002L
614
615#endif // PT_COROUTINE_H
AwaiterBase & operator=(const AwaiterBase &)=delete
No copy assignment.
AwaiterBase(const AwaiterBase &)=delete
No copy constructor.
virtual void cancel()=0
Cancels the pending operation.
virtual ~AwaiterBase()=default
Destructor.
AwaiterBase()=default
Constructor.
virtual void onCancel()=0
Aborts the in-flight operation.
void cancel() override
Cancels the in-flight operation.
Definition Coroutine.h:145
virtual void onBegin()=0
Starts the asynchronous operation.
bool await_suspend(std::coroutine_handle< P > h)
Starts the operation and suspends the coroutine.
Definition Coroutine.h:136
bool await_ready() const
Returns false so co_await always suspends.
Definition Coroutine.h:130
void setReady()
Resumes the waiting coroutine.
Definition Coroutine.h:162
Awaiter()
Constructor.
Definition Coroutine.h:154
void await_resume()
Resumes the coroutine after the operation completed.
Definition Coroutine.h:239
virtual void onReady()=0
Finalizes the operation when the awaitable resumes.
Provides an awaitable that delivers a result through onReady().
Definition Coroutine.h:208
R await_resume()
Returns the result produced when the awaitable resumes.
Definition Coroutine.h:212
virtual R onReady()=0
Returns the result for the co_await expression.
Represents a cancellable C++20 coroutine that produces a single result.
Definition Coroutine.h:436
std::coroutine_handle await_suspend(std::coroutine_handle< P > outer)
Suspends the outer coroutine and starts the inner coroutine.
Definition Coroutine.h:579
void run()
Starts execution of the coroutine.
Definition Coroutine.h:518
~Task()
Cancels the task if it still owns a coroutine frame.
Definition Coroutine.h:509
bool await_ready() const noexcept
Returns true if the inner coroutine has already finished.
Definition Coroutine.h:571
T await_resume()
Returns the result of the inner task.
Definition Coroutine.h:595
bool done() const
Returns true if the coroutine has finished.
Definition Coroutine.h:548
void cancel() override
Cancels the running coroutine.
Definition Coroutine.h:535
Task & operator=(Task &&other) noexcept
Moves the coroutine frame from other.
Definition Coroutine.h:496
Task() noexcept
Constructs an empty task with no coroutine frame.
Definition Coroutine.h:474
T result()
Returns the coroutine result.
Definition Coroutine.h:561
Task(Task &&other) noexcept
Moves the coroutine frame from other.
Definition Coroutine.h:486
Task(handle_type h)
Constructs a task that takes ownership of h.
Definition Coroutine.h:480
Core module.
Definition Allocator.h:33