Coroutines in Visual Studio 2015 – Update 1

Eric

November 30th, 2015

In preview of Visual Studio 2015, we introduced Coroutines for C++, see these blog post for an introduction and here.

You can also look at the CPPCon 2015 talk about C++ Coroutines here.

We continue to work on resumable functions, here it is a brief update on coroutines status in VS 2015 Update 1. See the Visual Studio 2015 Update1 post here.

Some limitations are gone:

Now supported on ARM, x86 and amd64

Now you can use exceptions in a coroutine

Now you can use return statement before await or yield in a coroutine

Now you can use coroutines with /ZI (Edit and Continue Debugging)

Some stayed:

Still not compatible with /sdl and /RTCx flags (should fix in VS Update 2)

We will give incorrect /W4 warnings about variables being unused or uninitialized in the coroutines

Design changes tracking the latest coroutine proposal (P0057):

Initial_suspend/final_suspend/yield_value must return awaitable

Allocation customization is done by overloading operator new of the promise rather than providing an allocator object

Await customization via operator await

yield is now expression, not a statement

(see P0054 for more details)

Removal of the limitations

Adding coroutine specific optimizations

await_transform customizaiton point (see P0054)

Adding the Kona 2015 keywords: co_await, co_yield and co_return.

References

Bonus

Use operator await to define how to await on std::chrono::duration that goes straight to Win32 threadpool APIs.

#include <windows.h>

#include <future>

#include <iostream>

auto operator await(std::chrono:: sys tem_clock :: duration duration ) {

class awaiter {

static void CALLBACK TimerCallback( PTP_CALLBACK_INSTANCE , void * Context , PTP_TIMER ) {

std::experimental:: coroutine_handle <>::from_address( Context ) () ;

}

PTP_TIMER timer = nullptr ;

std::chrono:: system_clock :: duration duration;

public :

explicit awaiter(std::chrono:: system_clock :: duration d ) : duration( d ) {}

bool await_ready() const { return duration.count() <= 0; }

bool await_suspend(std::experimental:: coroutine_handle <> resume_cb ) {

int64_t relative_count = -duration.count();

timer = CreateThreadpoolTimer(TimerCallback, resume_cb .to_address(), nullptr );

SetThreadpoolTimer(timer, ( PFILETIME )&relative_count, 0, 0);

return timer != 0;

}

void await_resume() {}

~awaiter() { if (timer) CloseThreadpoolTimer(timer); }

};

return awaiter { duration };

}

using namespace std;

using namespace std::chrono;

future < void > test() {

cout << this_thread::get_id() << “: sleeping…

” ;

await 1ms;

cout << this_thread::get_id() << “: woke up

” ;

}

int main() {

test().get();

cout << this_thread::get_id() << “: back in main

” ;