Skip to content

Commit 8f05af7

Browse files
committed
major fix
1 parent 5c41fd7 commit 8f05af7

File tree

12 files changed

+198
-173
lines changed

12 files changed

+198
-173
lines changed

runtime-light/coroutine/async-stack-methods.h

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Compiler for PHP (aka KPHP)
2+
// Copyright (c) 2025 LLC «V Kontakte»
3+
// Distributed under the GPL v3 License, see LICENSE.notice.txt
4+
5+
#pragma once
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+
*/
58+
59+
#define STACK_RETURN_ADDRESS __builtin_return_address(0)
60+
61+
#define STACK_FRAME_ADDRESS __builtin_frame_address(0)
62+
63+
namespace kphp::coro {
64+
65+
66+
67+
struct stack_frame {
68+
stack_frame* caller_stack_frame{};
69+
void* return_address{};
70+
};
71+
72+
struct async_stack_root;
73+
74+
struct async_stack_frame {
75+
async_stack_frame* caller_async_stack_frame{};
76+
async_stack_root* async_stack_root{};
77+
void* return_address{};
78+
};
79+
80+
struct async_stack_root {
81+
async_stack_frame* top_async_stack_frame{};
82+
stack_frame* stop_sync_stack_frame{};
83+
async_stack_root* next_async_stack_root{};
84+
};
85+
86+
/**
87+
* The async_stack_element class facilitates working with asynchronous traces in templated code.
88+
* This allows for uniform handling of any coroutines in places where async frames are pushed or popped.
89+
*/
90+
struct async_stack_element {
91+
async_stack_frame& get_async_stack_frame() noexcept {
92+
return m_async_stack_frame;
93+
}
94+
95+
private:
96+
async_stack_frame m_async_stack_frame;
97+
};
98+
99+
/**
100+
* The async_stack_element class facilitates working with asynchronous traces in templated code.
101+
* This allows for uniform handling of any coroutines in places where async frames are pushed or popped.
102+
*/
103+
104+
} // namespace kphp::coro

runtime-light/coroutine/async-stack.h

Lines changed: 33 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,94 +4,47 @@
44

55
#pragma once
66

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"
588

59-
#define STACK_RETURN_ADDRESS __builtin_return_address(0)
9+
#include <coroutine>
10+
#include <utility>
6011

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"
6214

6315
namespace kphp::coro {
6416

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+
}
6825
};
6926

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+
}
8332

8433
/**
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.
8737
*/
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+
}
9750
} // namespace kphp::coro

runtime-light/coroutine/await-set.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class await_set {
4040
template<typename awaitable_type>
4141
requires kphp::coro::concepts::awaitable<awaitable_type> && std::is_same_v<typename awaitable_traits<awaitable_type>::awaiter_return_type, return_type>
4242
void push(awaitable_type awaitable) noexcept {
43-
m_await_broker->start_task(detail::await_set::make_await_set_task(std::move(awaitable)), STACK_RETURN_ADDRESS);
43+
m_await_broker->start_task(detail::await_set::make_await_set_task(std::move(awaitable)));
4444
}
4545

4646
auto next() noexcept {

runtime-light/coroutine/coroutine-state.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#include "common/mixin/not_copyable.h"
88

9-
#include "runtime-light/coroutine/async-stack.h"
9+
#include "runtime-light/coroutine/async-stack-structs.h"
1010

1111
struct CoroutineInstanceState final : private vk::not_copyable {
1212

runtime-light/coroutine/detail/await-set.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
#include "runtime-common/core/allocator/script-malloc-interface.h"
1414
#include "runtime-common/core/std/containers.h"
15-
#include "runtime-light/coroutine/async-stack-methods.h"
1615
#include "runtime-light/coroutine/async-stack.h"
1716
#include "runtime-light/coroutine/type-traits.h"
1817
#include "runtime-light/coroutine/void-value.h"
@@ -66,9 +65,9 @@ class await_broker {
6665
kphp::memory::script::free(ptr);
6766
}
6867

69-
void start_task(await_set_task<return_type>&& task, void* return_address) noexcept {
68+
void start_task(await_set_task<return_type>&& task) noexcept {
7069
auto task_iterator{m_tasks_storage.insert(m_tasks_storage.begin(), std::move(task))};
71-
task_iterator->start(*this, task_iterator, return_address);
70+
task_iterator->start(*this, task_iterator);
7271
}
7372

7473
void push_ready_task(await_set_ready_task_element<return_type>& ready_task) noexcept {
@@ -158,6 +157,7 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element {
158157
std::optional<std::reference_wrapper<await_broker<return_type>>> m_await_broker{};
159158
await_set_ready_task_element<return_type> m_ready_task_element{};
160159

160+
kphp::coro::async_stack_root_wrapper root_wrapper_{};
161161
public:
162162
await_set_task_promise_base() noexcept = default;
163163

@@ -197,8 +197,8 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element {
197197
kphp::log::error("internal unhandled exception");
198198
}
199199

200-
auto start(detail::await_set::await_broker<return_type>& await_broker,
201-
kphp::stl::list<await_set_task<return_type>, kphp::memory::script_allocator>::iterator storage_location, void* return_address) noexcept {
200+
[[clang::noinline]] auto start(detail::await_set::await_broker<return_type>& await_broker,
201+
kphp::stl::list<await_set_task<return_type>, kphp::memory::script_allocator>::iterator storage_location) noexcept {
202202
m_await_broker = await_broker;
203203
m_ready_task_element.m_storage_location = storage_location;
204204

@@ -207,15 +207,14 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element {
207207
* await_set_task is the top of the stack for calls from it.
208208
* Therefore, it doesn't store caller_frame, but it save `await_set::push()` return address
209209
* */
210-
kphp::coro::async_stack_root root{};
211210
auto& async_stack_frame{get_async_stack_frame()};
212211
async_stack_frame.caller_async_stack_frame = nullptr;
213-
async_stack_frame.async_stack_root = std::addressof(root);
214-
async_stack_frame.return_address = return_address;
212+
async_stack_frame.async_stack_root = std::addressof(root_wrapper_.root);
213+
async_stack_frame.return_address = STACK_RETURN_ADDRESS; // this is necessary to prevent double printing of a coroutine
215214
async_stack_frame.async_stack_root->top_async_stack_frame = std::addressof(async_stack_frame);
216215

217216
decltype(auto) handle = std::coroutine_handle<promise_type>::from_promise(*static_cast<promise_type*>(this));
218-
kphp::coro::resume_with_new_root(handle, std::addressof(root));
217+
kphp::coro::resume_with_new_root(handle, std::addressof(root_wrapper_.root));
219218
}
220219
};
221220

@@ -271,8 +270,8 @@ class await_set_task {
271270
};
272271

273272
auto start(detail::await_set::await_broker<return_type>& await_broker,
274-
kphp::stl::list<await_set_task<return_type>, kphp::memory::script_allocator>::iterator storage_location, void* return_address) noexcept {
275-
m_coroutine.promise().start(await_broker, storage_location, return_address);
273+
kphp::stl::list<await_set_task<return_type>, kphp::memory::script_allocator>::iterator storage_location) noexcept {
274+
m_coroutine.promise().start(await_broker, storage_location);
276275
}
277276

278277
public:

0 commit comments

Comments
 (0)