Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f8cf7a6

Browse files
committed
Update recursive_generator to use tailcall suspend/resume.
- recursive_generator's final_suspend now directly resumes the parent generator using symmetric control transfer in await_suspend() rather than trampolining off a loop in the pull() method. - Recursive yield_value() now has much simpler logic for maintaining the root/parent/leaf pointers due to use of symmetric transfer meaning we don't have to explicitly handle a coroutine completing synchronously. - yield_value() now stores pointer to value in the root generator's m_value. This should make repeated iterator dereferences slightly more efficient as the derference now has one less pointer dereference (the extra dereference is now in the yield_value() method).
1 parent dfc4128 commit f8cf7a6

File tree

1 file changed

+72
-36
lines changed

1 file changed

+72
-36
lines changed

include/cppcoro/recursive_generator.hpp

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef CPPCORO_GENERATOR_HPP_INCLUDED
66
#define CPPCORO_GENERATOR_HPP_INCLUDED
77

8+
#include <cppcoro/detail/dummy_coroutine.hpp>
9+
810
#include <experimental/coroutine>
911
#include <type_traits>
1012
#include <utility>
@@ -41,9 +43,52 @@ namespace cppcoro
4143
return {};
4244
}
4345

44-
std::experimental::suspend_always final_suspend() noexcept
46+
auto final_suspend() noexcept
4547
{
46-
return {};
48+
class awaitable
49+
{
50+
public:
51+
52+
awaitable(promise_type* promise) noexcept
53+
: m_promise(promise)
54+
{}
55+
56+
constexpr bool await_ready() noexcept { return false; }
57+
58+
std::experimental::coroutine_handle<> await_suspend(
59+
std::experimental::coroutine_handle<> coroutine)
60+
{
61+
// If we are the root generator then just suspend without
62+
// resuming anything else to return control back to operator++().
63+
// Otherwise, we resume the parent generator.
64+
auto* const root = m_promise->m_root;
65+
if (m_promise == root)
66+
{
67+
return detail::dummy_coroutine::handle();
68+
}
69+
70+
// Set the leaf of the root generator to be the parent generator
71+
// we're about to resume.
72+
auto* parent = m_promise->m_parentOrLeaf;
73+
root->m_parentOrLeaf = parent;
74+
75+
// Reset our root/leaf to be self-contained.
76+
m_promise->m_root = m_promise;
77+
m_promise->m_parentOrLeaf = m_promise;
78+
79+
// Resume the parent now that we're suspended.
80+
return parent->handle();
81+
}
82+
83+
void await_resume() noexcept {}
84+
85+
private:
86+
87+
promise_type* m_promise;
88+
89+
};
90+
91+
return awaitable{ this };
4792
}
4893

4994
void unhandled_exception() noexcept
@@ -55,13 +100,13 @@ namespace cppcoro
55100

56101
std::experimental::suspend_always yield_value(T& value) noexcept
57102
{
58-
m_value = std::addressof(value);
103+
m_root->m_value = std::addressof(value);
59104
return {};
60105
}
61106

62107
std::experimental::suspend_always yield_value(T&& value) noexcept
63108
{
64-
m_value = std::addressof(value);
109+
m_root->m_value = std::addressof(value);
65110
return {};
66111
}
67112

@@ -81,40 +126,33 @@ namespace cppcoro
81126

82127
bool await_ready() noexcept
83128
{
84-
return this->m_childPromise == nullptr;
129+
return m_childPromise == nullptr || m_childPromise->is_complete();
85130
}
86131

87-
void await_suspend(std::experimental::coroutine_handle<promise_type>) noexcept
88-
{}
132+
std::experimental::coroutine_handle<> await_suspend(
133+
std::experimental::coroutine_handle<promise_type> parentHandle) noexcept
134+
{
135+
auto& parentPromise = parentHandle.promise();
136+
auto* rootPromise = parentPromise.m_root;
137+
m_childPromise->m_root = rootPromise;
138+
m_childPromise->m_parentOrLeaf = &parentPromise;
139+
rootPromise->m_parentOrLeaf = m_childPromise;
140+
return m_childPromise->handle();
141+
}
89142

90143
void await_resume()
91144
{
92-
if (this->m_childPromise != nullptr)
145+
if (m_childPromise != nullptr)
93146
{
94-
this->m_childPromise->throw_if_exception();
147+
m_childPromise->throw_if_exception();
95148
}
96149
}
97150

98151
private:
99152
promise_type* m_childPromise;
100153
};
101154

102-
if (generator.m_promise != nullptr)
103-
{
104-
m_root->m_parentOrLeaf = generator.m_promise;
105-
generator.m_promise->m_root = m_root;
106-
generator.m_promise->m_parentOrLeaf = this;
107-
generator.m_promise->resume();
108-
109-
if (!generator.m_promise->is_complete())
110-
{
111-
return awaitable{ generator.m_promise };
112-
}
113-
114-
m_root->m_parentOrLeaf = this;
115-
}
116-
117-
return awaitable{ nullptr };
155+
return awaitable{ generator.m_promise };
118156
}
119157

120158
// Don't allow any use of 'co_await' inside the recursive_generator coroutine.
@@ -123,7 +161,7 @@ namespace cppcoro
123161

124162
void destroy() noexcept
125163
{
126-
std::experimental::coroutine_handle<promise_type>::from_promise(*this).destroy();
164+
handle().destroy();
127165
}
128166

129167
void throw_if_exception()
@@ -136,35 +174,33 @@ namespace cppcoro
136174

137175
bool is_complete() noexcept
138176
{
139-
return std::experimental::coroutine_handle<promise_type>::from_promise(*this).done();
177+
return handle().done();
140178
}
141179

142180
T& value() noexcept
143181
{
144182
assert(this == m_root);
145183
assert(!is_complete());
146-
return *(m_parentOrLeaf->m_value);
184+
return *m_value;
147185
}
148186

149187
void pull() noexcept
150188
{
151189
assert(this == m_root);
152190
assert(!m_parentOrLeaf->is_complete());
153-
154191
m_parentOrLeaf->resume();
155-
156-
while (m_parentOrLeaf != this && m_parentOrLeaf->is_complete())
157-
{
158-
m_parentOrLeaf = m_parentOrLeaf->m_parentOrLeaf;
159-
m_parentOrLeaf->resume();
160-
}
161192
}
162193

163194
private:
164195

196+
auto handle() noexcept
197+
{
198+
return std::experimental::coroutine_handle<promise_type>::from_promise(*this);
199+
}
200+
165201
void resume() noexcept
166202
{
167-
std::experimental::coroutine_handle<promise_type>::from_promise(*this).resume();
203+
handle().resume();
168204
}
169205

170206
T* m_value;

0 commit comments

Comments
 (0)