147 lines
4.1 KiB
C++
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
|