AutoCatGnome/coro/Task.h
2022-02-23 15:14:13 +03:00

147 lines
4.1 KiB
C++

//
// Created by selim on 03.01.2022.
//
#ifndef AUTOCAT_GNOME_TASK_H
#define AUTOCAT_GNOME_TASK_H
#include <coroutine>
#include <iostream>
#include <optional>
#include <exception>
#include <boost/thread/future.hpp>
#include <boost/exception/errinfo_nested_exception.hpp>
#include <utility>
#include <glibmm.h>
class FutureException: public virtual std::exception, public virtual boost::exception {
private:
std::string _message;
public:
explicit FutureException(const char* msg): _message(msg) {}
explicit FutureException(std::string msg): _message(std::move(msg)) {}
const char* what() const noexcept {
return _message.c_str();
}
};
// Enable the use of std::future<T> as a coroutine type
// by using a std::promise<T> as the promise type.
template <typename T, typename... Args>
requires(!std::is_void_v<T> && !std::is_reference_v<T>)
struct std::coroutine_traits<boost::future<T>, Args...> {
struct promise_type : boost::promise<T> {
boost::future<T> get_return_object() noexcept {
return this->get_future();
}
std::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
return {};
}
void return_value(const T &value)
noexcept(std::is_nothrow_copy_constructible_v<T>) {
this->set_value(value);
}
void return_value(T &&value)
noexcept(std::is_nothrow_move_constructible_v<T>) {
this->set_value(std::move(value));
}
void unhandled_exception() noexcept {
this->set_exception(boost::current_exception());
}
};
};
// Same for std::future<void>.
template <typename... Args>
struct std::coroutine_traits<boost::future<void>, Args...> {
struct promise_type : boost::promise<void> {
boost::future<void> get_return_object() noexcept {
return this->get_future();
}
std::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
return {};
}
void return_void() noexcept {
this->set_value();
}
void unhandled_exception() noexcept {
this->set_exception(boost::current_exception());
}
};
};
template<typename... Args>
struct std::coroutine_traits<void, Args...> {
struct promise_type {
void get_return_object() noexcept {}
std::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
return {};
}
void return_void() noexcept {}
void unhandled_exception() noexcept {
try {
boost::rethrow_exception(boost::current_exception());
} catch (std::exception& ex) {
std::cout << "Unhandled exception (in void coroutine) detected: " << ex.what() << std::endl;
}
}
};
};
template <typename T>
auto operator co_await(boost::future<T> future) noexcept requires(!std::is_reference_v<T>) {
struct awaiter : public boost::future<T> {
bool await_ready() const noexcept {
return this->is_ready();
}
void await_suspend(std::coroutine_handle<> cont) {
this->then([this, cont](boost::future<T>&& fut){
// future was moved out of awaiter to this lambda
// so, we need to bring it back
this->swap(fut);
// Resume coroutine on the main thread (i.e. GMainContext)
g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, awaiter::resumeContinuation, (gpointer)&cont, nullptr);
});
}
T await_resume() {
return this->get();
}
static int resumeContinuation(void* data) {
auto handler = reinterpret_cast<std::coroutine_handle<>*>(data);
if(*handler) {
handler->resume();
}
return G_SOURCE_REMOVE;
}
};
return awaiter{std::move(future)};
}
#endif //AUTOCAT_GNOME_TASK_H