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 
42 namespace Pt {
43 
54 {
55  public:
58  virtual void cancel() = 0;
59 
60  protected:
63  AwaiterBase() = default;
64 
67  AwaiterBase(const AwaiterBase&) = delete;
68 
71  AwaiterBase& operator=(const AwaiterBase&) = delete;
72 
75  virtual ~AwaiterBase() = default;
76 };
77 
125 class 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 
206 template<typename R>
207 class BasicAwaiter : public Awaiter
208 {
209  public:
213  {
214  return onReady();
215  }
216 
217  protected:
222  virtual R onReady() = 0;
223 };
224 
225 
233 template<>
234 class BasicAwaiter<void> : public Awaiter
235 {
236  public:
240  { onReady(); }
241 
242  protected:
247  virtual void onReady() = 0;
248 };
249 
250 
253 class 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 
286 template<typename T>
287 class 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 
309 template<typename T>
310 class 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 
332 template<>
333 class 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 
353 template<typename A>
354 class 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 
377 class 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 
434 template<typename T = void>
435 class 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 
561  T result()
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
Core module.
Definition: Allocator.h:33
AwaiterBase()=default
Constructor.
Represents a cancellable C++20 coroutine that produces a single result.
Definition: Coroutine.h:436
void cancel() override
Cancels the running coroutine.
Definition: Coroutine.h:535
T result()
Returns the coroutine result.
Definition: Coroutine.h:561
R await_resume()
Returns the result produced when the awaitable resumes.
Definition: Coroutine.h:212
Task(handle_type h)
Constructs a task that takes ownership of h.
Definition: Coroutine.h:480
Defines the cancellation interface for a pending awaitable.
Definition: Coroutine.h:54
virtual void onBegin()=0
Starts the asynchronous operation.
~Task()
Cancels the task if it still owns a coroutine frame.
Definition: Coroutine.h:509
AwaiterBase(const AwaiterBase &)=delete
No copy constructor.
Task & operator=(Task &&other) noexcept
Moves the coroutine frame from other.
Definition: Coroutine.h:496
virtual ~AwaiterBase()=default
Destructor.
bool await_ready() const noexcept
Returns true if the inner coroutine has already finished.
Definition: Coroutine.h:571
Task(Task &&other) noexcept
Moves the coroutine frame from other.
Definition: Coroutine.h:486
virtual void onReady()=0
Finalizes the operation when the awaitable resumes.
bool done() const
Returns true if the coroutine has finished.
Definition: Coroutine.h:548
void cancel() override
Cancels the in-flight operation.
Definition: Coroutine.h:145
std::coroutine_handle await_suspend(std::coroutine_handle< P > outer)
Suspends the outer coroutine and starts the inner coroutine.
Definition: Coroutine.h:579
bool await_suspend(std::coroutine_handle< P > h)
Starts the operation and suspends the coroutine.
Definition: Coroutine.h:136
virtual void onCancel()=0
Aborts the in-flight operation.
virtual R onReady()=0
Returns the result for the co_await expression.
bool await_ready() const
Returns false so co_await always suspends.
Definition: Coroutine.h:130
AwaiterBase & operator=(const AwaiterBase &)=delete
No copy assignment.
Awaiter()
Constructor.
Definition: Coroutine.h:154
T await_resume()
Returns the result of the inner task.
Definition: Coroutine.h:595
void await_resume()
Resumes the coroutine after the operation completed.
Definition: Coroutine.h:239
void run()
Starts execution of the coroutine.
Definition: Coroutine.h:518
virtual void cancel()=0
Cancels the pending operation.
Provides an awaitable that delivers a result through onReady().
Definition: Coroutine.h:208
void setReady()
Resumes the waiting coroutine.
Definition: Coroutine.h:162
Task() noexcept
Constructs an empty task with no coroutine frame.
Definition: Coroutine.h:474
Provides the base class for I/O-driven co_await-able operations.
Definition: Coroutine.h:126