Coroutines in Visual Studio 2015 – Update 1

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)

What to expect in VS Update 2

  • 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::system_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…\n”;

  await 1ms;

  cout << this_thread::get_id() << “: woke up\n”;

}

 

int main() {

  test().get();

  cout << this_thread::get_id() << “: back in main\n”;

}