Skip to content

Commit c017fc2

Browse files
authored
Allow subclassing coroutines (#20682)
Slight refactoring of coroutine glue to allow subclassing via the public API. This doesn't change how `co_await some_val;` behaves, but makes it possible to create your own subclasses that can also be awaited.
1 parent 28a09f6 commit c017fc2

File tree

3 files changed

+32
-12
lines changed

3 files changed

+32
-12
lines changed

system/include/emscripten/val.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,10 @@ class val {
596596
constexpr nullptr_t end() const { return nullptr; }
597597

598598
#if __cplusplus >= 202002L
599-
struct promise_type;
599+
class awaiter;
600+
awaiter operator co_await() const;
601+
602+
class promise_type;
600603
#endif
601604

602605
private:
@@ -660,12 +663,11 @@ inline val::iterator val::begin() const {
660663
}
661664

662665
#if __cplusplus >= 202002L
663-
namespace internal {
664666
// Awaiter defines a set of well-known methods that compiler uses
665667
// to drive the argument of the `co_await` operator (regardless
666668
// of the type of the parent coroutine).
667669
// This one is used for Promises represented by the `val` type.
668-
class val_awaiter {
670+
class val::awaiter {
669671
// State machine holding awaiter's current state. One of:
670672
// - initially created with promise
671673
// - waiting with a given coroutine handle
@@ -677,11 +679,11 @@ class val_awaiter {
677679
constexpr static std::size_t STATE_RESULT = 2;
678680

679681
public:
680-
val_awaiter(val&& promise)
681-
: state(std::in_place_index<STATE_PROMISE>, std::move(promise)) {}
682+
awaiter(const val& promise)
683+
: state(std::in_place_index<STATE_PROMISE>, promise) {}
682684

683685
// just in case, ensure nobody moves / copies this type around
684-
val_awaiter(val_awaiter&&) = delete;
686+
awaiter(awaiter&&) = delete;
685687

686688
// Promises don't have a synchronously accessible "ready" state.
687689
bool await_ready() { return false; }
@@ -705,6 +707,9 @@ class val_awaiter {
705707
// of the `co_await ...` expression - in our case, the stored value.
706708
val await_resume() { return std::move(std::get<STATE_RESULT>(state)); }
707709
};
710+
711+
inline val::awaiter val::operator co_await() const {
712+
return {*this};
708713
}
709714

710715
// `promise_type` is a well-known subtype with well-known method names
@@ -742,11 +747,6 @@ class val::promise_type {
742747
void return_value(T&& value) {
743748
resolve(std::forward<T>(value));
744749
}
745-
746-
// Return our awaiter on `co_await promise`.
747-
internal::val_awaiter await_transform(val promise) {
748-
return {std::move(promise)};
749-
}
750750
};
751751
#endif
752752

system/lib/embind/bind.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void _embind_register_bindings(InitFunc* f) {
6363
init_funcs = f;
6464
}
6565

66-
void _emval_coro_resume(val_awaiter* awaiter, EM_VAL result) {
66+
void _emval_coro_resume(val::awaiter* awaiter, EM_VAL result) {
6767
awaiter->resume_with(val::take_ownership(result));
6868
}
6969

test/embind/test_val_coro.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,32 @@ val promise_sleep(int ms, int result = 0) {
2020
return val::take_ownership(promise_sleep_impl(ms, result));
2121
}
2222

23+
// Test that we can subclass and make custom awaitable types.
24+
template <typename T>
25+
class typed_promise: public val {
26+
public:
27+
typed_promise(val&& promise): val(std::move(promise)) {}
28+
29+
auto operator co_await() const {
30+
struct typed_awaiter: public val::awaiter {
31+
T await_resume() {
32+
return val::awaiter::await_resume().template as<T>();
33+
}
34+
};
35+
36+
return typed_awaiter(*this);
37+
}
38+
};
39+
2340
val asyncCoro() {
2441
// check that just sleeping works
2542
co_await promise_sleep(1);
2643
// check that sleeping and receiving value works
2744
val v = co_await promise_sleep(1, 12);
2845
assert(v.as<int>() == 12);
46+
// check that awaiting a subclassed promise works and returns the correct type
47+
int x = co_await typed_promise<int>(promise_sleep(1, 23));
48+
assert(x == 23);
2949
// check that returning value works (checked by JS in tests)
3050
co_return 34;
3151
}

0 commit comments

Comments
 (0)