From e3f59ddb770f5a7de2b80e8a2277af0dc6a81867 Mon Sep 17 00:00:00 2001 From: Berrysoft Date: Wed, 2 Dec 2020 00:55:58 +0800 Subject: [PATCH 1/3] Add async_action. --- CMakeLists.txt | 2 + async/CMakeLists.txt | 27 ++++ async/include/xaml/async/action.h | 171 ++++++++++++++++++++++++ async/include/xaml/async/info.h | 27 ++++ async/src/action.cpp | 26 ++++ async/test/CMakeLists.txt | 6 + async/test/src/main.cpp | 71 ++++++++++ global/include/xaml/observable_vector.h | 4 +- global/include/xaml/utility.h | 8 +- 9 files changed, 337 insertions(+), 5 deletions(-) create mode 100644 async/CMakeLists.txt create mode 100644 async/include/xaml/async/action.h create mode 100644 async/include/xaml/async/info.h create mode 100644 async/src/action.cpp create mode 100644 async/test/CMakeLists.txt create mode 100644 async/test/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d861dc4..e38ea43b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,7 @@ endif() set(_XAML_TARGETS xaml_global xaml_meta xaml_markup + xaml_async xaml_ui xaml_ui_appmain ) @@ -226,6 +227,7 @@ xaml_fix_char8_t_flags() add_subdirectory(global) add_subdirectory(helpers) add_subdirectory(meta) +add_subdirectory(async) add_subdirectory(markup) add_subdirectory(ui) if(${BUILD_PARSER}) diff --git a/async/CMakeLists.txt b/async/CMakeLists.txt new file mode 100644 index 00000000..d3199b80 --- /dev/null +++ b/async/CMakeLists.txt @@ -0,0 +1,27 @@ +project(XamlAsync CXX) + +file(GLOB ASYNC_HEADERS "include/xaml/async/*.h") +file(GLOB ASYNC_SOURCE "src/*.cpp") +add_library(xaml_async ${ASYNC_SOURCE}) + +target_include_directories(xaml_async + PUBLIC + $ + $ + PRIVATE + $ +) + +target_link_libraries(xaml_async PUBLIC xaml_global xaml_meta) + +if(${BUILD_SHARED_LIBS}) + target_compile_definitions(xaml_async PRIVATE "XAML_ASYNC_API=__XAML_EXPORT") +endif() + +if(${XAML_INSTALL}) + install(FILES ${ASYNC_HEADERS} DESTINATION include/xaml) +endif() + +if(${BUILD_TESTS}) + add_subdirectory(test) +endif() diff --git a/async/include/xaml/async/action.h b/async/include/xaml/async/action.h new file mode 100644 index 00000000..bfe1de9f --- /dev/null +++ b/async/include/xaml/async/action.h @@ -0,0 +1,171 @@ +#ifndef XAML_ASYNC_ACTION_H +#define XAML_ASYNC_ACTION_H + +#include +#include +#include + +#ifdef __cplusplus + #include + #include + #include +#endif // __cplusplus + +XAML_CLASS(xaml_async_action, { 0xe31c7f48, 0x8af4, 0x46af, { 0xac, 0x54, 0x01, 0xa1, 0xc1, 0xea, 0xea, 0x1e } }) + +#ifndef xaml_delegate_2__xaml_async_action__xaml_async_status_defined + #define xaml_delegate_2__xaml_async_action__xaml_async_status_defined +XAML_DELEGATE_2_TYPE(XAML_T_O(xaml_async_action), XAML_T_V(xaml_async_status)) +#endif // !xaml_delegate_2__xaml_async_action__xaml_async_status_defined + +#define XAML_ASYNC_ACTION_VTBL(type) \ + XAML_VTBL_INHERIT(XAML_ASYNC_INFO_VTBL(type)); \ + XAML_PROP(completed, type, XAML_DELEGATE_2_NAME(xaml_async_action, xaml_async_status)**, XAML_DELEGATE_2_NAME(xaml_async_action, xaml_async_status)*) + +XAML_DECL_INTERFACE_(xaml_async_action, xaml_async_info) +{ + XAML_DECL_VTBL(xaml_async_action, XAML_ASYNC_ACTION_VTBL); +}; + +EXTERN_C XAML_ASYNC_API xaml_result XAML_CALL xaml_async_action_wait(xaml_async_action*) XAML_NOEXCEPT; + +#ifdef __cplusplus +template +struct xaml_async_promise_base : xaml_implement +{ + std::exception_ptr m_exception{}; + std::atomic m_status{}; + xaml_ptr> m_handler{}; + + std::uint32_t XAML_CALL release() noexcept override + { + std::int32_t res = --this->m_ref_count; + if (res == 0) + { + std::coroutine_handle::from_promise(*static_cast(this)).destroy(); + } + return res; + } + + xaml_result XAML_CALL get_completed(xaml_delegate** ptr) noexcept override + { + return m_handler.query(ptr); + } + + xaml_result XAML_CALL set_completed(xaml_delegate* h) noexcept override + { + m_handler = h; + if (m_status != xaml_async_started) + { + return invoke_completed(); + } + return XAML_S_OK; + } + + xaml_result invoke_completed() noexcept + { + if (m_handler) + { + return m_handler->invoke(this, m_status); + } + return XAML_S_OK; + } + + xaml_ptr get_return_object() noexcept { return this; } + std::suspend_never initial_suspend() const noexcept { return {}; } + auto final_suspend() noexcept + { + struct awaitable + { + xaml_async_promise_base* m_promise; + + bool await_ready() const noexcept { return false; } + void await_resume() const noexcept {} + + bool await_suspend(std::coroutine_handle<>) const noexcept + { + if (m_promise->m_status == xaml_async_started) m_promise->m_status = xaml_async_completed; + m_promise->invoke_completed(); + return m_promise->release() > 0; + } + }; + return awaitable{ this }; + } + + void unhandled_exception() noexcept + { + m_exception = std::current_exception(); + m_status = xaml_async_error; + } + + void rethrow_if_failed() const + { + if (m_status == xaml_async_error) + { + std::rethrow_exception(m_exception); + } + } + + xaml_result XAML_CALL get_error() noexcept override + try + { + rethrow_if_failed(); + return XAML_S_OK; + } + XAML_CATCH_RETURN() + + xaml_result XAML_CALL get_status(xaml_async_status* ptr) noexcept override + { + *ptr = m_status; + return XAML_S_OK; + } +}; + +auto operator co_await(xaml_ptr action) +{ + struct awaitable + { + xaml_ptr m_action; + + bool await_ready() const + { + xaml_async_status status; + XAML_THROW_IF_FAILED(m_action->get_status(&status)); + return status != xaml_async_started; + } + + void await_resume() const noexcept {} + + void await_suspend(std::coroutine_handle<> h) const + { + xaml_ptr> handler; + XAML_THROW_IF_FAILED(xaml_delegate_new( + [h](xaml_ptr const&, xaml_async_status) noexcept -> xaml_result { + try + { + h.resume(); + return XAML_S_OK; + } + XAML_CATCH_RETURN() + }, + &handler)); + XAML_THROW_IF_FAILED(m_action->set_completed(handler)); + } + }; + return awaitable{ action }; +} + +namespace std +{ + template + struct coroutine_traits, Args...> + { + struct promise_type final : xaml_async_promise_base + { + void return_void() const noexcept {} + }; + }; +} // namespace std +#endif // __cplusplus + +#endif // !XAML_ASYNC_ACTION_H diff --git a/async/include/xaml/async/info.h b/async/include/xaml/async/info.h new file mode 100644 index 00000000..a5e5fee2 --- /dev/null +++ b/async/include/xaml/async/info.h @@ -0,0 +1,27 @@ +#ifndef XAML_ASYNC_INFO_H +#define XAML_ASYNC_INFO_H + +#include + +typedef enum xaml_async_status +{ + xaml_async_started, + xaml_async_completed, + xaml_async_error +} xaml_async_status; + +XAML_TYPE(xaml_async_status, { 0x2e1ff26a, 0x5de9, 0x4d44, { 0x8d, 0xbd, 0xcf, 0xf6, 0xd2, 0xe8, 0x12, 0xf7 } }) + +XAML_CLASS(xaml_async_info, { 0x0d9e61f6, 0x5eb4, 0x4d82, { 0x9e, 0xb0, 0x98, 0x2f, 0xf8, 0x20, 0x82, 0x23 } }) + +#define XAML_ASYNC_INFO_VTBL(type) \ + XAML_VTBL_INHERIT(XAML_OBJECT_VTBL(type)); \ + XAML_METHOD(get_error, type); \ + XAML_METHOD(get_status, type, xaml_async_status*) + +XAML_DECL_INTERFACE_(xaml_async_info, xaml_object) +{ + XAML_DECL_VTBL(xaml_async_info, XAML_ASYNC_INFO_VTBL); +}; + +#endif // !XAML_ASYNC_INFO_H diff --git a/async/src/action.cpp b/async/src/action.cpp new file mode 100644 index 00000000..72180cbe --- /dev/null +++ b/async/src/action.cpp @@ -0,0 +1,26 @@ +#include +#include + +using namespace std; + +xaml_result XAML_CALL xaml_async_action_wait(xaml_async_action* action) noexcept +try +{ + atomic_flag flag{}; + xaml_ptr> handler; + XAML_RETURN_IF_FAILED(xaml_delegate_new( + [&](xaml_ptr const&, xaml_async_status) noexcept -> xaml_result { + try + { + flag.test_and_set(); + return XAML_S_OK; + } + XAML_CATCH_RETURN() + }, + &handler)); + XAML_RETURN_IF_FAILED(action->set_completed(handler)); + if (!flag.test()) + flag.wait(true); + return action->get_error(); +} +XAML_CATCH_RETURN() diff --git a/async/test/CMakeLists.txt b/async/test/CMakeLists.txt new file mode 100644 index 00000000..0571586b --- /dev/null +++ b/async/test/CMakeLists.txt @@ -0,0 +1,6 @@ +project(AsyncTest CXX) + +file(GLOB TEST_SOURCE "src/*.cpp") + +add_executable(async_test ${TEST_SOURCE}) +target_link_libraries(async_test xaml_async stream_format nowide) diff --git a/async/test/src/main.cpp b/async/test/src/main.cpp new file mode 100644 index 00000000..b723f6dd --- /dev/null +++ b/async/test/src/main.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include + +auto operator co_await(std::chrono::system_clock::duration duration) +{ + struct awaitable + { + std::chrono::system_clock::duration duration; + + explicit awaitable(std::chrono::system_clock::duration d) + : duration(d) + { + } + + bool await_ready() const + { + return duration.count() <= 0; + } + + void await_suspend(std::coroutine_handle<> h) + { + std::thread t{ + [h, this] { + std::this_thread::sleep_for(duration); + h.resume(); + } + }; + t.detach(); + } + + void await_resume() {} + }; + return awaitable{ duration }; +} + +using namespace sf; +using nowide::cout; +using namespace std::chrono_literals; + +xaml_ptr foo() +{ + co_await 1s; + println(cout, "Here is in foo."); +} + +xaml_ptr bar() +{ + println(cout, "Here is in bar."); + auto task = foo(); + println(cout, "Here is also in bar."); + std::this_thread::sleep_for(1.5s); + co_await task; + println(cout, "Now back to bar once again."); + throw std::bad_alloc{}; +} + +int main() +{ + auto action = bar(); + try + { + XAML_THROW_IF_FAILED(xaml_async_action_wait(action)); + } + catch (xaml_result_error const& e) + { + println(cout, xaml_result_get_message(e.get_result())); + } + println(cout, "Back to main."); +} diff --git a/global/include/xaml/observable_vector.h b/global/include/xaml/observable_vector.h index 256d3f21..4ec417e1 100644 --- a/global/include/xaml/observable_vector.h +++ b/global/include/xaml/observable_vector.h @@ -89,7 +89,9 @@ xaml_result XAML_CALL xaml_vector_changed_args_new(xaml_vector_changed_action ac XAML_TYPE_BASE(xaml_observable_vector_1, { 0xc84cb35f, 0x0a1c, 0x40e2, { 0x8e, 0x1c, 0x2c, 0x43, 0x0b, 0x1b, 0xb6, 0xcf } }) -#define __XAML_DELEGATE_2_NAME(a, b) XAML_DELEGATE_2_NAME(a, b) +#ifndef __XAML_DELEGATE_2_NAME + #define __XAML_DELEGATE_2_NAME(a, b) XAML_DELEGATE_2_NAME(a, b) +#endif // !__XAML_DELEGATE_2_NAME #define XAML_OBSERVABLE_VECTOR_1_VTBL(type, TN, TI) \ XAML_VTBL_INHERIT(XAML_VECTOR_1_VTBL(type, TN, TI)); \ diff --git a/global/include/xaml/utility.h b/global/include/xaml/utility.h index e399fe12..7ca0ef41 100644 --- a/global/include/xaml/utility.h +++ b/global/include/xaml/utility.h @@ -49,14 +49,14 @@ #define XAML_META_API __XAML_IMPORT #endif // !XAML_META_API +#ifndef XAML_ASYNC_API + #define XAML_ASYNC_API __XAML_IMPORT +#endif // !XAML_ASYNC_API + #ifndef XAML_UI_API #define XAML_UI_API __XAML_IMPORT #endif // !XAML_UI_API -#ifndef XAML_UI_META_API - #define XAML_UI_META_API __XAML_META_IMPORT -#endif // !XAML_UI_META_API - #ifndef XAML_UI_CONTROLS_API #define XAML_UI_CONTROLS_API __XAML_IMPORT #endif // !XAML_UI_CONTROLS_API From d8485b29e1a5b11767a55242350786ee4dcce7ac Mon Sep 17 00:00:00 2001 From: Berrysoft Date: Wed, 2 Dec 2020 14:29:14 +0800 Subject: [PATCH 2/3] [WIP] --- async/src/action.cpp | 8 ++++++-- async/test/src/main.cpp | 30 ++++++++++++++++-------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/async/src/action.cpp b/async/src/action.cpp index 72180cbe..1c97208c 100644 --- a/async/src/action.cpp +++ b/async/src/action.cpp @@ -7,13 +7,17 @@ xaml_result XAML_CALL xaml_async_action_wait(xaml_async_action* action) noexcept try { atomic_flag flag{}; + xaml_ptr> old_handler; + XAML_RETURN_IF_FAILED(action->get_completed(&old_handler)); xaml_ptr> handler; XAML_RETURN_IF_FAILED(xaml_delegate_new( - [&](xaml_ptr const&, xaml_async_status) noexcept -> xaml_result { + [&](xaml_ptr const& a, xaml_async_status s) noexcept -> xaml_result { + xaml_result hr = XAML_S_OK; + if (old_handler) hr = old_handler->invoke(a, s); try { flag.test_and_set(); - return XAML_S_OK; + return hr; } XAML_CATCH_RETURN() }, diff --git a/async/test/src/main.cpp b/async/test/src/main.cpp index b723f6dd..11491b21 100644 --- a/async/test/src/main.cpp +++ b/async/test/src/main.cpp @@ -1,33 +1,34 @@ +#include #include #include #include #include -auto operator co_await(std::chrono::system_clock::duration duration) +auto get_awaitable(std::chrono::system_clock::duration duration) { struct awaitable { - std::chrono::system_clock::duration duration; + std::chrono::system_clock::duration m_duration; + std::thread m_thread; explicit awaitable(std::chrono::system_clock::duration d) - : duration(d) + : m_duration(d), + m_thread( + [d] { + if (d.count() > 0) std::this_thread::sleep_for(d); + }) { } bool await_ready() const { - return duration.count() <= 0; + return m_duration.count() <= 0; } - void await_suspend(std::coroutine_handle<> h) + auto await_suspend(std::coroutine_handle<> h) { - std::thread t{ - [h, this] { - std::this_thread::sleep_for(duration); - h.resume(); - } - }; - t.detach(); + m_thread.join(); + return h; } void await_resume() {} @@ -41,8 +42,10 @@ using namespace std::chrono_literals; xaml_ptr foo() { - co_await 1s; + auto task = get_awaitable(1s); println(cout, "Here is in foo."); + co_await task; + println(cout, "Here is also in foo."); } xaml_ptr bar() @@ -50,7 +53,6 @@ xaml_ptr bar() println(cout, "Here is in bar."); auto task = foo(); println(cout, "Here is also in bar."); - std::this_thread::sleep_for(1.5s); co_await task; println(cout, "Now back to bar once again."); throw std::bad_alloc{}; From 0050744deb18f83b2180f05c007445f6a5a18023 Mon Sep 17 00:00:00 2001 From: Berrysoft Date: Tue, 8 Dec 2020 17:57:15 +0800 Subject: [PATCH 3/3] Suspend on initial. --- async/include/xaml/async/action.h | 27 ++++++++++++++++++++++----- async/src/action.cpp | 26 ++------------------------ async/test/src/main.cpp | 7 +++---- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/async/include/xaml/async/action.h b/async/include/xaml/async/action.h index bfe1de9f..202e6012 100644 --- a/async/include/xaml/async/action.h +++ b/async/include/xaml/async/action.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __cplusplus #include @@ -27,7 +28,17 @@ XAML_DECL_INTERFACE_(xaml_async_action, xaml_async_info) XAML_DECL_VTBL(xaml_async_action, XAML_ASYNC_ACTION_VTBL); }; -EXTERN_C XAML_ASYNC_API xaml_result XAML_CALL xaml_async_action_wait(xaml_async_action*) XAML_NOEXCEPT; +#ifndef xaml_enumerator_1__xaml_async_action_defined + #define xaml_enumerator_1__xaml_async_action_defined +XAML_ENUMERATOR_1_TYPE(XAML_T_O(xaml_async_action)) +#endif // !xaml_enumerator_1__xaml_async_action_defined + +#ifndef xaml_vector_view_1__xaml_async_action_defined + #define xaml_vector_view_1__xaml_async_action_defined +XAML_VECTOR_VIEW_1_TYPE(XAML_T_O(xaml_async_action)) +#endif // !xaml_vector_view_1__xaml_async_action_defined + +EXTERN_C XAML_ASYNC_API xaml_result XAML_CALL xaml_async_action_wait_all(XAML_VECTOR_VIEW_1_NAME(xaml_async_action) *) XAML_NOEXCEPT; #ifdef __cplusplus template @@ -37,12 +48,14 @@ struct xaml_async_promise_base : xaml_implement std::atomic m_status{}; xaml_ptr> m_handler{}; + std::coroutine_handle get_handle() { return std::coroutine_handle::from_promise(*static_cast(this)); } + std::uint32_t XAML_CALL release() noexcept override { std::int32_t res = --this->m_ref_count; if (res == 0) { - std::coroutine_handle::from_promise(*static_cast(this)).destroy(); + get_handle().destroy(); } return res; } @@ -72,7 +85,7 @@ struct xaml_async_promise_base : xaml_implement } xaml_ptr get_return_object() noexcept { return this; } - std::suspend_never initial_suspend() const noexcept { return {}; } + std::suspend_always initial_suspend() const noexcept { return {}; } auto final_suspend() noexcept { struct awaitable @@ -84,11 +97,11 @@ struct xaml_async_promise_base : xaml_implement bool await_suspend(std::coroutine_handle<>) const noexcept { - if (m_promise->m_status == xaml_async_started) m_promise->m_status = xaml_async_completed; m_promise->invoke_completed(); return m_promise->release() > 0; } }; + if (m_status == xaml_async_started) m_status = xaml_async_completed; return awaitable{ this }; } @@ -109,6 +122,8 @@ struct xaml_async_promise_base : xaml_implement xaml_result XAML_CALL get_error() noexcept override try { + auto handle = get_handle(); + while (!handle.done()) handle.resume(); rethrow_if_failed(); return XAML_S_OK; } @@ -138,6 +153,7 @@ auto operator co_await(xaml_ptr action) void await_suspend(std::coroutine_handle<> h) const { + auto action = m_action; xaml_ptr> handler; XAML_THROW_IF_FAILED(xaml_delegate_new( [h](xaml_ptr const&, xaml_async_status) noexcept -> xaml_result { @@ -149,7 +165,8 @@ auto operator co_await(xaml_ptr action) XAML_CATCH_RETURN() }, &handler)); - XAML_THROW_IF_FAILED(m_action->set_completed(handler)); + XAML_THROW_IF_FAILED(action->set_completed(handler)); + XAML_THROW_IF_FAILED(action->get_error()); } }; return awaitable{ action }; diff --git a/async/src/action.cpp b/async/src/action.cpp index 1c97208c..9e5356b6 100644 --- a/async/src/action.cpp +++ b/async/src/action.cpp @@ -1,30 +1,8 @@ -#include #include using namespace std; -xaml_result XAML_CALL xaml_async_action_wait(xaml_async_action* action) noexcept -try +xaml_result XAML_CALL xaml_async_action_wait_all(xaml_vector_view* actions) noexcept { - atomic_flag flag{}; - xaml_ptr> old_handler; - XAML_RETURN_IF_FAILED(action->get_completed(&old_handler)); - xaml_ptr> handler; - XAML_RETURN_IF_FAILED(xaml_delegate_new( - [&](xaml_ptr const& a, xaml_async_status s) noexcept -> xaml_result { - xaml_result hr = XAML_S_OK; - if (old_handler) hr = old_handler->invoke(a, s); - try - { - flag.test_and_set(); - return hr; - } - XAML_CATCH_RETURN() - }, - &handler)); - XAML_RETURN_IF_FAILED(action->set_completed(handler)); - if (!flag.test()) - flag.wait(true); - return action->get_error(); + return XAML_E_NOTIMPL; } -XAML_CATCH_RETURN() diff --git a/async/test/src/main.cpp b/async/test/src/main.cpp index 11491b21..ded3719a 100644 --- a/async/test/src/main.cpp +++ b/async/test/src/main.cpp @@ -4,7 +4,7 @@ #include #include -auto get_awaitable(std::chrono::system_clock::duration duration) +auto operator co_await(std::chrono::system_clock::duration duration) { struct awaitable { @@ -42,9 +42,8 @@ using namespace std::chrono_literals; xaml_ptr foo() { - auto task = get_awaitable(1s); println(cout, "Here is in foo."); - co_await task; + co_await 1s; println(cout, "Here is also in foo."); } @@ -63,7 +62,7 @@ int main() auto action = bar(); try { - XAML_THROW_IF_FAILED(xaml_async_action_wait(action)); + XAML_THROW_IF_FAILED(action->get_error()); } catch (xaml_result_error const& e) {