cutelyst  5.0.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
CoroContext.h
1 /*
2  * SPDX-FileCopyrightText: (C) 2024-2025 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-FileCopyrightText: (C) 2025 Matthias Fehring <mf@huessenbergnetz.de>
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 #pragma once
7 
8 #include <Cutelyst/async.h>
9 #include <Cutelyst/controller.h>
10 #include <coroutine>
11 #include <functional>
12 #include <memory>
13 
14 #include <QObject>
15 
16 namespace Cutelyst {
17 
36 {
37 public:
38  struct promise_type {
39  std::coroutine_handle<promise_type> handle;
40  std::vector<QMetaObject::Connection> connections;
41 
42  void clean()
43  {
44  for (const auto &conn : connections) {
45  QObject::disconnect(conn);
46  }
47  connections.clear();
48  }
49 
50  void return_void() noexcept {}
51 
52  CoroContext get_return_object()
53  {
54  handle = std::coroutine_handle<promise_type>::from_promise(*this);
55  return {};
56  }
57 
58  std::suspend_never initial_suspend() const noexcept { return {}; }
59  std::suspend_never final_suspend() noexcept { return {}; }
60  void unhandled_exception() {}
61 
62  bool await_ready() const noexcept { return false; }
63  void await_suspend(std::coroutine_handle<> h) noexcept {}
64  void await_resume() const noexcept
65  {
66  Q_ASSERT_X(!connections.empty(), "Cutelyst::CoroContext", "Did not yield any QObject*");
67  }
68 
69  std::suspend_never yield_value(const QObject *obj)
70  {
71  auto conn = QObject::connect(obj, &QObject::destroyed, [this] {
72  clean();
73 
74  if (handle) {
75  handle.destroy();
76  }
77  });
78  connections.emplace_back(std::move(conn));
79  return {};
80  }
81 
82  std::suspend_never yield_value(Cutelyst::Context *context)
83  {
84  trackContext(context);
85  return {};
86  }
87 
88  promise_type() = default; // required for lambdas
89 
90  template <typename... ArgTypes>
91  promise_type(QObject &obj, Cutelyst::Context *context, ArgTypes &&...)
92  {
93  Q_UNUSED(obj);
94  trackContext(context);
95  }
96 
97  template <typename... ArgTypes>
98  explicit promise_type(Cutelyst::Context *context, ArgTypes &&...)
99  {
100  trackContext(context);
101  }
102 
103  void trackContext(Context *context)
104  {
105  if (context) {
106  // Automatically delay replies
107  // async cannot be used in coroutine body
108  // else we get a double free when the coroutine
109  // body ends and Cutelyst::Engine deletes the Context*
110  // resulting in destroyed signal being emitted and
111  // and coroutine dtor already on the stack to be called
112  ASync a = context->response()->isFinalized() ? ASync{} : ASync{context};
113 
114  auto conn = QObject::connect(context, &QObject::destroyed, [this, a] {
115  clean();
116 
117  if (handle) {
118  handle.destroy();
119  }
120  });
121  connections.emplace_back(std::move(conn));
122  }
123  }
124 
125  ~promise_type() { clean(); }
126  };
127 };
128 
129 } // namespace Cutelyst
The CoroContext class.
Definition: CoroContext.h:35
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
Helper class for asynchronous processing.
Definition: async.h:15
The Cutelyst Context.
Definition: context.h:42
bool isFinalized() const noexcept
The Cutelyst namespace holds all public Cutelyst API.
bool disconnect(const QMetaObject::Connection &connection)
Response * response() const noexcept
Definition: context.cpp:98
void destroyed(QObject *obj)