Is it required to resume co-routine from the calling function in C++?

880 views Asked by At

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 ?

2

There are 2 answers

0
Davis Herring On

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_awaited, 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.

0
Nicol Bolas On

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_returned 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.