Skip to content

Instantly share code, notes, and snippets.

@nokotan
Created January 20, 2021 07:20
Show Gist options
  • Save nokotan/f2b3585c0fb6314afea00c1856ff46d7 to your computer and use it in GitHub Desktop.
Save nokotan/f2b3585c0fb6314afea00c1856ff46d7 to your computer and use it in GitHub Desktop.
std::future をコルーチンで待ってみる on emscripten
# include <future>
# include <chrono>
# include <iostream>
# include <experimental/coroutine>
# include <emscripten.h>
# include <emscripten/html5.h>
namespace std
{
using namespace std::experimental;
}
using namespace std::literals::chrono_literals;
struct task
{
struct promise_type;
using handle = std::coroutine_handle<promise_type>;
struct promise_type
{
auto get_return_object() { return task{*this}; }
auto initial_suspend() { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
template <class T>
auto await_transform(std::future<T>&& fut)
{
struct awaiter
{
bool await_ready() const
{
return fut.wait_for(0ms) == std::future_status::ready;
}
void await_suspend(handle h)
{
struct await_callback
{
const std::chrono::milliseconds polling_interval = 20ms;
handle co;
const awaiter& waiter;
long intervalID;
await_callback(handle _co, const awaiter& _waiter) :
co(_co),
waiter(_waiter)
{
intervalID = ::emscripten_set_interval(&awake_and_delete, polling_interval.count(), this);
}
static void awake_and_delete(void* userData)
{
auto that = static_cast<await_callback*>(userData);
if (that->waiter.await_ready())
{
::emscripten_clear_interval(that->intervalID);
that->co.resume();
delete that;
}
}
};
new await_callback(h, *this);
}
T await_resume() { return fut.get(); }
awaiter(std::future<T>&& f) : fut(std::move(f)) {}
private:
std::future<T> fut;
};
return awaiter{std::move(fut)};
}
};
task(task const&) = delete;
task(task&& rhs) : coro(std::exchange(rhs.coro, nullptr)) {}
private:
explicit task(promise_type& p) : coro(handle::from_promise(p)) {}
handle coro;
};
void callback(void* userData)
{
auto promise = static_cast<std::promise<int>*>(userData);
promise->set_value(2021);
delete promise;
}
std::future<int> delayed_get(std::chrono::milliseconds ms)
{
auto promise = new std::promise<int>();
auto future = promise->get_future();
::emscripten_set_timeout(&callback, ms.count(), promise);
return std::move(future);
}
task f()
{
using namespace std;
cout << co_await delayed_get(5s) << endl;
}
int main()
{
using namespace std;
auto g = f();
cout << "Start Coroutine!" << endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment