Skip to content

Commit 62adfd5

Browse files
committed
UPD | docs: async context
1 parent 535f512 commit 62adfd5

File tree

5 files changed

+299
-385
lines changed

5 files changed

+299
-385
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ test_package/build
2727
test_package/CMakeUserPresets.json
2828
protobuf
2929
docs/yarn.lock
30-
Testing
30+
Testing
31+
main.cpp
32+
handlers.cpp
33+
handlers.hpp
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
---
2+
title: Async Context
3+
description: About Async Context
4+
---
5+
6+
## Concept
7+
When an application starts, we create multiple threads, each with its own async context. Each context contains its own EventLoop, TimerPool, and TaskPool, all running in the same thread.
8+
9+
The context that executes the ```manapi::async::context::run``` method can create copies of itself.
10+
11+
<Callout title="Note">Each thread can only contain one async context at the same time.</Callout>
12+
13+
### Example
14+
15+
```cpp
16+
#include <thread>
17+
18+
#include <manapihttp/ManapiEventLoop.hpp>
19+
#include <manapihttp/std/ManapiAsyncContext.hpp>
20+
21+
int main(int argc, char *argv[]) {
22+
manapi::async::context::threadpoolfs(4);
23+
auto blockedsignals = manapi::async::context::blockedsignals();
24+
auto ctx = manapi::async::context::create(/*threadnum*/ 4).unwrap();
25+
26+
std::atomic<int> runs;
27+
// Creates 4 copies of itself
28+
ctx->run(4, [&runs](std::function<void()> bind) -> void {
29+
manapi_log_debug("Async Context #%d has started. threadid=%zu",
30+
runs.fetch_add(1), std::hash<std::thread::id>{}(std::this_thread::get_id()));
31+
bind();
32+
});
33+
34+
return 0;
35+
}
36+
```
37+
38+
Output:
39+
```bash
40+
DEBUG main.cpp:12: Async Context #0 has started. threadid=12782699731343853225
41+
DEBUG main.cpp:12: Async Context #1 has started. threadid=12997127320206271399
42+
DEBUG main.cpp:12: Async Context #2 has started. threadid=12543349191528547103
43+
DEBUG main.cpp:12: Async Context #3 has started. threadid=4417295317971758371
44+
DEBUG main.cpp:12: Async Context #4 has started. threadid=17555092437611076153
45+
```
46+
47+
## TaskPool
48+
49+
```TaskPool``` is unique to each ```EventLoop```. You can add tasks to the ```EventLoop``` that will be executed in the next iteration.
50+
51+
```cpp
52+
#include <manapihttp/ManapiEventLoop.hpp>
53+
#include <manapihttp/ManapiThreadPool.hpp>
54+
#include <manapihttp/std/ManapiAsyncContext.hpp>
55+
56+
int main(int argc, char *argv[]) {
57+
manapi::async::context::threadpoolfs(4);
58+
auto blockedsignals = manapi::async::context::blockedsignals();
59+
auto ctx = manapi::async::context::create(/*threadnum*/ 4).unwrap();
60+
61+
std::atomic<int> runs;
62+
ctx->run(4, [&runs](std::function<void()> bind) -> void {
63+
manapi::async::current()->etaskpool()->append_task([&runs]() -> void {
64+
manapi_log_debug("Hello from Context #%d", runs.fetch_add(1));
65+
});
66+
67+
bind();
68+
});
69+
70+
return 0;
71+
}
72+
```
73+
74+
Output:
75+
```bash
76+
DEBUG main.cpp:16: Hello from Context #0
77+
DEBUG main.cpp:16: Hello from Context #2
78+
DEBUG main.cpp:16: Hello from Context #3
79+
DEBUG main.cpp:16: Hello from Context #1
80+
DEBUG main.cpp:16: Hello from Context #4
81+
```
82+
83+
<Callout title="Information">Instead of ```append_task()```, you can use ```append_static_task()```, which avoids dynamic memory allocation and stores only **64 bytes** of information.</Callout>
84+
85+
## TimerPool
86+
87+
TimerPool manages timeouts and intervals. Each timer has a priority level:
88+
89+
- **```TIMER_IMPORTANT```**
90+
The Async Context will continue running until no important timers remain.
91+
92+
```cpp
93+
#include <manapihttp/ManapiEventLoop.hpp>
94+
#include <manapihttp/ManapiTimerPool.hpp>
95+
#include <manapihttp/ManapiThreadPool.hpp>
96+
#include <manapihttp/std/ManapiAsyncContext.hpp>
97+
98+
int main(int argc, char *argv[]) {
99+
manapi::async::context::threadpoolfs(4);
100+
auto blockedsignals = manapi::async::context::blockedsignals();
101+
auto ctx = manapi::async::context::create(/*threadnum*/ 4).unwrap();
102+
103+
ctx->run([](std::function<void()> bind) -> void {
104+
manapi::async::current()->timerpool()->append_timer_sync(5000, manapi::TIMER_IMPORTANT, [](auto t) -> void {
105+
manapi_log_debug("5000ms timeout reached");
106+
});
107+
108+
manapi::async::run(manapi::async::current()->stop());
109+
110+
bind();
111+
});
112+
113+
return 0;
114+
}
115+
```
116+
117+
This code will print ```5000ms timeout reached``` and then terminate.
118+
119+
- **```TIMER_DEFAULT```**
120+
The Async Context does not wait for these timers to complete.
121+
122+
```cpp
123+
#include <manapihttp/ManapiEventLoop.hpp>
124+
#include <manapihttp/ManapiTimerPool.hpp>
125+
#include <manapihttp/ManapiThreadPool.hpp>
126+
#include <manapihttp/std/ManapiAsyncContext.hpp>
127+
128+
int main(int argc, char *argv[]) {
129+
manapi::async::context::threadpoolfs(4);
130+
auto blockedsignals = manapi::async::context::blockedsignals();
131+
auto ctx = manapi::async::context::create(/*threadnum*/ 4).unwrap();
132+
133+
ctx->run([](std::function<void()> bind) -> void {
134+
manapi::async::current()->timerpool()->append_timer_sync(5000, [](auto t) -> void {
135+
manapi_log_debug("5000ms timeout reached");
136+
});
137+
138+
manapi::async::run(manapi::async::current()->stop());
139+
140+
bind();
141+
});
142+
143+
return 0;
144+
}
145+
```
146+
147+
This code may not print the message unless the ```EventLoop``` remains active for more than 5 seconds.
148+
149+
### Methods
150+
151+
TimerPool provides four main methods:
152+
153+
```cpp
154+
// After 5 seconds (synchronous)
155+
manapi::async::current()->timerpool()->append_timer_sync(5000, [](manapi::timer t) -> void {
156+
manapi_log_debug("5000ms timeout reached");
157+
});
158+
159+
// After 5 seconds (asynchronous)
160+
manapi::async::current()->timerpool()->append_timer_async(5000, [](manapi::timer t) -> manapi::future<> {
161+
manapi_log_debug("5000ms timeout reached");
162+
co_return;
163+
});
164+
165+
// Every 5 seconds (synchronous)
166+
manapi::async::current()->timerpool()->append_interval_sync(5000, [](manapi::timer t) -> void {
167+
manapi_log_debug("5000ms interval reached");
168+
});
169+
170+
// Every 5 seconds (asynchronous)
171+
manapi::async::current()->timerpool()->append_interval_async(5000, [](manapi::timer t) -> manapi::future<> {
172+
manapi_log_debug("5000ms interval reached");
173+
co_return;
174+
});
175+
```
176+
177+
### Timer Control
178+
179+
You can stop an active ```Timer``` at any time using the ```stop()``` method:
180+
181+
```cpp
182+
// Every 5 seconds
183+
manapi::async::current()->timerpool()->append_interval_sync(5000, [a = 0](manapi::timer t) mutable -> void {
184+
manapi_log_debug("5000ms interval reached");
185+
if (++a == 2) {
186+
// Immediately stop the timer
187+
t.stop();
188+
}
189+
});
190+
```
191+
192+
Output:
193+
```bash
194+
DEBUG main.cpp:16: 5000ms interval reached
195+
DEBUG main.cpp:16: 5000ms interval reached
196+
```
197+
198+
You can also use the ```again()``` method to reactivate or modify a timer:
199+
200+
```cpp
201+
manapi::async::current()->timerpool()->append_interval_sync(1000, [a = 0](manapi::timer t) mutable -> void {
202+
manapi_log_debug("%dms interval reached", t.interval().count());
203+
++a;
204+
if (a == 2) {
205+
t.again(200);
206+
}
207+
if (a == 5) {
208+
t.stop();
209+
}
210+
});
211+
```
212+
213+
Output:
214+
```bash
215+
DEBUG main.cpp:16: 1000ms interval reached
216+
DEBUG main.cpp:16: 1000ms interval reached
217+
DEBUG main.cpp:16: 200ms interval reached
218+
DEBUG main.cpp:16: 200ms interval reached
219+
DEBUG main.cpp:16: 200ms interval reached
220+
```
221+
222+
## Async Tasks
223+
To execute ```manapi::future<>``` tasks, use the ```manapi::async::run``` method:
224+
225+
```cpp
226+
#include <manapihttp/ManapiEventLoop.hpp>
227+
#include <manapihttp/ManapiTimerPool.hpp>
228+
#include <manapihttp/ManapiFetch2.hpp>
229+
#include <manapihttp/ManapiThreadPool.hpp>
230+
#include <manapihttp/std/ManapiAsyncContext.hpp>
231+
232+
int main(int argc, char *argv[]) {
233+
manapi::async::context::threadpoolfs(4);
234+
auto blockedsignals = manapi::async::context::blockedsignals();
235+
auto ctx = manapi::async::context::create(/*threadnum*/ 4).unwrap();
236+
237+
ctx->run([](std::function<void()> bind) -> void {
238+
// Execute future<> task
239+
manapi::async::run([]() -> manapi::future<> {
240+
auto fetch = manapi::unwrap(co_await manapi::net::fetch2::fetch(
241+
"http://example.com"));
242+
auto body = manapi::unwrap(co_await fetch.text());
243+
manapi_log_debug("%s", body.data());
244+
});
245+
246+
bind();
247+
});
248+
249+
return 0;
250+
}
251+
```
252+
253+
<Callout title="Note">```manapi::async::run``` executes the callback in the current scope immediately when called.</Callout>
254+
255+
<Callout title="Warning" type="warn">Use ```manapi::async::invoke()``` if you need to wait for lambda execution. Otherwise, data stored in the lambda may be destroyed prematurely.</Callout>
256+
257+
```cpp
258+
manapi::async::run([]() -> manapi::future<> {
259+
...
260+
co_await manapi::async::invoke([&]() -> manapi::future<> {
261+
...
262+
});
263+
});
264+
```
265+
266+
## Stopping Mechanism
267+
268+
To stop the current context, use the asynchronous ```stop()``` method in the ```manapi::async::context``` class:
269+
270+
```cpp
271+
ctx->run([](std::function<void()> bind) -> void {
272+
// The current context will stop as soon as possible
273+
manapi::async::run(manapi::async::current()->stop());
274+
bind();
275+
});
276+
```

docs/content/docs/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"intro/syslogs",
88
"intro/threadpoolfs",
99
"intro/blockedsignals",
10+
"intro/async-context",
1011
"---Data Formats---",
1112
"data-formats/json"
1213
]

include/std/ManapiAsyncContext.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,11 @@ namespace manapi {
8282
#if defined (__unix__) || defined(__APPLE__)
8383
struct sigset_t : public ::sigset_t {};
8484
#else
85+
# define MANAPIHTTP_SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
8586
struct sigset_t {
86-
char payload[1];
87+
unsigned long int payload[MANAPIHTTP_SIGSET_NWORDS];
8788
};
89+
# undef MANAPIHTTP_SIGSET_NWORDS
8890
#endif
8991

9092
class event_loop;

0 commit comments

Comments
 (0)