Skip to content

Commit 4a9b236

Browse files
committed
refactor dump_callstack
1 parent 79865c4 commit 4a9b236

File tree

8 files changed

+78
-51
lines changed

8 files changed

+78
-51
lines changed

include/asyncio/callstack.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@ struct CallStackAwaiter {
1414
constexpr void await_resume() const noexcept {}
1515
template<typename Promise>
1616
bool await_suspend(std::coroutine_handle<Promise> caller) const noexcept {
17-
size_t i = 0;
18-
for (Handle* handle = &caller.promise();
19-
handle != nullptr;
20-
handle = handle->get_continuation(), ++i) {
21-
fmt::print("[{}] {}\n", i, handle->name());
22-
}
17+
caller.promise().dump_backtrace();
2318
return false;
2419
}
2520
};

include/asyncio/concept/awaitable.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ template<typename A>
1212
struct GetAwaiter: std::type_identity<A> { };
1313

1414
template<typename A>
15-
requires requires(A a) { a.operator co_await(); }
15+
requires requires(A&& a) { std::forward<A>(a).operator co_await(); }
1616
struct GetAwaiter<A>: std::type_identity<decltype(std::declval<A>().operator co_await())> { };
1717

1818
template<typename A>
19-
requires requires(A a) {
20-
operator co_await(a);
21-
requires ! (requires { a.operator co_await(); });
19+
requires requires(A&& a) {
20+
operator co_await(std::forward<A>(a));
21+
requires ! (requires { std::forward<A>(a).operator co_await(); });
2222
}
2323
struct GetAwaiter<A>: std::type_identity<decltype(operator co_await(std::declval<A>()))> { };
2424

include/asyncio/gather.h

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ ASYNCIO_NS_BEGIN
1313
namespace detail {
1414

1515
template<typename... Rs>
16-
class GatherAwaiter {
16+
class GatherAwaiter: NonCopyable {
1717
using ResultTypes = std::tuple<GetTypeIfVoid_t<Rs>...>;
1818
public:
1919
constexpr bool await_ready() noexcept { return is_finished(); }
@@ -71,16 +71,31 @@ template<concepts::Awaitable... Futs> // C++17 deduction guide
7171
GatherAwaiter(Futs&&...) -> GatherAwaiter<AwaitResult<Futs>...>;
7272

7373
template<concepts::Awaitable... Futs>
74-
auto gather(NoWaitAtInitialSuspend, Futs&&... futs) // need NoWaitAtInitialSuspend to lift futures lifetime early
75-
-> Task<std::tuple<GetTypeIfVoid_t<AwaitResult<Futs>>...>> {
76-
// futures(tuple) to lift Future's lifetime
74+
struct GatherAwaiterRepositry {
75+
explicit GatherAwaiterRepositry(Futs&&... futs)
76+
: futs_(std::forward<Futs>(futs)...) { }
77+
78+
auto operator co_await() && {
79+
return std::apply([]<concepts::Awaitable... F>(F&&... f) {
80+
return GatherAwaiter { std::forward<F>(f)... };
81+
}, std::move(futs_));
82+
}
83+
84+
private:
85+
// futs_ to lift Future's lifetime
7786
// 1. if Future is rvalue(Fut&&), then move it to tuple(Fut)
7887
// 2. if Future is xvalue(Fut&&), then move it to tuple(Fut)
7988
// 3. if Future is lvalue(Fut&), then store as lvalue-ref(Fut&)
80-
std::tuple<Futs...> futures{ std::forward<Futs>(futs)... };
81-
co_return co_await std::apply([]<concepts::Awaitable... Fs>(Fs&&... fs) {
82-
return GatherAwaiter{ std::forward<Fs>(fs)... };
83-
}, std::move(futures));
89+
std::tuple<Futs...> futs_;
90+
};
91+
92+
template<concepts::Awaitable... Futs> // need deduction guide to deduce future type
93+
GatherAwaiterRepositry(Futs&&...) -> GatherAwaiterRepositry<Futs...>;
94+
95+
template<concepts::Awaitable... Futs>
96+
auto gather(NoWaitAtInitialSuspend, Futs&&... futs) // need NoWaitAtInitialSuspend to lift futures lifetime early
97+
-> Task<std::tuple<GetTypeIfVoid_t<AwaitResult<Futs>>...>> {
98+
co_return co_await detail::GatherAwaiterRepositry{ std::forward<Futs>(futs)... };
8499
}
85100
}
86101

include/asyncio/handle.h

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,21 @@ enum class PromiseState: uint8_t {
1515
PENDING,
1616
};
1717

18-
struct HandleFrameInfo {
19-
uint32_t line{0};
20-
std::string_view file_name;
21-
std::string_view func_name;
22-
constexpr HandleFrameInfo() = default;
23-
constexpr HandleFrameInfo(std::source_location loc):
24-
line(loc.line()),
25-
file_name(loc.file_name()),
26-
func_name(loc.function_name()) {}
27-
};
28-
2918
struct Handle {
3019
virtual void run() = 0;
31-
std::string name() {
20+
std::string frame_name() const {
3221
const auto& frame_info = get_frame_info();
33-
// return fmt::format("{} at {}:{}", frame_info.func_name,
34-
// frame_info.file_name, frame_info.line);
35-
return fmt::format("{} at {}", frame_info.func_name, frame_info.file_name);
22+
return fmt::format("{} at {}:{}", frame_info.function_name(),
23+
frame_info.file_name(), frame_info.line());
3624
}
37-
virtual Handle* get_continuation() { return nullptr; }
25+
virtual void dump_backtrace(size_t depth = 0) const {};
3826
virtual void set_state(PromiseState state) {}
3927
virtual ~Handle() = default;
4028
private:
41-
virtual const HandleFrameInfo& get_frame_info() = 0;
29+
virtual const std::source_location& get_frame_info() const {
30+
static std::source_location frame_info = std::source_location::current();
31+
return frame_info;
32+
}
4233
};
4334

4435
ASYNCIO_NS_END

include/asyncio/noncopyable.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ ASYNCIO_NS_BEGIN
1010
struct NonCopyable {
1111
protected:
1212
NonCopyable() = default;
13+
~NonCopyable() = default;
14+
NonCopyable(NonCopyable&&) = default;
15+
NonCopyable& operator=(NonCopyable&&) = default;
1316
NonCopyable(const NonCopyable&) = delete;
17+
NonCopyable& operator=(const NonCopyable&) = delete;
1418
};
1519
ASYNCIO_NS_END
1620

include/asyncio/task.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ struct Task: private NonCopyable {
6565
}
6666

6767
struct promise_type: Handle, Result<R> {
68-
promise_type(std::source_location loc = std::source_location::current()):
69-
frame_info_(loc) { }
68+
promise_type() = default;
7069

7170
template<typename... Args> // from free function
7271
promise_type(NoWaitAtInitialSuspend, Args&&...): wait_at_initial_suspend_{false} { }
@@ -99,25 +98,35 @@ struct Task: private NonCopyable {
9998
Task get_return_object() noexcept {
10099
return Task{coro_handle::from_promise(*this)};
101100
}
101+
template<concepts::Awaitable A>
102+
decltype(auto) await_transform(A&& awaiter, // for save source_location info
103+
std::source_location loc = std::source_location::current()) {
104+
frame_info_ = loc;
105+
return std::forward<A>(awaiter);
106+
}
102107

103108
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
104-
void run() override {
109+
void run() final {
105110
assert(state_ == PromiseState::PENDING);
106111
auto handle = coro_handle::from_promise(*this);
107112
// set to unschedule in advance, because 'resume' may change state
108113
state_ = PromiseState::UNSCHEDULED;
109114
handle.resume();
110115
}
111-
void set_state(PromiseState state) override { state_ = state; }
112-
const HandleFrameInfo& get_frame_info() override { return frame_info_; }
113-
Handle* get_continuation() override { return continuation_; }
116+
void set_state(PromiseState state) final { state_ = state; }
117+
const std::source_location& get_frame_info() const final { return frame_info_; }
118+
void dump_backtrace(size_t depth = 0) const final {
119+
fmt::print("[{}] {}\n", depth, frame_name());
120+
if (continuation_) { continuation_->dump_backtrace(depth + 1); }
121+
else { fmt::print("\n"); }
122+
}
114123
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
115124

116125
// to auto delete by final awaiter
117126
PromiseState state_ {PromiseState::UNSCHEDULED};
118127
const bool wait_at_initial_suspend_ {true};
119128
Handle* continuation_ {};
120-
HandleFrameInfo frame_info_{};
129+
std::source_location frame_info_{};
121130
};
122131

123132
bool valid() const { return handle_ != nullptr; }

include/asyncio/wait_for.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
ASYNCIO_NS_BEGIN
1515
namespace detail {
1616
template<typename R, typename Duration>
17-
struct WaitForAwaiter {
17+
struct WaitForAwaiter: NonCopyable {
1818
constexpr bool await_ready() noexcept { return false; }
1919
constexpr decltype(auto) await_resume() {
2020
return result_.result();
@@ -60,16 +60,12 @@ struct WaitForAwaiter {
6060
EventLoop& loop{get_event_loop()};
6161
loop.call_later(timeout, *this);
6262
}
63-
void run() override { // timeout!
63+
void run() final { // timeout!
6464
EventLoop& loop{get_event_loop()};
6565
loop.cancel_handle(current_task_);
6666
awaiter_.result_.set_exception(std::make_exception_ptr(TimeoutError{}));
6767
loop.call_soon(*awaiter_.continuation_);
6868
}
69-
const HandleFrameInfo& get_frame_info() override {
70-
static HandleFrameInfo frame_info;
71-
return frame_info;
72-
}
7369

7470
WaitForAwaiter& awaiter_;
7571
Handle& current_task_;
@@ -79,11 +75,27 @@ struct WaitForAwaiter {
7975
template<concepts::Awaitable Fut, typename Duration>
8076
WaitForAwaiter(Fut&&, Duration) -> WaitForAwaiter<AwaitResult<Fut>, Duration>;
8177

78+
template<concepts::Awaitable Fut, typename Duration>
79+
struct WaitForAwaiterRegistry {
80+
WaitForAwaiterRegistry(Fut&& fut, Duration duration)
81+
: fut_(std::forward<Fut>(fut)), duration_(duration) { }
82+
83+
auto operator co_await () && {
84+
return WaitForAwaiter{std::move(fut_), duration_};
85+
}
86+
private:
87+
Fut fut_; // lift Future's lifetime
88+
Duration duration_;
89+
};
90+
91+
template<concepts::Awaitable Fut, typename Duration>
92+
WaitForAwaiterRegistry(Fut&& fut, Duration duration)
93+
-> WaitForAwaiterRegistry<Fut, Duration>;
94+
8295
template<concepts::Awaitable Fut, typename Rep, typename Period>
8396
auto wait_for(NoWaitAtInitialSuspend, Fut&& fut, std::chrono::duration<Rep, Period> timeout)
8497
-> Task<AwaitResult<Fut>> {
85-
Fut future = std::forward<Fut>(fut); // lift fut lifetime
86-
co_return co_await WaitForAwaiter { std::forward<Fut>(future), timeout };
98+
co_return co_await WaitForAwaiterRegistry { std::forward<Fut>(fut), timeout };
8799
}
88100
}
89101

test/ut/task_test.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <catch2/catch_test_macros.hpp>
55
#include <catch2/catch_approx.hpp>
66
#include <asyncio/event_loop.h>
7+
#include <asyncio/callstack.h>
78
#include <asyncio/task.h>
89
#include <asyncio/gather.h>
910
#include <asyncio/exception.h>
@@ -79,7 +80,7 @@ SCENARIO("test Task await result value") {
7980
GIVEN("fibonacci") {
8081
std::function<auto(size_t) -> Task<size_t>> fibo =
8182
[&](size_t n) -> Task<size_t> {
82-
if (n <= 1) co_return n;
83+
if (n <= 1) { co_return n; }
8384
co_return co_await fibo(n - 1) +
8485
co_await fibo(n - 2);
8586
};

0 commit comments

Comments
 (0)