2021-10-07 14:03:54 +00:00
|
|
|
#include <cassert>
|
|
|
|
#include <iostream>
|
|
|
|
#include <string>
|
|
|
|
#include <optional>
|
|
|
|
|
|
|
|
#include <Common/Exception.h>
|
|
|
|
#include <base/logger_useful.h>
|
|
|
|
#include <Poco/ConsoleChannel.h>
|
|
|
|
#include <Poco/Logger.h>
|
|
|
|
#include <Poco/AutoPtr.h>
|
|
|
|
|
|
|
|
#if defined(__clang__)
|
|
|
|
#include <experimental/coroutine>
|
|
|
|
|
2021-10-08 12:03:31 +00:00
|
|
|
namespace std
|
|
|
|
{
|
|
|
|
using namespace experimental::coroutines_v1;
|
|
|
|
}
|
2021-10-07 14:03:54 +00:00
|
|
|
|
|
|
|
#else
|
|
|
|
#include <coroutine>
|
2021-10-08 12:03:31 +00:00
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
|
2021-10-07 14:03:54 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
2021-10-08 18:55:09 +00:00
|
|
|
struct suspend_value // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
2021-10-08 18:55:09 +00:00
|
|
|
constexpr bool await_ready() const noexcept { return true; } // NOLINT(readability-identifier-naming)
|
|
|
|
constexpr void await_suspend(std::coroutine_handle<>) const noexcept {} // NOLINT(readability-identifier-naming)
|
|
|
|
constexpr T await_resume() const noexcept // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
std::cout << " ret " << val << std::endl;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
T val;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
2021-10-08 18:55:09 +00:00
|
|
|
struct Task
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
2021-10-08 18:55:09 +00:00
|
|
|
struct promise_type // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
2021-10-08 12:03:31 +00:00
|
|
|
using coro_handle = std::coroutine_handle<promise_type>;
|
2021-10-08 18:55:09 +00:00
|
|
|
auto get_return_object() { return coro_handle::from_promise(*this); } // NOLINT(readability-identifier-naming)
|
|
|
|
auto initial_suspend() { return std::suspend_never(); } // NOLINT(readability-identifier-naming)
|
|
|
|
auto final_suspend() noexcept { return suspend_value<T>{*r->value}; } // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
//void return_void() {}
|
2021-10-08 18:55:09 +00:00
|
|
|
void return_value(T value_) { r->value = value_; } // NOLINT(readability-identifier-naming)
|
|
|
|
void unhandled_exception() // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
DB::tryLogCurrentException("Logger");
|
2021-10-08 18:55:09 +00:00
|
|
|
r->exception = std::current_exception(); // NOLINT(bugprone-throw-keyword-missing)
|
2021-10-07 14:03:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
explicit promise_type(std::string tag_) : tag(tag_) {}
|
|
|
|
~promise_type() { std::cout << "~promise_type " << tag << std::endl; }
|
|
|
|
std::string tag;
|
|
|
|
coro_handle next;
|
2021-10-08 18:55:09 +00:00
|
|
|
Task * r = nullptr;
|
2021-10-07 14:03:54 +00:00
|
|
|
};
|
|
|
|
|
2021-10-08 12:03:31 +00:00
|
|
|
using coro_handle = std::coroutine_handle<promise_type>;
|
2021-10-07 14:03:54 +00:00
|
|
|
|
2021-10-08 18:55:09 +00:00
|
|
|
bool await_ready() const noexcept { return false; } // NOLINT(readability-identifier-naming)
|
|
|
|
void await_suspend(coro_handle g) noexcept // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
std::cout << " await_suspend " << my.promise().tag << std::endl;
|
|
|
|
std::cout << " g tag " << g.promise().tag << std::endl;
|
|
|
|
g.promise().next = my;
|
|
|
|
}
|
2021-10-08 18:55:09 +00:00
|
|
|
T await_resume() noexcept // NOLINT(readability-identifier-naming)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
std::cout << " await_res " << my.promise().tag << std::endl;
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
|
2021-10-09 06:00:51 +00:00
|
|
|
Task(coro_handle handle) : my(handle), tag(handle.promise().tag) // NOLINT(google-explicit-constructor)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
assert(handle);
|
|
|
|
my.promise().r = this;
|
2021-10-08 18:55:09 +00:00
|
|
|
std::cout << " Task " << tag << std::endl;
|
2021-10-07 14:03:54 +00:00
|
|
|
}
|
2021-10-08 18:55:09 +00:00
|
|
|
Task(Task &) = delete;
|
2022-02-25 19:04:48 +00:00
|
|
|
Task(Task &&rhs) noexcept : my(rhs.my), tag(rhs.tag)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
rhs.my = {};
|
2021-10-08 18:55:09 +00:00
|
|
|
std::cout << " Task&& " << tag << std::endl;
|
2021-10-07 14:03:54 +00:00
|
|
|
}
|
2021-10-08 18:55:09 +00:00
|
|
|
static bool resumeImpl(Task *r)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
if (r->value)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto & next = r->my.promise().next;
|
|
|
|
|
|
|
|
if (next)
|
|
|
|
{
|
2021-10-08 18:55:09 +00:00
|
|
|
if (resumeImpl(next.promise().r))
|
2021-10-07 14:03:54 +00:00
|
|
|
return true;
|
|
|
|
next = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r->value)
|
|
|
|
{
|
|
|
|
r->my.resume();
|
|
|
|
if (r->exception)
|
|
|
|
std::rethrow_exception(r->exception);
|
|
|
|
}
|
|
|
|
return !r->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool resume()
|
|
|
|
{
|
2021-10-08 18:55:09 +00:00
|
|
|
return resumeImpl(this);
|
2021-10-07 14:03:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
T res()
|
|
|
|
{
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
|
2021-10-08 18:55:09 +00:00
|
|
|
~Task()
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
2021-10-08 18:55:09 +00:00
|
|
|
std::cout << " ~Task " << tag << std::endl;
|
2021-10-07 14:03:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
coro_handle my;
|
|
|
|
std::string tag;
|
|
|
|
std::optional<T> value;
|
|
|
|
std::exception_ptr exception;
|
|
|
|
};
|
|
|
|
|
2021-10-08 18:55:09 +00:00
|
|
|
Task<int> boo([[maybe_unused]] std::string tag)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
std::cout << "x" << std::endl;
|
2021-10-08 12:03:31 +00:00
|
|
|
co_await std::suspend_always();
|
2021-10-07 14:03:54 +00:00
|
|
|
std::cout << StackTrace().toString();
|
|
|
|
std::cout << "y" << std::endl;
|
|
|
|
co_return 1;
|
|
|
|
}
|
|
|
|
|
2021-10-08 18:55:09 +00:00
|
|
|
Task<int> bar([[maybe_unused]] std::string tag)
|
2021-10-07 14:03:54 +00:00
|
|
|
{
|
|
|
|
std::cout << "a" << std::endl;
|
|
|
|
int res1 = co_await boo("boo1");
|
|
|
|
std::cout << "b " << res1 << std::endl;
|
|
|
|
int res2 = co_await boo("boo2");
|
|
|
|
if (res2 == 1)
|
|
|
|
throw DB::Exception(1, "hello");
|
|
|
|
std::cout << "c " << res2 << std::endl;
|
|
|
|
co_return res1 + res2; // 1 + 1 = 2
|
|
|
|
}
|
|
|
|
|
2021-10-08 19:44:08 +00:00
|
|
|
Task<int> foo([[maybe_unused]] std::string tag)
|
|
|
|
{
|
2021-10-07 14:03:54 +00:00
|
|
|
std::cout << "Hello" << std::endl;
|
|
|
|
auto res1 = co_await bar("bar1");
|
|
|
|
std::cout << "Coro " << res1 << std::endl;
|
|
|
|
auto res2 = co_await bar("bar2");
|
|
|
|
std::cout << "World " << res2 << std::endl;
|
|
|
|
co_return res1 * res2; // 2 * 2 = 4
|
|
|
|
}
|
|
|
|
|
|
|
|
int main()
|
|
|
|
{
|
|
|
|
Poco::AutoPtr<Poco::ConsoleChannel> app_channel(new Poco::ConsoleChannel(std::cerr));
|
|
|
|
Poco::Logger::root().setChannel(app_channel);
|
|
|
|
Poco::Logger::root().setLevel("trace");
|
|
|
|
|
|
|
|
LOG_INFO(&Poco::Logger::get(""), "Starting");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto t = foo("foo");
|
|
|
|
std::cout << ".. started" << std::endl;
|
|
|
|
while (t.resume())
|
|
|
|
std::cout << ".. yielded" << std::endl;
|
|
|
|
std::cout << ".. done: " << t.res() << std::endl;
|
|
|
|
}
|
|
|
|
catch (DB::Exception & e)
|
|
|
|
{
|
|
|
|
std::cout << "Got exception " << e.what() << std::endl;
|
|
|
|
std::cout << e.getStackTraceString() << std::endl;
|
|
|
|
}
|
|
|
|
}
|