@@ -160,14 +160,14 @@ async::future<void> delay_example(async::context& p_ctx) {
160160
161161``` cpp
162162async::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