Reputation: 73
I had a question regarding the working of co_await in C++. I have the following code snippet:-
// Downloads url to cache and
// returns cache file path.
future<path> cacheUrl(string url)
{
cout << "Downloading url.";
string text = co_await downloadAsync(url); // suspend coroutine
cout << "Saving in cache.";
path p = randomFileName();
co_await saveInCacheAsync(p, text); // suspend coroutine
co_return p;
}
int main(void) {
future<path> filePath = cacheUrl("https://localhost:808/");
return 0;
}
The co_await
keyword is used to suspend the execution of any co-routine. We have 2 instances in the above code where it is used. In the main function, we get access to the co-routine. When the program executes the line co_await downloadAsync(url)
will it invoke downloadAsync
or just suspend the co-routine.
Also, for executing the next saveInCacheAsync(p, text)
function, should the main function call resume on the co-routine ? Or will it get called automatically ?
Upvotes: 1
Views: 1020
Reputation: 40013
The coroutine model in C++ is opaque: the caller of a coroutine sees it as an ordinary function call that synchronously returns a value of the declared type (here, future<path>
). That value is but a placeholder, though: the function body executes only when that result is awaited—but not necessarily co_await
ed, since the caller need not be a coroutine (the opacity again).
Separately, co_await
may suspend a coroutine, but need not do so (consider that it might be “waiting” on a coroutine with an empty function body). It’s also quite separate from calling the coroutine: one may write
auto cr=coroutine(…);
do_useful_work();
co_await cr;
to create the placeholder long before using it.
Upvotes: 2
Reputation: 473966
co_await
in C++ is an operator, just like prefix *
or whatever. If you saw *downloadAsync(...)
, you would expect the function call to happen, then the *
operator would act on the value returned by that function. So too with co_await
.
The objects that downloadAsync
and saveInCacheAsync
return are expected to have some mechanism in them to determine when and where to continue the execution of the coroutine once their asynchronous processes have concluded. The co_await
expression (potentially) suspends execution of the coroutine and then accesses those mechanisms, scheduling the resumption of the coroutine's execution with that mechanism.
The future object return value defined by your coroutine function is meant to be able to shepherd the co_return
ed value from your function to whomever asks for it. How that works depends entirely on how you wrote your promise/future machinery for your coroutine.
The typical way to handle it is to be able to block the thread who asks for the value (eg. with a mutex
) until the asynchronous process computing that value has completed. But it could do something else. Indeed, being able to co_await
on such things, and thereby form long-chains of asynchronous continuations to build more complex values, is a common part of most coroutine machinery.
But at some point, someone has to actually retrieve the value resulting from all of this.
Upvotes: 1