After watching the interview with Gor Nishanov on await and coroutines, I decided to toy a bit with resumable functions N4131. I then realized that the following code printed 'false' to my second question (tested with Visual Studio 2015 RC and the online compiler (requires flag: /await
)):
#include <future>
#include <iostream>
using namespace std;
using namespace chrono;
struct Foo
{
future<void> coro(thread::id ui_thread_id)
{
cout << "Q: awaiting result in UI thread? " << boolalpha << (this_thread::get_id() == ui_thread_id) << endl;
auto intermediate_result = __await async([] {
this_thread::sleep_for(250ms);
return 42;
});
cout << "Q: received result in UI thread? " << boolalpha << (this_thread::get_id() == ui_thread_id) << endl;
result = intermediate_result; // race
}
int result = 0;
};
int main()
{
const auto ui_thread_id = this_thread::get_id();
Foo f;
auto c = f.coro(ui_thread_id);
for (int i = 0; i < 7; ++i)
{
cout << " -- event loop: " << f.result << endl;
this_thread::sleep_for(50ms);
}
c.wait();
}
My expectation resulted from the statement that "the fact that a function is implemented as resumable function is unobservable by the caller" (N4131, p.8, Resumable Function, 4th paragraph).
This seems to be an important detail and I could not find any statement in N4131 which clarifies my question.
Is it wrong to expect resumable functions actually resume their work in the original thread? If it is wrong, please try to explain why?
To answer your question: yes, it is probably wrong to expect resumable functions actually resume their work in the original thread because the proposal is underspecified with regards to that behavior (maybe on purpose, maybe not).
I think your misconception is around which function is resumable in the example, and thus which function is bound by "the fact that a function is implemented as resumable function is unobservable by the caller".
From the proposal (emphasis mine):
A function or a lambda is called resumable function or resumable lambda if a body of the function or lambda contains at least one suspend/resume point. Suspend/resume points are expressions with one or more await operators, yield statements or await-for statements
This means that the lambda isn't the resumable function, coro
is because the await
keyword is part of its body. The proposal specifies how the resumable function behaves with regards to its callers and the example shows that it is indeed unobservable by the caller, main()
in this case, whether it was implemented as resumable or not (main()
is also still on the UI thread when coro
returns).
Now, one could argue that this might be an oversight in the proposal to not specify this but I'm sure there was a good reason for it, yet I wasn't able to find anything in the proposal relating to the actual resuming of the function.
There could also be debate around the interaction with modifiable parameters. For example:
future<void> coro(thread::id ui_thread_id, int* foo)
{
auto intermediate_result = __await async([] {
this_thread::sleep_for(250ms);
});
*foo = 42;
}
I would personally consider the fact that foo
can be modified by another thread as "observable" from the caller, but then again someone could likely argue otherwise (I'm not the greatest at standard-ese).
Last but not least, I'm not sure what you consider extraordinarily race-y about the assignment to result. It does race with the reads in main()
but you wrote the code specifically for this and there's only one assignment (the body of the function is suspended at the await
call and resumed once the async lambda returns). Changing await
to resume on the same thread would not affect this situation at all (except in the case of non-atomic read-writes where you could read an object in an inconsistent state, which is another can of worms and is probably a valid point).
Note that this feature is very young and currently only experimentally implemented in MSVC IIRC so I would expect it to go through some changes before it's standardized (if it ever is). There's also a non-negligible chance that this is actually specification/implementation bug.
That being said, there's a good chance that the behavior on resuming resumable functions was barely specified to allow more freedom in the implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With