Date: Thu, 14 May 2020 13:57:32 +0300
Coroutines capture parameters passed by value and copy them to the
coroutine frame (all quotes from cppreference):
> When a coroutine begins execution, it performs the following:
>
> allocates the coroutine state object using operator new (see below)
> copies all function parameters to the coroutine state: by-value
parameters are moved or copied, by-reference parameters remain
references (and so may become dangling if the coroutine is resumed after
the lifetime of referred object ends)
>
This allows a coroutine's parameters to outlive the point it is launched at:
void foo() {
call_some_coroutine(a_temporary_value());
}
Normally, a_temporary_value() would e destroyed at the end of the
expression, which can be before the coroutine completes.
With a lambda coroutine, we have a problem. This is because lambdas are
captured by reference:
> If the coroutine is a non-static member function, such as task<void>
my_class::method1(int x) const;, its Promise type is
std::coroutine_traits<task<void>, const my_class&, int>::promise_type
For a lambda, the non-static member function is
synthetic_lambda_type::operator() const, and so the lambda would be
captured as const synthetic_lambda_type&. Consider this call:
int var;
void foo() {
[i = 3] () -> future<> {
co_await delay(1s);
var = i;
}();
}
Because the lambda is captured by reference, the read of 'i' refers to a
freed stack frame.
I consider this is a serious defect. It can be fixed by changing how
"this" parameters to member functions are captured:
- if the object is an rvalue[1], then it is captured by value (and the
promise type is std::coroutine_traits<task<void>, const my_class,
int>::promise_type
- if the object is a glvalue, then it is captured by reference (and
the promise type is std::coroutine_traits<task<void>, const my_class&,
int>::promise_type
I filed a bug[2] against gcc regarding this, but I now believe gcc to be
innocent.
[1] I'm not up to speed on C++ value categories so I'm not sure if this
is a prvalue, xvalue, rvalue, or something else.
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95111
coroutine frame (all quotes from cppreference):
> When a coroutine begins execution, it performs the following:
>
> allocates the coroutine state object using operator new (see below)
> copies all function parameters to the coroutine state: by-value
parameters are moved or copied, by-reference parameters remain
references (and so may become dangling if the coroutine is resumed after
the lifetime of referred object ends)
>
This allows a coroutine's parameters to outlive the point it is launched at:
void foo() {
call_some_coroutine(a_temporary_value());
}
Normally, a_temporary_value() would e destroyed at the end of the
expression, which can be before the coroutine completes.
With a lambda coroutine, we have a problem. This is because lambdas are
captured by reference:
> If the coroutine is a non-static member function, such as task<void>
my_class::method1(int x) const;, its Promise type is
std::coroutine_traits<task<void>, const my_class&, int>::promise_type
For a lambda, the non-static member function is
synthetic_lambda_type::operator() const, and so the lambda would be
captured as const synthetic_lambda_type&. Consider this call:
int var;
void foo() {
[i = 3] () -> future<> {
co_await delay(1s);
var = i;
}();
}
Because the lambda is captured by reference, the read of 'i' refers to a
freed stack frame.
I consider this is a serious defect. It can be fixed by changing how
"this" parameters to member functions are captured:
- if the object is an rvalue[1], then it is captured by value (and the
promise type is std::coroutine_traits<task<void>, const my_class,
int>::promise_type
- if the object is a glvalue, then it is captured by reference (and
the promise type is std::coroutine_traits<task<void>, const my_class&,
int>::promise_type
I filed a bug[2] against gcc regarding this, but I now believe gcc to be
innocent.
[1] I'm not up to speed on C++ value categories so I'm not sure if this
is a prvalue, xvalue, rvalue, or something else.
[2] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95111
Received on 2020-05-14 06:00:38