Compare commits

...

3 Commits

Author SHA1 Message Date
Nitromelon
e2e5d6d57f
Allow omitting template paremeter in execCommandSync. (#1764) 2023-08-31 19:19:58 +08:00
Nitromelon
53c84305b2
Simplify coroutine implementation (#1762) 2023-08-31 10:08:44 +08:00
an-tao
85d7c068e4 Update trantor (fix botan) 2023-08-30 19:57:51 +08:00
5 changed files with 96 additions and 189 deletions

View File

@ -24,7 +24,6 @@
#include <exception>
#include <future>
#include <mutex>
#include <list>
#include <type_traits>
#include <optional>
@ -84,6 +83,11 @@ struct is_awaitable<
template <typename T>
constexpr bool is_awaitable_v = is_awaitable<T>::value;
/**
* @struct final_awaiter
* @brief An awaiter for `Task::promise_type::final_suspend()`. Transfer
* execution back to the coroutine who is co_awaiting this Task.
*/
struct final_awaiter
{
bool await_ready() noexcept
@ -102,6 +106,52 @@ struct final_awaiter
}
};
/**
* @struct task_awaiter
* @brief Convert Task to an awaiter when it is co_awaited.
* Following things will happen:
* 1. Suspend current coroutine
* 2. Set current coroutine as continuation of this Task
* 3. Transfer execution to the co_awaited Task
*/
template <typename Promise>
struct task_awaiter
{
using handle_type = std::coroutine_handle<Promise>;
public:
explicit task_awaiter(handle_type coro) : coro_(coro)
{
}
bool await_ready() noexcept
{
return !coro_ || coro_.done();
}
auto await_suspend(std::coroutine_handle<> handle) noexcept
{
coro_.promise().setContinuation(handle);
return coro_;
}
auto await_resume()
{
if constexpr (std::is_void_v<decltype(coro_.promise().result())>)
{
coro_.promise().result(); // throw exception if any
return;
}
else
{
return std::move(coro_.promise().result());
}
}
private:
handle_type coro_;
};
template <typename T = void>
struct [[nodiscard]] Task
{
@ -114,7 +164,7 @@ struct [[nodiscard]] Task
Task(const Task &) = delete;
Task(Task &&other)
Task(Task &&other) noexcept
{
coro_ = other.coro_;
other.coro_ = nullptr;
@ -128,7 +178,7 @@ struct [[nodiscard]] Task
Task &operator=(const Task &) = delete;
Task &operator=(Task &&other)
Task &operator=(Task &&other) noexcept
{
if (std::addressof(other) == this)
return *this;
@ -198,69 +248,9 @@ struct [[nodiscard]] Task
std::coroutine_handle<> continuation_;
};
auto operator co_await() const &noexcept
auto operator co_await() const noexcept
{
struct awaiter
{
public:
explicit awaiter(handle_type coro) : coro_(coro)
{
}
bool await_ready() noexcept
{
return !coro_ || coro_.done();
}
auto await_suspend(std::coroutine_handle<> handle) noexcept
{
coro_.promise().setContinuation(handle);
return coro_;
}
T await_resume()
{
auto &&v = coro_.promise().result();
return std::move(v);
}
private:
handle_type coro_;
};
return awaiter(coro_);
}
auto operator co_await() const &&noexcept
{
struct awaiter
{
public:
explicit awaiter(handle_type coro) : coro_(coro)
{
}
bool await_ready() noexcept
{
return !coro_ || coro_.done();
}
auto await_suspend(std::coroutine_handle<> handle) noexcept
{
coro_.promise().setContinuation(handle);
return coro_;
}
T await_resume()
{
return std::move(coro_.promise().result());
}
private:
handle_type coro_;
};
return awaiter(coro_);
return task_awaiter(coro_);
}
handle_type coro_;
@ -278,7 +268,7 @@ struct [[nodiscard]] Task<void>
Task(const Task &) = delete;
Task(Task &&other)
Task(Task &&other) noexcept
{
coro_ = other.coro_;
other.coro_ = nullptr;
@ -292,7 +282,7 @@ struct [[nodiscard]] Task<void>
Task &operator=(const Task &) = delete;
Task &operator=(Task &&other)
Task &operator=(Task &&other) noexcept
{
if (std::addressof(other) == this)
return *this;
@ -345,68 +335,9 @@ struct [[nodiscard]] Task<void>
std::coroutine_handle<> continuation_;
};
auto operator co_await() const &noexcept
auto operator co_await() const noexcept
{
struct awaiter
{
public:
explicit awaiter(handle_type coro) : coro_(coro)
{
}
bool await_ready() noexcept
{
return !coro_ || coro_.done();
}
auto await_suspend(std::coroutine_handle<> handle) noexcept
{
coro_.promise().setContinuation(handle);
return coro_;
}
auto await_resume()
{
coro_.promise().result();
}
private:
handle_type coro_;
};
return awaiter(coro_);
}
auto operator co_await() const &&noexcept
{
struct awaiter
{
public:
explicit awaiter(handle_type coro) : coro_(coro)
{
}
bool await_ready() noexcept
{
return false;
}
auto await_suspend(std::coroutine_handle<> handle) noexcept
{
coro_.promise().setContinuation(handle);
return coro_;
}
void await_resume()
{
coro_.promise().result();
}
private:
handle_type coro_;
};
return awaiter(coro_);
return task_awaiter(coro_);
}
handle_type coro_;
@ -429,7 +360,7 @@ struct AsyncTask
AsyncTask(const AsyncTask &) = delete;
AsyncTask(AsyncTask &&other)
AsyncTask(AsyncTask &&other) noexcept
{
coro_ = other.coro_;
other.coro_ = nullptr;
@ -437,7 +368,7 @@ struct AsyncTask
AsyncTask &operator=(const AsyncTask &) = delete;
AsyncTask &operator=(AsyncTask &&other)
AsyncTask &operator=(AsyncTask &&other) noexcept
{
if (std::addressof(other) == this)
return *this;
@ -449,8 +380,6 @@ struct AsyncTask
struct promise_type
{
std::coroutine_handle<> continuation_;
AsyncTask get_return_object() noexcept
{
return {std::coroutine_handle<promise_type>::from_promise(*this)};
@ -471,51 +400,12 @@ struct AsyncTask
{
}
void setContinuation(std::coroutine_handle<> handle)
std::suspend_never final_suspend() const noexcept
{
continuation_ = handle;
}
auto final_suspend() const noexcept
{
// Can't simply use suspend_never because we need symmetric transfer
struct awaiter final
{
bool await_ready() const noexcept
{
return true;
}
auto await_suspend(
std::coroutine_handle<promise_type> coro) const noexcept
{
return coro.promise().continuation_;
}
void await_resume() const noexcept
{
}
};
return awaiter{};
return {};
}
};
bool await_ready() const noexcept
{
return coro_.done();
}
void await_resume() const noexcept
{
}
auto await_suspend(std::coroutine_handle<> coroutine) noexcept
{
coro_.promise().setContinuation(coroutine);
return coro_;
}
handle_type coro_;
};

View File

@ -31,7 +31,7 @@ struct StructAwaiter : public CallbackAwaiter<std::shared_ptr<SomeStruct>>
} // namespace drogon::internal
// Workarround limitation of macros
// Workaround limitation of macros
template <typename T>
using is_int = std::is_same<T, int>;
template <typename T>
@ -69,7 +69,7 @@ DROGON_TEST(CroutineBasics)
}());
CHECK(n == 1);
// Testing that exceptions can propergate through coroutines
// Testing that exceptions can propagate through coroutines
auto throw_in_task = [TEST_CTX]() -> Task<> {
auto f = []() -> Task<> { throw std::runtime_error("test error"); };
@ -77,7 +77,7 @@ DROGON_TEST(CroutineBasics)
};
sync_wait(throw_in_task());
// Test sync_wait propergrates exception
// Test sync_wait propagates exception
auto throws = []() -> Task<> {
throw std::runtime_error("bla");
co_return;
@ -100,7 +100,7 @@ DROGON_TEST(CroutineBasics)
};
sync_wait(await_non_copyable());
// This only works because async_run tries to run the corouine as soon as
// This only works because async_run tries to run the coroutine as soon as
// possible and the coroutine does not wait
int testVar = 0;
async_run([&testVar]() -> Task<void> {

View File

@ -164,26 +164,40 @@ class DROGON_EXPORT RedisClient
std::string_view command,
Args &&...args)
{
std::shared_ptr<std::promise<T>> pro(new std::promise<T>);
std::future<T> f = pro->get_future();
return execCommandSync<std::decay_t<decltype(processFunc)>>(
std::move(processFunc), command, std::forward<Args>(args)...);
}
/**
* @brief Execute a redis command synchronously
* Return type can be deduced automatically in this version.
*/
template <typename F, typename... Args>
std::invoke_result_t<F, const RedisResult &> execCommandSync(
F &&processFunc,
std::string_view command,
Args &&...args)
{
using Ret = std::invoke_result_t<F, const RedisResult &>;
std::promise<Ret> prom;
execCommandAsync(
[process = std::move(processFunc), pro](const RedisResult &result) {
[&processFunc, &prom](const RedisResult &result) {
try
{
pro->set_value(process(result));
prom.set_value(processFunc(result));
}
catch (...)
{
pro->set_exception(std::current_exception());
prom.set_exception(std::current_exception());
}
},
[pro](const RedisException &err) {
pro->set_exception(std::make_exception_ptr(err));
[&prom](const RedisException &err) {
prom.set_exception(std::make_exception_ptr(err));
},
command,
std::forward<Args>(args)...);
return f.get();
return prom.get_future().get();
}
/**

View File

@ -168,11 +168,14 @@ DROGON_TEST(RedisTest)
SUCCESS();
}
// 12. Test omit template parameter
try
{
redisClient->execCommandSync<int>([](const RedisResult &) { return 1; },
"del %s",
"sync_key");
auto i = redisClient->execCommandSync(
[](const RedisResult &r) { return r.asInteger(); },
"del %s",
"sync_key");
MANDATE(i == 1);
}
catch (const RedisException &err)
{

@ -1 +1 @@
Subproject commit 2b6c9d62e6414fe90b456db829723814a534545e
Subproject commit 528406f0567e6ac48356eeb2b4b7b9ba06e8593e