-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 812a865
Showing
24 changed files
with
777 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
BasedOnStyle: Google | ||
|
||
IndentWidth: 2 | ||
ColumnLimit: 120 | ||
|
||
SpaceAfterTemplateKeyword: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
cmake_minimum_required(VERSION 3.10) | ||
project(asyncpp) | ||
|
||
option(BUILD_EXAMPLES "Build examples" OFF) | ||
|
||
add_subdirectory(asyncpp) | ||
add_subdirectory(asyncpp_uv) | ||
add_subdirectory(asyncpp_uv_curl) | ||
|
||
if(BUILD_EXAMPLES) | ||
add_subdirectory(examples) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
asynccpp | ||
===== | ||
|
||
C++20 event-loop agnostic coroutines (`co_await`/`co_return`) implementation + support for libuv and curl. | ||
|
||
## Modules | ||
|
||
### asyncpp | ||
|
||
Event loop-agnostic implementation of C++20 coroutines (`co_await`/`co_return`). | ||
|
||
Provides `makeTask` function which takes a callback with `resolve` and `reject` passed as arguments. Similar to how JavaScript `new Promise` constructor works. | ||
|
||
### asyncpp_uv | ||
|
||
libuv library wrappers to be used in coroutine-based program. | ||
|
||
Implemented functions: | ||
|
||
* `uvSleep` - waits passed amount of time before continuing. | ||
|
||
### asyncpp_uv_curl | ||
|
||
libuv based, coroutine API for curl. | ||
|
||
## Example | ||
|
||
```c++ | ||
#include <asyncpp_uv/asyncpp_uv_sleep.h> | ||
#include <uv.h> | ||
|
||
asyncpp::task<void> asyncMain() { | ||
printf("asyncMain BEGIN\n"); | ||
auto timer1 = asyncpp_uv::uvSleep(1000); | ||
auto timer2 = asyncpp_uv::uvSleep(1000); | ||
co_await timer1; | ||
co_await timer2; | ||
printf("asyncMain END\n"); | ||
} | ||
|
||
int main() { | ||
asyncMain(); | ||
uv_run(uv_default_loop(), UV_RUN_DEFAULT); | ||
return 0; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
add_library(asyncpp STATIC src/asyncpp.cpp) | ||
target_include_directories(asyncpp PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) | ||
target_compile_options(asyncpp PUBLIC -fcoroutines) | ||
set_property(TARGET asyncpp PROPERTY CXX_STANDARD 20) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#include "asyncpp_base.h" | ||
#include "asyncpp_make_task.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/* | ||
* Based on https://github.com/jimspr/awaituv | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <cassert> | ||
#include <coroutine> | ||
#include <functional> | ||
#include <memory> | ||
|
||
namespace asyncpp { | ||
enum struct future_error { | ||
not_ready, // get_value called when value not available | ||
}; | ||
|
||
struct future_exception : std::exception { | ||
future_error _error; | ||
explicit future_exception(future_error fe) : _error(fe) {} | ||
}; | ||
|
||
struct awaitable_state_base { | ||
std::function<void(void)> _coro; | ||
bool _ready = false; | ||
|
||
awaitable_state_base() = default; | ||
awaitable_state_base(awaitable_state_base&&) = delete; | ||
awaitable_state_base(const awaitable_state_base&) = delete; | ||
|
||
void set_coroutine_callback(const std::function<void(void)>& cb) { | ||
// Test to make sure nothing else is waiting on this future. | ||
assert(((cb == nullptr) || (_coro == nullptr)) && "This future is already being awaited."); | ||
_coro = cb; | ||
} | ||
|
||
void set_value() { | ||
// Set all members first as calling coroutine may reset stuff here. | ||
_ready = true; | ||
auto coro = _coro; | ||
_coro = nullptr; | ||
if (coro != nullptr) coro(); | ||
} | ||
|
||
[[nodiscard]] bool ready() const { return _ready; } | ||
}; | ||
|
||
template<typename TValue> | ||
struct awaitable_state : public awaitable_state_base { | ||
TValue _value; | ||
|
||
void set_value(TValue&& t) { | ||
_value = std::move(t); | ||
awaitable_state_base::set_value(); | ||
} | ||
|
||
TValue&& get_value() { | ||
if (!_ready) throw future_exception(future_error::not_ready); | ||
return std::move(_value); | ||
} | ||
}; | ||
|
||
// specialization of awaitable_state<void> | ||
template<> | ||
struct awaitable_state<void> : public awaitable_state_base { | ||
void get_value() const { | ||
if (!_ready) throw future_exception(future_error::not_ready); | ||
} | ||
}; | ||
|
||
template<typename T> | ||
struct promise_t; | ||
|
||
template<typename T> | ||
struct task { | ||
// promise_type declaration required by C++20 coroutines | ||
typedef promise_t<T> promise_type; | ||
|
||
std::shared_ptr<awaitable_state<T>> _state; | ||
std::shared_ptr<void> _customState; | ||
|
||
task() = default; | ||
|
||
explicit task(std::shared_ptr<awaitable_state<T>>& state) : _state(state) {} | ||
|
||
task(std::shared_ptr<awaitable_state<T>>& state, std::shared_ptr<void> custom_state) | ||
: _state(state), _customState(std::move(custom_state)) {} | ||
|
||
// not copyable | ||
task(const task&) = delete; | ||
task& operator=(const task&) = delete; | ||
// movable | ||
task(task&&) noexcept = default; | ||
task& operator=(task&&) noexcept = default; | ||
|
||
T await_resume() const { return std::move(_state->get_value()); } | ||
|
||
[[nodiscard]] bool await_ready() const { return _state->_ready; } | ||
|
||
void await_suspend(std::coroutine_handle<> resume_cb) { _state->set_coroutine_callback(resume_cb); } | ||
}; | ||
|
||
template<> | ||
struct task<void> { | ||
// promise_type declaration required by C++20 coroutines | ||
typedef promise_t<void> promise_type; | ||
|
||
std::shared_ptr<awaitable_state<void>> _state; | ||
std::shared_ptr<void> _customState; | ||
|
||
task() = default; | ||
|
||
explicit task(std::shared_ptr<awaitable_state<void>>& state) : _state(state) {} | ||
|
||
task(std::shared_ptr<awaitable_state<void>>& state, std::shared_ptr<void> custom_state) | ||
: _state(state), _customState(std::move(custom_state)) {} | ||
|
||
// not copyable | ||
task(const task&) = delete; | ||
task& operator=(const task&) = delete; | ||
// movable | ||
task(task&&) noexcept = default; | ||
task& operator=(task&&) noexcept = default; | ||
|
||
void await_resume() const {} | ||
|
||
[[nodiscard]] bool await_ready() const { return _state->_ready; } | ||
|
||
void await_suspend(std::coroutine_handle<> resume_cb) { _state->set_coroutine_callback(resume_cb); } | ||
}; | ||
|
||
template<typename T> | ||
struct promise_t { | ||
std::shared_ptr<awaitable_state<T>> state; | ||
|
||
promise_t() : state(std::make_shared<awaitable_state<T>>()) {} | ||
|
||
// not copyable | ||
promise_t(const promise_t&) = delete; | ||
promise_t& operator=(const promise_t&) = delete; | ||
// movable | ||
promise_t(promise_t&&) noexcept = default; | ||
promise_t& operator=(promise_t&&) noexcept = default; | ||
|
||
task<T> get_return_object() { return task(state); } | ||
|
||
[[nodiscard]] std::suspend_never initial_suspend() const noexcept { return {}; } | ||
[[nodiscard]] std::suspend_never final_suspend() const noexcept { return {}; } | ||
|
||
void return_value(T&& val) { state->set_value(std::move(val)); } | ||
|
||
void return_value(const T& val) { state->set_value(val); } | ||
|
||
[[noreturn]] void unhandled_exception() { std::terminate(); } | ||
}; | ||
|
||
template<> | ||
struct promise_t<void> { | ||
std::shared_ptr<awaitable_state<void>> state; | ||
|
||
explicit promise_t() : state(std::make_shared<awaitable_state<void>>()) {} | ||
|
||
// not copyable | ||
promise_t(const promise_t&) = delete; | ||
promise_t& operator=(const promise_t&) = delete; | ||
// movable | ||
promise_t(promise_t&&) = default; | ||
promise_t& operator=(promise_t&&) = default; | ||
|
||
task<void> get_return_object() { return task(state); } | ||
|
||
[[nodiscard]] std::suspend_never initial_suspend() const noexcept { return {}; } | ||
[[nodiscard]] std::suspend_never final_suspend() const noexcept { return {}; } | ||
|
||
void return_void() { state->set_value(); } | ||
|
||
[[noreturn]] void unhandled_exception() { std::terminate(); } | ||
}; | ||
} // namespace asyncpp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#pragma once | ||
|
||
#include <functional> | ||
|
||
namespace asyncpp { | ||
template<typename T> | ||
using ResolveResultCb = std::function<void(T&&)>; | ||
|
||
using ResolveVoidCb = std::function<void()>; | ||
|
||
template<typename T, typename TState> | ||
[[maybe_unused]] task<T> makeTask(const std::function<void(TState&, ResolveResultCb<T>)>& cb) { | ||
auto state = std::make_shared<awaitable_state<T>>(); | ||
|
||
std::shared_ptr<TState> customState = std::make_shared<TState>(); | ||
|
||
ResolveResultCb<T> resolve = [state](const T& value) { state->set_value(value); }; | ||
|
||
cb(*customState, resolve); | ||
|
||
return task<T>(state, customState); | ||
} | ||
|
||
template<typename TState> | ||
[[maybe_unused]] task<void> makeTask(const std::function<void(TState&, ResolveVoidCb)>& cb) { | ||
auto state = std::make_shared<awaitable_state<void>>(); | ||
|
||
std::shared_ptr<TState> customState = std::make_shared<TState>(); | ||
|
||
ResolveVoidCb resolve = [state]() { state->set_value(); }; | ||
|
||
cb(*customState, resolve); | ||
|
||
return task<void>(state, customState); | ||
} | ||
|
||
template<typename T> | ||
[[maybe_unused]] task<T> makeTask(const std::function<void(ResolveResultCb<T>&)>& cb) { | ||
auto state = std::make_shared<awaitable_state<T>>(); | ||
|
||
ResolveResultCb<T> resolve = [state](T&& value) { state->set_value(std::move(value)); }; | ||
|
||
cb(resolve); | ||
|
||
return task<T>(state); | ||
} | ||
|
||
[[maybe_unused]] static task<void> makeTask(const std::function<void(ResolveVoidCb)>& cb) { | ||
auto state = std::make_shared<awaitable_state<void>>(); | ||
|
||
ResolveVoidCb resolve = [state]() { state->set_value(); }; | ||
|
||
cb(resolve); | ||
|
||
return task<void>(state); | ||
} | ||
} // namespace asyncpp |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
add_library(asyncpp_uv STATIC | ||
src/asyncpp_uv_wrapper.cpp | ||
src/asyncpp_uv_sleep.cpp | ||
) | ||
target_link_libraries(asyncpp_uv PUBLIC asyncpp -luv) | ||
target_include_directories(asyncpp_uv PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) | ||
set_property(TARGET asyncpp_uv PROPERTY CXX_STANDARD 20) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#include "asyncpp_uv_sleep.h" | ||
#include "asyncpp_uv_wrapper.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#pragma once | ||
|
||
#include <asyncpp/asyncpp.h> | ||
|
||
#include <chrono> | ||
#include <cstdint> | ||
|
||
namespace asyncpp_uv { | ||
asyncpp::task<void> uvSleep(uint64_t timeoutMs); | ||
asyncpp::task<void> uvSleep(const std::chrono::milliseconds& duration); | ||
} // namespace asyncpp_uv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#pragma once | ||
|
||
#include <uv.h> | ||
|
||
#include <cstdint> | ||
#include <functional> | ||
|
||
namespace asyncpp_uv::wrapper { | ||
void uvTimerStart(uv_loop_t* loop, uint64_t timeoutMs, const std::function<void()>& cb); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#include <asyncpp_uv/asyncpp_uv.h> | ||
#include <asyncpp_uv/asyncpp_uv_sleep.h> | ||
|
||
namespace asyncpp_uv { | ||
asyncpp::task<void> uvSleep(uint64_t timeoutMs) { | ||
return asyncpp::makeTask([timeoutMs](auto resolve) { wrapper::uvTimerStart(uv_default_loop(), timeoutMs, resolve); }); | ||
} | ||
|
||
asyncpp::task<void> uvSleep(const std::chrono::milliseconds& duration) { return uvSleep((uint64_t)duration.count()); } | ||
} // namespace asyncpp_uv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#include <asyncpp_uv/asyncpp_uv_wrapper.h> | ||
#include <uv.h> | ||
|
||
#include <functional> | ||
|
||
namespace asyncpp_uv::wrapper { | ||
void uvCloseDelete(void* handle) { | ||
uv_close((uv_handle_t*)handle, [](uv_handle_t* handleInner) { delete handleInner; }); | ||
} | ||
|
||
void uvTimerStart(uv_loop_t* loop, uint64_t timeoutMs, const std::function<void()>& cb) { | ||
uv_timer_t* handle = new uv_timer_t(); | ||
handle->data = new std::function<void()>(cb); | ||
|
||
uv_timer_init(loop, handle); | ||
uv_timer_start( | ||
handle, | ||
[](uv_timer_t* handleInner) { | ||
std::function<void()>* data = (std::function<void()>*)handleInner->data; | ||
(*data)(); | ||
uv_timer_stop(handleInner); | ||
delete data; | ||
uvCloseDelete(handleInner); | ||
}, | ||
timeoutMs, 0); | ||
} | ||
} // namespace asyncpp_uv::wrapper |
Oops, something went wrong.