Skip to content

Commit b3aba7f

Browse files
committed
📝 Proxy diagram and explanation
1 parent e308aa3 commit b3aba7f

File tree

1 file changed

+110
-25
lines changed

1 file changed

+110
-25
lines changed

README.md

Lines changed: 110 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,14 @@ async::future<void> delay_example(async::context& p_ctx) {
160160

161161
```cpp
162162
async::future<void> io_example(async::context& p_ctx) {
163-
dma_controller.on_completion([&ctx]() {
164-
ctx.unblock();
163+
dma_controller.on_completion([&p_ctx]() {
164+
p_ctx.unblock();
165165
});
166166

167167
// Start DMA transaction...
168168

169169
while (!dma_complete) {
170-
co_await ctx.block_by_io();
170+
co_await p_ctx.block_by_io();
171171
}
172172
co_return;
173173
}
@@ -229,6 +229,32 @@ ctx.sync_wait([](async::sleep_duration p_sleep_time) {
229229
});
230230
```
231231

232+
## Exception Handling
233+
234+
Exceptions thrown in coroutines are propagated through the coroutine chain
235+
until it reaches the top level coroutine. When the top level is reached, the
236+
exception will be thrown from a call to `.resume()`.
237+
238+
```cpp
239+
async::future<void> may_throw(async::context& p_ctx) {
240+
throw std::runtime_error("error");
241+
co_return;
242+
}
243+
244+
async::future<void> just_calls(async::context& p_ctx) {
245+
co_await may_throw(p_ctx);
246+
co_return;
247+
}
248+
249+
simple_context ctx;
250+
auto future = may_throw(ctx);
251+
try {
252+
future.resume();
253+
} catch (const std::runtime_error& e) {
254+
// Handle exception
255+
}
256+
```
257+
232258
### Proxy Context for Timeouts
233259
234260
```cpp
@@ -249,29 +275,88 @@ async::future<int> supervised(async::context& p_ctx) {
249275
}
250276
```
251277

252-
## Exception Handling
253-
254-
Exceptions thrown in coroutines are propagated through the coroutine chain
255-
until it reaches the top level coroutine. When the top level is reached, the
256-
exception will be thrown from a call to `.resume()`.
257-
258-
```cpp
259-
async::future<void> may_throw(async::context& ctx) {
260-
throw std::runtime_error("error");
261-
co_return;
262-
}
263-
264-
async::future<void> just_calls(async::context& ctx) {
265-
co_await may_throw(ctx);
266-
co_return;
267-
}
278+
`async::proxy_context::from()`: consumes the rest of the stack memory of the
279+
context for itself. The original context's stack memory will be clamped to
280+
where it was when it called the `supervised` function. The stack memory is
281+
restored to the original context, once the proxy is destroyed. This prevents
282+
the context from being used again and overwriting the memory of the stack.
283+
284+
Each coroutine frame is allowed to have at most 1 proxy on its stack. This
285+
allows for top down chaining of proxies as shown below. When a proxy blocks by
286+
something, that blocking state is communicated to the original context and its
287+
schedule function is executed. When the original context is resumed, it will
288+
execute from its active coroutine which contains a proxy. That coroutine may
289+
check for timeouts and resume its supervised future. Then that future may have
290+
yet another proxy which performs the same work of timeout detection as before
291+
and resumes the future it has supervision for. This continues on until the
292+
bottom is reached or a coroutine decides to cancel its future or exit via
293+
exception or normal return path.
294+
295+
This naturally gives the top most supervising coroutine priority to determine
296+
if its time frame has expired.
268297

269-
auto future = may_throw(ctx);
270-
try {
271-
future.resume();
272-
} catch (const std::runtime_error& e) {
273-
// Handle exception
274-
}
298+
```ascii
299+
┌─────────────────────────────┐ Address 0
300+
│ &context::m_stack_pointer │
301+
├─────────────────────────────┤
302+
│ Coroutine Frame A │
303+
│ (promise + locals) │
304+
│ (96 B) │
305+
├─────────────────────────────┤
306+
│ &context::m_stack_pointer │
307+
├─────────────────────────────┤
308+
│ Coroutine Frame B │
309+
│ (64 B) │
310+
│ (promise + locals) │
311+
├─────────────────────────────┤
312+
│ &context::m_stack_pointer │ <-- Origin Stack ends here
313+
├─────────────────────────────┤ (m_stack_pointer shrunk to here)
314+
│ Coroutine Frame C │
315+
│ (128 B) │ Proxy-1 Stack begins
316+
│ (promise + locals) │
317+
│ │
318+
├─────────────────────────────┤
319+
│ &proxy1::m_stack_pointer │
320+
├─────────────────────────────┤
321+
│ Coroutine Frame D │
322+
│ (80 B) │
323+
│ (promise + locals) │
324+
├─────────────────────────────┤
325+
│ &proxy1::m_stack_pointer │
326+
├─────────────────────────────┤
327+
│ Coroutine Frame E │
328+
│ (72 B) │
329+
│ (promise + locals) │
330+
├─────────────────────────────┤
331+
│ &proxy1::m_stack_pointer │ <-- Proxy-1 Stack ends here
332+
├─────────────────────────────┤
333+
│ Coroutine Frame F │
334+
│ (96 B) │ Proxy-2 Stack begins
335+
│ (promise + locals) │
336+
├─────────────────────────────┤
337+
│ &proxy2::m_stack_pointer │
338+
├─────────────────────────────┤
339+
│ Coroutine Frame G │
340+
│ (144 B) │
341+
│ (promise + locals) │
342+
│ │
343+
├─────────────────────────────┤
344+
│ &proxy2::m_stack_pointer │ <-- Proxy-2 Stack ends here
345+
├─────────────────────────────┤
346+
│ Coroutine Frame H │
347+
│ (88 B) │ Proxy-3 Stack begins
348+
│ (promise + locals) │
349+
├─────────────────────────────┤
350+
│ &proxy3::m_stack_pointer │ <-- Proxy-3 current position
351+
├─────────────────────────────┤
352+
│ Unused Memory │
353+
│ │
354+
│ │
355+
│ │
356+
│ │
357+
│ │
358+
│ │
359+
└─────────────────────────────┘ Address N (bytes of stack memory)
275360
```
276361

277362
## Creating the package

0 commit comments

Comments
 (0)