Skip to content

Commit 419c33a

Browse files
authored
Reduce stack consumption if unable to switch to apartment_context (#1276)
1 parent 4363e5c commit 419c33a

File tree

3 files changed

+12
-76
lines changed

3 files changed

+12
-76
lines changed

strings/base_coroutine_foundation.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ namespace winrt::impl
169169
}
170170

171171
template <typename T>
172-
auto await_suspend(coroutine_handle<T> handle)
172+
bool await_suspend(coroutine_handle<T> handle)
173173
{
174174
this->set_cancellable_promise_from_handle(handle);
175175
return register_completed_callback(handle);
@@ -183,17 +183,10 @@ namespace winrt::impl
183183
}
184184

185185
private:
186-
auto register_completed_callback(coroutine_handle<> handle)
186+
bool register_completed_callback(coroutine_handle<> handle)
187187
{
188188
async.Completed(disconnect_aware_handler(this, handle));
189-
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
190-
if (!suspending.exchange(false, std::memory_order_acquire))
191-
{
192-
handle.resume();
193-
}
194-
#else
195189
return suspending.exchange(false, std::memory_order_acquire);
196-
#endif
197190
}
198191

199192
static fire_and_forget cancel_asynchronously(Async async)

strings/base_coroutine_threadpool.h

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ namespace winrt::impl
7777
return 0;
7878
};
7979

80-
inline void resume_apartment_sync(com_ptr<IContextCallback> const& context, coroutine_handle<> handle, int32_t* failure)
80+
[[nodiscard]] inline bool resume_apartment_sync(com_ptr<IContextCallback> const& context, coroutine_handle<> handle, int32_t* failure)
8181
{
8282
com_callback_args args{};
8383
args.data = handle.address();
@@ -87,8 +87,9 @@ namespace winrt::impl
8787
{
8888
// Resume the coroutine on the wrong apartment, but tell it why.
8989
*failure = result;
90-
handle();
90+
return false;
9191
}
92+
return true;
9293
}
9394

9495
struct threadpool_resume
@@ -103,7 +104,10 @@ namespace winrt::impl
103104
inline void __stdcall fallback_submit_threadpool_callback(void*, void* p) noexcept
104105
{
105106
std::unique_ptr<threadpool_resume> state{ static_cast<threadpool_resume*>(p) };
106-
resume_apartment_sync(state->m_context, state->m_handle, state->m_failure);
107+
if (!resume_apartment_sync(state->m_context, state->m_handle, state->m_failure))
108+
{
109+
state->m_handle.resume();
110+
}
107111
}
108112

109113
inline void resume_apartment_on_threadpool(com_ptr<IContextCallback> const& context, coroutine_handle<> handle, int32_t* failure)
@@ -113,7 +117,7 @@ namespace winrt::impl
113117
state.release();
114118
}
115119

116-
inline auto resume_apartment(resume_apartment_context const& context, coroutine_handle<> handle, int32_t* failure)
120+
[[nodiscard]] inline auto resume_apartment(resume_apartment_context const& context, coroutine_handle<> handle, int32_t* failure)
117121
{
118122
WINRT_ASSERT(context.valid());
119123
if ((context.m_context == nullptr) || (context.m_context == try_capture<IContextCallback>(WINRT_IMPL_CoGetObjectContext)))
@@ -132,8 +136,7 @@ namespace winrt::impl
132136
}
133137
else
134138
{
135-
resume_apartment_sync(context.m_context, handle, failure);
136-
return true;
139+
return resume_apartment_sync(context.m_context, handle, failure);
137140
}
138141
}
139142
}
@@ -331,17 +334,10 @@ namespace winrt::impl
331334
check_hresult(failure);
332335
}
333336

334-
auto await_suspend(impl::coroutine_handle<> handle)
337+
bool await_suspend(impl::coroutine_handle<> handle)
335338
{
336339
auto context_copy = context;
337-
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
338-
if (!impl::resume_apartment(context_copy.context, handle, &failure))
339-
{
340-
handle.resume();
341-
}
342-
#else
343340
return impl::resume_apartment(context_copy.context, handle, &failure);
344-
#endif
345341
}
346342
};
347343

test/test/await_completed.cpp

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -27,46 +27,12 @@ namespace
2727
}
2828
#endif
2929

30-
// Simple awaiter that (inefficiently) resumes from inside a function nested in
31-
// await_suspend, for the purpose of measuring how much stack it consumes.
32-
// This is the best we can do with MSVC prerelease coroutines prior to 16.11.
33-
// This simulates the behavior of await_adapter.
34-
struct resume_sync_from_await_suspend
35-
{
36-
bool await_ready() { return false; }
37-
template <typename T>
38-
void await_suspend(winrt::impl::coroutine_handle<T> h) { resume_inner(h); }
39-
void await_resume() { }
40-
41-
private:
42-
void resume_inner(winrt::impl::coroutine_handle<> h) { h(); }
43-
};
44-
4530
IAsyncAction SyncCompletion()
4631
{
4732
uintptr_t initial = approximate_stack_pointer();
48-
co_await resume_sync_from_await_suspend();
49-
uintptr_t sync_usage = initial - approximate_stack_pointer();
50-
51-
initial = approximate_stack_pointer();
5233
co_await AlreadyCompleted();
5334
uintptr_t consumed = initial - approximate_stack_pointer();
54-
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
55-
// This branch is taken only for MSVC prerelease coroutines.
56-
//
57-
// MSVC prerelease coroutines prior to 16.11 do not implement "bool await_suspend" reliably,
58-
// so we can't use it impl::await_adapter. We must resume inline inside await_suspend,
59-
// so there is a small amount of stack usage. (Pre-16.11 and post-16.11 prerelease coroutines
60-
// are interoperable, so we cannot change behavior based on which compiler we are using,
61-
// because that would introduce ODR violations. Our first opportunity to change behavior
62-
// is the ABI breaking change with MSVC standard-conforming coroutines.)
63-
REQUIRE(consumed <= sync_usage);
64-
#else
65-
(void)sync_usage;
66-
// MSVC standard-conforming coroutines (as well as gcc and clang coroutines)
67-
// support "bool await_suspend" just fine.
6835
REQUIRE(consumed == 0);
69-
#endif
7036
}
7137

7238
// co_await the same apartment context and confirm that stack does not grow.
@@ -77,28 +43,9 @@ namespace
7743
winrt::apartment_context same_context;
7844

7945
uintptr_t initial = approximate_stack_pointer();
80-
co_await resume_sync_from_await_suspend();
81-
uintptr_t sync_usage = initial - approximate_stack_pointer();
82-
83-
initial = approximate_stack_pointer();
8446
co_await same_context;
8547
uintptr_t consumed = initial - approximate_stack_pointer();
86-
87-
#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED
88-
// This branch is taken only for MSVC prerelease coroutines.
89-
//
90-
// MSVC prerelease coroutines prior to 16.11 do not implement "bool await_suspend" reliably,
91-
// so we can't use it impl::apartment_awaiter. We must resume inline inside await_suspend,
92-
// so there is a small amount of stack usage. (Pre-16.11 and post-16.11 prerelease coroutines
93-
// are interoperable, so we cannot change behavior based on which compiler we are using,
94-
// because that would introduce ODR violations. Our first opportunity to change behavior
95-
// is the ABI breaking change with MSVC standard-conforming coroutines.)
96-
REQUIRE(consumed <= sync_usage);
97-
#else
98-
// MSVC standard-conforming coroutines (as well as gcc and clang coroutines)
99-
// support "bool await_suspend" just fine.
10048
REQUIRE(consumed == 0);
101-
#endif
10249
}
10350
}
10451
TEST_CASE("await_completed_await")

0 commit comments

Comments
 (0)