|
18 | 18 | #include <cstdint> // uintptr_t
|
19 | 19 | #include <vector>
|
20 | 20 | #include <type_traits>
|
| 21 | +#if _LIBCPP_STD_VER >= 20 |
| 22 | +#include <coroutine> |
| 23 | +#include <variant> |
| 24 | +#endif |
21 | 25 |
|
22 | 26 |
|
23 | 27 | namespace emscripten {
|
@@ -110,6 +114,11 @@ EM_VAL _emval_await(EM_VAL promise);
|
110 | 114 | EM_VAL _emval_iter_begin(EM_VAL iterable);
|
111 | 115 | EM_VAL _emval_iter_next(EM_VAL iterator);
|
112 | 116 |
|
| 117 | +#if _LIBCPP_STD_VER >= 20 |
| 118 | +void _emval_coro_suspend(EM_VAL promise, void* coro_ptr); |
| 119 | +EM_VAL _emval_coro_make_promise(EM_VAL *resolve, EM_VAL *reject); |
| 120 | +#endif |
| 121 | + |
113 | 122 | } // extern "C"
|
114 | 123 |
|
115 | 124 | template<const char* address>
|
@@ -586,6 +595,10 @@ class val {
|
586 | 595 | // our iterators are sentinel-based range iterators; use nullptr as the end sentinel
|
587 | 596 | constexpr nullptr_t end() const { return nullptr; }
|
588 | 597 |
|
| 598 | +#if _LIBCPP_STD_VER >= 20 |
| 599 | + struct promise_type; |
| 600 | +#endif |
| 601 | + |
589 | 602 | private:
|
590 | 603 | // takes ownership, assumes handle already incref'd and lives on the same thread
|
591 | 604 | explicit val(EM_VAL handle)
|
@@ -646,6 +659,83 @@ inline val::iterator val::begin() const {
|
646 | 659 | return iterator(*this);
|
647 | 660 | }
|
648 | 661 |
|
| 662 | +#if _LIBCPP_STD_VER >= 20 |
| 663 | +namespace internal { |
| 664 | +struct val_awaiter { |
| 665 | + val_awaiter(val&& promise) |
| 666 | + : state(std::in_place_index<STATE_PROMISE>, std::move(promise)) {} |
| 667 | + |
| 668 | + // just in case, ensure nobody moves / copies this type around |
| 669 | + val_awaiter(val_awaiter&&) = delete; |
| 670 | + |
| 671 | + bool await_ready() { return false; } |
| 672 | + |
| 673 | + void await_suspend(std::coroutine_handle<val::promise_type> handle) { |
| 674 | + internal::_emval_coro_suspend(std::get<STATE_PROMISE>(state).as_handle(), this); |
| 675 | + state.emplace<STATE_CORO>(handle); |
| 676 | + } |
| 677 | + |
| 678 | + void resume_with(val&& result) { |
| 679 | + auto coro = std::move(std::get<STATE_CORO>(state)); |
| 680 | + state.emplace<STATE_RESULT>(std::move(result)); |
| 681 | + coro.resume(); |
| 682 | + } |
| 683 | + |
| 684 | + val await_resume() { return std::move(std::get<STATE_RESULT>(state)); } |
| 685 | + |
| 686 | +private: |
| 687 | + // State machine holding awaiter's current state. One of: |
| 688 | + // - initially created with promise |
| 689 | + // - waiting with a given coroutine handle |
| 690 | + // - completed with a result |
| 691 | + std::variant<val, std::coroutine_handle<val::promise_type>, val> state; |
| 692 | + |
| 693 | + constexpr static std::size_t STATE_PROMISE = 0; |
| 694 | + constexpr static std::size_t STATE_CORO = 1; |
| 695 | + constexpr static std::size_t STATE_RESULT = 2; |
| 696 | +}; |
| 697 | + |
| 698 | +extern "C" { |
| 699 | + void _emval_coro_resume(val_awaiter* awaiter, EM_VAL result) { |
| 700 | + awaiter->resume_with(val::take_ownership(result)); |
| 701 | + } |
| 702 | +} |
| 703 | +} |
| 704 | + |
| 705 | +// Note: this type can't be internal because coroutines look for the public `promise_type` member. |
| 706 | +struct val::promise_type { |
| 707 | + promise_type() { |
| 708 | + EM_VAL resolve_handle; |
| 709 | + EM_VAL reject_handle; |
| 710 | + promise = val(internal::_emval_coro_make_promise(&resolve_handle, &reject_handle)); |
| 711 | + resolve = val(resolve_handle); |
| 712 | + reject_with_current_exception = val(reject_handle); |
| 713 | + } |
| 714 | + |
| 715 | + val get_return_object() { return promise; } |
| 716 | + |
| 717 | + auto initial_suspend() noexcept { return std::suspend_never{}; } |
| 718 | + |
| 719 | + auto final_suspend() noexcept { return std::suspend_never{}; } |
| 720 | + |
| 721 | + void unhandled_exception() { |
| 722 | + reject_with_current_exception(); |
| 723 | + } |
| 724 | + |
| 725 | + template<typename T> |
| 726 | + void return_value(T&& value) { |
| 727 | + resolve(std::forward<T>(value)); |
| 728 | + } |
| 729 | + |
| 730 | + internal::val_awaiter await_transform(val promise) { |
| 731 | + return {std::move(promise)}; |
| 732 | + } |
| 733 | + |
| 734 | +private: |
| 735 | + val promise, resolve, reject_with_current_exception; |
| 736 | +}; |
| 737 | +#endif |
| 738 | + |
649 | 739 | // Declare a custom type that can be used in conjunction with
|
650 | 740 | // emscripten::register_type to emit custom TypeScript definitions for val
|
651 | 741 | // types.
|
|
0 commit comments