|
4 | 4 |
|
5 | 5 | #pragma once |
6 | 6 |
|
7 | | -/** |
8 | | - * This header defines the data structures used to represent a coroutine asynchronous stack. |
9 | | - * |
10 | | - * Overview: |
11 | | - * The asynchronous stack is used to manage the execution state of coroutines, allowing for |
12 | | - * efficient context switching and stack management. |
13 | | - * |
14 | | - * Diagram: Normal and Asynchronous Stacks |
15 | | - * |
16 | | - * Base Pointer (%rbp) async_stack_root |
17 | | - * | | |
18 | | - * V V |
19 | | - * stack_frame async_stack_frame |
20 | | - * | | |
21 | | - * V V |
22 | | - * stack_frame async_stack_frame |
23 | | - * ... ... |
24 | | - * | | |
25 | | - * V V |
26 | | - * stack_frame async_stack_frame |
27 | | - * | | |
28 | | - * V V |
29 | | - * |
30 | | - * In the diagram above, the left side represents a typical call stack with stack frames linked by |
31 | | - * the base pointer (%rbp). The right side illustrates an asynchronous stack where `async_stack_frame` |
32 | | - * structures are linked by `async_stack_root`. |
33 | | - * |
34 | | - * Diagram: Backtrace Mechanism |
35 | | - * |
36 | | - * Base Pointer (%rbp) |
37 | | - * | |
38 | | - * V |
39 | | - * stack_frame |
40 | | - * | |
41 | | - * V |
42 | | - * stack_frame (stop_sync_frame) <- async_stack_root |
43 | | - * | |
44 | | - * V |
45 | | - * async_stack_frame (top_async_frame) |
46 | | - * | |
47 | | - * V |
48 | | - * async_stack_frame |
49 | | - * ... |
50 | | - * | |
51 | | - * V |
52 | | - * async_stack_frame |
53 | | - * |
54 | | - * The backtrace mechanism involves traversing the stack frames to capture the call stack. |
55 | | - * The `stop_sync_frame` serves as a marker where the transition to the asynchronous stack occurs, |
56 | | - * allowing the backtrace to continue through the `async_stack_frame` structures. |
57 | | - */ |
| 7 | +#include "runtime-light/coroutine/async-stack-structs.h" |
58 | 8 |
|
59 | | -#define STACK_RETURN_ADDRESS __builtin_return_address(0) |
| 9 | +#include <coroutine> |
| 10 | +#include <utility> |
60 | 11 |
|
61 | | -#define STACK_FRAME_ADDRESS __builtin_frame_address(0) |
| 12 | +#include "runtime-light/coroutine/coroutine-state.h" |
| 13 | +#include "runtime-light/stdlib/diagnostics/logs.h" |
62 | 14 |
|
63 | 15 | namespace kphp::coro { |
64 | 16 |
|
65 | | -struct stack_frame { |
66 | | - stack_frame* caller_stack_frame{}; |
67 | | - void* return_address{}; |
| 17 | +struct async_stack_root_wrapper { |
| 18 | + async_stack_root root; |
| 19 | + async_stack_root_wrapper() = default; |
| 20 | + async_stack_root_wrapper(const async_stack_root_wrapper& other) = default; |
| 21 | + async_stack_root_wrapper& operator=(const async_stack_root_wrapper& other) = default; |
| 22 | + ~async_stack_root_wrapper() { |
| 23 | + CoroutineInstanceState::get().current_async_stack_root = root.next_async_stack_root; |
| 24 | + } |
68 | 25 | }; |
69 | 26 |
|
70 | | -struct async_stack_root; |
71 | | - |
72 | | -struct async_stack_frame { |
73 | | - async_stack_frame* caller_async_stack_frame{}; |
74 | | - async_stack_root* async_stack_root{}; |
75 | | - void* return_address{}; |
76 | | -}; |
77 | | - |
78 | | -struct async_stack_root { |
79 | | - async_stack_frame* top_async_stack_frame{}; |
80 | | - stack_frame* stop_sync_stack_frame{}; |
81 | | - async_stack_root* next_async_stack_root{}; |
82 | | -}; |
| 27 | +inline void preparation_for_resume(kphp::coro::async_stack_root* root, void* stack_frame_addr, |
| 28 | + CoroutineInstanceState& coroutine_state = CoroutineInstanceState::get()) { |
| 29 | + root->stop_sync_stack_frame = reinterpret_cast<stack_frame*>(stack_frame_addr); |
| 30 | + coroutine_state.current_async_stack_root = root; |
| 31 | +} |
83 | 32 |
|
84 | 33 | /** |
85 | | - * The async_stack_element class facilitates working with asynchronous traces in templated code. |
86 | | - * This allows for uniform handling of any coroutines in places where async frames are pushed or popped. |
| 34 | + * The `resume_with_new_root` function is responsible for storing the current synchronous stack frame |
| 35 | + * in async_stack_root::stop_sync_frame before resuming the coroutine. This allows |
| 36 | + * capturing one of the stack frames in the synchronous stack trace and jump between parts of the async_stack. |
87 | 37 | */ |
88 | | -struct async_stack_element { |
89 | | - async_stack_frame& get_async_stack_frame() noexcept { |
90 | | - return m_async_stack_frame; |
91 | | - } |
92 | | - |
93 | | -private: |
94 | | - async_stack_frame m_async_stack_frame; |
95 | | -}; |
96 | | - |
| 38 | +[[clang::noinline]] inline void resume_with_new_root(std::coroutine_handle<> handle, async_stack_root* new_async_stack_root) noexcept { |
| 39 | + kphp::log::assertion(new_async_stack_root != nullptr); |
| 40 | + auto& coroutine_st{CoroutineInstanceState::get()}; |
| 41 | + // auto* previous_stack_root{coroutine_st.current_async_stack_root}; |
| 42 | + |
| 43 | + new_async_stack_root->next_async_stack_root = coroutine_st.current_async_stack_root; |
| 44 | + new_async_stack_root->stop_sync_stack_frame = reinterpret_cast<stack_frame*>(STACK_FRAME_ADDRESS); |
| 45 | + |
| 46 | + coroutine_st.current_async_stack_root = new_async_stack_root; |
| 47 | + handle.resume(); |
| 48 | + new_async_stack_root->stop_sync_stack_frame = nullptr; |
| 49 | +} |
97 | 50 | } // namespace kphp::coro |
0 commit comments