You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have an example of code that works with G++ 12, older Clang++ (v14.0.0 on macOS with experimental coroutines) and Clang++ in unoptimized compilation but crashes with -O2 or -O3 turned on. The crash happens when calling destroy on the handle of the suspended coroutine. The docs on cppreference.com states that when await_resume is called:
the coroutine is suspended (its coroutine state is populated with local variables and current suspension point). awaiter.await_suspend(handle) is called, where handle is the coroutine handle representing the current coroutine. Inside that function, the suspended coroutine state is observable via that handle, and it's this function's responsibility to schedule it to resume on some executor, or to be destroyed.
My code does just that. It destroys the calling subroutine and resumes another coroutine. Unfortunately, in macOS, Clang++ 14.0.2 (recent update) makes this code crash but only when -O2 or -O3 are used.
The below code is the minimum code I could build to demonstrate the problem. Note that almost any change will not be able to demonstrate the crash. For example, removing the loop in main (the part inside is called only once) will avoid the crash. The compiler does very aggressive optimization such that running the debugger does not allow to stop in several places that might be useful.
#include<iostream>
#if __has_include(<coroutine>)
#include<coroutine>namespacecrt= std;
#else
#include<experimental/coroutine>namespacecrt= std::experimental::coroutines_v1;
#endifstructTimer {
autodie_or_return(int t, bool destroy_handle=false)
{
structawaiter {
boolawait_ready() constnoexcept { returnfalse; }
voidawait_resume() constnoexcept {}
/* https://en.cppreference.com/w/cpp/language/coroutines states: * The coroutine is suspended (its coroutine state is populated with local variables and current suspension point). * awaiter.await_suspend(handle) is called, where handle is the coroutine handle representing the current coroutine. * Inside that function, the suspended coroutine state is observable via that handle, * and it's this function's responsibility to schedule it to resume on some executor, * or to be destroyed (returning false counts as scheduling)*/
crt::coroutine_handle<> await_suspend(crt::coroutine_handle<> h) noexcept
{
auto n = next;
if (destroy_handle) {
h.destroy();
}
return n;
}
crt::coroutine_handle<> next;
bool destroy_handle;
};
return awaiter{ender, destroy_handle};
}
crt::coroutine_handle<> ender = crt::noop_coroutine();
};
inlinestaticbool active{true};
struct [[nodiscard]] autodestruct_task {
structpromise_type {
~promise_type() { active = false; std::cout << "Dying" << std::endl; }
autodestruct_task get_return_object() noexcept
{
return autodestruct_task{crt::coroutine_handle<promise_type>::from_promise(*this)};
}
crt::suspend_always initial_suspend() noexcept { return {}; }
/// If the coroutine does not suspend at the end, its handle is automatically/// destroyed.
crt::suspend_never final_suspend() noexcept {return {}; }
voidreturn_void() noexcept {}
voidunhandled_exception() noexcept { std::terminate(); }
};
crt::coroutine_handle<promise_type> handle;
explicitautodestruct_task(crt::coroutine_handle<promise_type> h) noexcept : handle(h) {}
};
autodestruct_task test_death(Timer &timer) {
co_await timer.die_or_return(0,true);
}
autodestruct_task end_task() {
std::cout << "Ending" << std::endl;
co_return;
}
intmain() {
Timer timer;
timer.ender = end_task().handle;
auto tsk_handle = test_death(timer).handle;
while (active && !tsk_handle.done()) {
std::cout << "Resume" << std::endl;
tsk_handle();
}
}
The expected output:
Resume
Dying
Ending
Dying
Actual output:
Resume
Dying
**seg fault**
The text was updated successfully, but these errors were encountered:
FunMiles
changed the title
clang++ code crash when calling coroutine handle.destroy() in await suspend()
clang++ 14.0.2 compiled code crash when calling coroutine handle.destroy() in await suspend()
Apr 27, 2023
The problem remains under 14.0.3.
Changing the one line auto n = next; to thread_local auto n = next; appears to be a workaround. But without somebody in the know figuring out why there is this bug, it may be a fragile workaround.
I have an example of code that works with G++ 12, older Clang++ (v14.0.0 on macOS with experimental coroutines) and Clang++ in unoptimized compilation but crashes with -O2 or -O3 turned on. The crash happens when calling destroy on the handle of the suspended coroutine. The docs on cppreference.com states that when await_resume is called:
the coroutine is suspended (its coroutine state is populated with local variables and current suspension point). awaiter.await_suspend(handle) is called, where handle is the coroutine handle representing the current coroutine. Inside that function, the suspended coroutine state is observable via that handle, and it's this function's responsibility to schedule it to resume on some executor, or to be destroyed.
My code does just that. It destroys the calling subroutine and resumes another coroutine. Unfortunately, in macOS, Clang++ 14.0.2 (recent update) makes this code crash but only when -O2 or -O3 are used.
The below code is the minimum code I could build to demonstrate the problem. Note that almost any change will not be able to demonstrate the crash. For example, removing the loop in main (the part inside is called only once) will avoid the crash. The compiler does very aggressive optimization such that running the debugger does not allow to stop in several places that might be useful.
The expected output:
Actual output:
The text was updated successfully, but these errors were encountered: