init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
159
cmake/external/glib/cppgir/examples/co-async.hpp
vendored
Normal file
159
cmake/external/glib/cppgir/examples/co-async.hpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <gi/gi.hpp>
|
||||
|
||||
#include <coroutine>
|
||||
#include <future>
|
||||
|
||||
#ifdef CO_DEBUG
|
||||
#include <iostream>
|
||||
static auto &dout = std::cerr;
|
||||
#else
|
||||
#include <sstream>
|
||||
static std::ostringstream dout;
|
||||
#endif
|
||||
|
||||
template<typename T, typename SELF>
|
||||
struct holder
|
||||
{
|
||||
void return_value(T &&v)
|
||||
{
|
||||
dout << "return value " << std::endl;
|
||||
auto self = (SELF *)this;
|
||||
self->set_value(std::move(v));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename SELF>
|
||||
struct holder<void, SELF>
|
||||
{
|
||||
void return_void()
|
||||
{
|
||||
auto self = (SELF *)this;
|
||||
self->set_value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RESULT>
|
||||
class promise_type_t : public holder<RESULT, promise_type_t<RESULT>>
|
||||
{
|
||||
protected:
|
||||
std::promise<RESULT> result_;
|
||||
std::coroutine_handle<> waiter_;
|
||||
|
||||
public:
|
||||
struct init
|
||||
{
|
||||
std::coroutine_handle<> handle;
|
||||
std::future<RESULT> f;
|
||||
};
|
||||
|
||||
~promise_type_t() { dout << "promise destruction" << std::endl; }
|
||||
|
||||
auto get_return_object(bool refresh = false)
|
||||
{
|
||||
dout << "return obj " << std::endl;
|
||||
if (refresh)
|
||||
result_ = decltype(result_)();
|
||||
return init{std::coroutine_handle<promise_type_t>::from_promise(*this),
|
||||
result_.get_future()};
|
||||
}
|
||||
std::suspend_never initial_suspend() noexcept { return {}; }
|
||||
std::suspend_never final_suspend() noexcept { return {}; }
|
||||
|
||||
bool resume()
|
||||
{
|
||||
auto w = waiter_;
|
||||
if (w) {
|
||||
// waiter takes care of itself again
|
||||
waiter_ = nullptr;
|
||||
w.resume();
|
||||
}
|
||||
return bool(w);
|
||||
}
|
||||
|
||||
// use any dummy type to avoid reference to void below
|
||||
using arg_type = typename std::conditional<std::is_same<RESULT, void>::value,
|
||||
std::nullptr_t, RESULT>::type;
|
||||
|
||||
void set_value(arg_type &&v)
|
||||
{
|
||||
result_.set_value(std::move(v));
|
||||
resume();
|
||||
}
|
||||
|
||||
void set_value()
|
||||
{
|
||||
result_.set_value();
|
||||
resume();
|
||||
}
|
||||
|
||||
void set_waiter(std::coroutine_handle<> handle)
|
||||
{
|
||||
// a task/promise represent a coroutine function (frame)
|
||||
// it should only be waited upon by one other task
|
||||
// (rather than handed around and waited in multiple locations)
|
||||
if (waiter_)
|
||||
gi::detail::try_throw(std::logic_error("already waited upon"));
|
||||
waiter_ = handle;
|
||||
}
|
||||
|
||||
void unhandled_exception()
|
||||
{
|
||||
#if GI_CONFIG_EXCEPTIONS
|
||||
result_.set_exception(std::current_exception());
|
||||
// if no-one waiting, deliver to caller
|
||||
// the latter likely is the original caller
|
||||
// (to which we have not yet returned, so it can yet await)
|
||||
// otherwise it might end up totally lost
|
||||
if (!resume())
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename RESULT, typename P = promise_type_t<RESULT>>
|
||||
class task
|
||||
{
|
||||
public:
|
||||
using promise_type = P;
|
||||
|
||||
// only 1 actually active
|
||||
std::coroutine_handle<promise_type> coro_;
|
||||
std::unique_ptr<promise_type> p_;
|
||||
// but always this
|
||||
std::future<RESULT> result_;
|
||||
|
||||
public:
|
||||
// NOTE if coroutine exits by co_return, then handle is not useful
|
||||
// but the future should have a value
|
||||
task(typename P::init i)
|
||||
: coro_(decltype(coro_)::from_address(i.handle.address())),
|
||||
result_(std::move(i.f))
|
||||
{
|
||||
dout << "init task" << std::endl;
|
||||
}
|
||||
|
||||
task() : p_(new promise_type()) { result_ = p_->get_return_object().f; }
|
||||
|
||||
// move-only
|
||||
task(task &&other) = default;
|
||||
task &operator=(task &&other) = delete;
|
||||
|
||||
bool await_ready()
|
||||
{
|
||||
return result_.wait_for(std::chrono::seconds(0)) ==
|
||||
std::future_status::ready;
|
||||
}
|
||||
|
||||
promise_type &promise() const { return coro_ ? coro_.promise() : *p_; }
|
||||
|
||||
void await_suspend(std::coroutine_handle<> handle)
|
||||
{
|
||||
if (!coro_ && !p_)
|
||||
gi::detail::try_throw(std::logic_error("no routine to wait on"));
|
||||
promise().set_waiter(handle);
|
||||
}
|
||||
|
||||
RESULT await_resume() { return result_.get(); }
|
||||
};
|
||||
Reference in New Issue
Block a user