I will start with the sleep_for example from Gor’s presentation (PDF, YouTube). The schedule function is a small extension that adds a lambda that can return a value once the time arrives. The schedule function implements the Awaitable Concept on its return type awaiter via the await_ready, await_suspend and await_resume functions.

// await.cpp : Defines the entry point for the console application.
//
// In VS 2015 x64 Native prompt:
// CL.exe /Zi /nologo /W3 /sdl- /Od /D _DEBUG /D WIN32 
//   /D _CONSOLE /D _UNICODE /D UNICODE /Gm /EHsc /MDd 
//  /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP /await await.cpp
//

#include <iostream>
#include <future>

#include <chrono>
namespace t = std::chrono;
using clk = t::system_clock;
using namespace std::chrono_literals;

#include <experimental/resumable>
namespace ex = std::experimental;

#include <windows.h>
#include <threadpoolapiset.h>

// usage: await schedule(std::chrono::system_clock::now() + 1s, [](){. . .});
template<class Work>
auto schedule(clk::time_point at, Work work) {
    class awaiter {
        static void CALLBACK TimerCallback(PTP_CALLBACK_INSTANCE, void* Context, PTP_TIMER) {
            ex::resumable_handle<>::from_address(Context)();
        }
        PTP_TIMER timer = nullptr;
        std::chrono::system_clock::time_point at;
        Work work;
    public:
        awaiter(std::chrono::system_clock::time_point a, Work w)
            : at(a), work(std::move(w)) {}
        bool await_ready() const {
            return std::chrono::system_clock::now() >= at;
        }
        void await_suspend(ex::resumable_handle<> resume_cb) {
            auto duration = at - std::chrono::system_clock::now();
            int64_t relative_count = -duration.count();
            timer = CreateThreadpoolTimer(TimerCallback, resume_cb.to_address(), nullptr);
            if (timer == 0)
                throw std::system_error(GetLastError(), std::system_category());
            SetThreadpoolTimer(timer, (PFILETIME)&relative_count, 0, 0);
        }
        auto await_resume() {
            return work();
        }
        ~awaiter() {
            if (timer) CloseThreadpoolTimer(timer);
        }
    };
    return awaiter{ at, work };
}

template<class... T>
void outln(T... t) {
    std::cout << std::this_thread::get_id();
    int seq[] = {(std::cout << t, 0)...};
    std::cout << std::endl;
}

std::future<int> schedule_test() {
    outln(" - schedule_test ");
    auto answer = __await schedule(clk::now() + 1s, []() {
        outln(" - schedule_test - lambda");
        return 42;
    });
    outln(" - schedule_test - answer = ", answer);
    return answer;
}

int wmain() {
    try {
        outln(" - main ");
        auto pending = schedule_test();
        outln(" - main - returned");
        int answer = pending.get();
        outln(" - main - answer = ", answer);
    }
    catch (const std::exception& e) {
        outln("schedule_test exception ", e.what());
    }
}

diagram

PlantUML SVG diagram

The schedule_test() function uses await and return to stitch the std::future<T> return type to the awaiter returned by schedule(). The compiler creates resumable_traits<std::future<int>>::promise_type, which is std::promise<int>, gets the future<int> from the promise, calls awaiter::await_ready and awaiter::await_suspend and returns the empty future. Once the TimerCallback resumes the await the compiler calls awaiter::await_resume to get the result of the lambda and then, when return is reached, std::promise<int>::set_result().

The call to schedule_test() in wmain() returns a std::future<T> and the .get() call blocks until the answer is available. Since wmain() can only return void or int await cannot be used.

output

  8068 - main
  8068 - schedule_test
  8068 - main - returned
  4324 - schedule_test - lambda
  4324 - schedule_test - answer = 42
  8068 - main - answer = 42

Notice that schedule_test() is running on two different threads because it returned after calling CreateThreadpoolTimer() and then was resumed by TimerCallback(). The complexity that the compiler is hiding makes the code much cleaner, but does not allow the user to ignore threads. For example, adding a std::unique_lock<std::mutex> to schedule_test() would lock in 8068 and unlock in 4324.

Libraries that provide safe and efficient algorithms that compose with the await proposal are needed as much as the algorithms in the existing STL and the proposed RangeV3 libraries. I will show some options for these in subsequent posts.

try it out

After installing Visual Studio 2015 Preview clone await and open the await solution. This code is in the await project in the solution. Currently the implementation requires that RTC be disabled and /await for the compiler and x64 machine target for the linker, which this solution has configured correctly.